diff --git a/DEPS b/DEPS
index 0e2d1e8..12f6c891 100644
--- a/DEPS
+++ b/DEPS
@@ -273,7 +273,7 @@
   'screen_ai_windows_386': 'version:138.00',
 
   # siso CIPD package version.
-  'siso_version': 'git_revision:70e1167e0e6dad10c8388cace8fd9d9376c43316',
+  'siso_version': 'git_revision:17b491ae74e86312a96239ff88e852acee83c135',
 
   # download libaom test data
   'download_libaom_testdata': False,
@@ -295,19 +295,19 @@
   # 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': '23319755d0789a1e63cc4c1dfb6f38403c9bfa63',
+  'src_internal_revision': '20b9f62811d76f8f5de0eba491b161f8bb948c6e',
   # 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': '32591be9cd3bea8c9e1ad39c64bc8230a4c4d499',
+  'skia_revision': 'c8f54c1bc565b8a7eced0a77088b228efdc70c2c',
   # 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': '8b2e5e57af618aadee7b3b9f967348c87da61591',
+  'v8_revision': '3005b00df2a95266a853252e20e78f637adc5c2f',
   # 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': '1c7fa5f847f236cc758f58838a9f5f958c1de0de',
+  'angle_revision': '8357b6a24cae20d24058cfef3277066c4993a1ae',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -371,11 +371,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': '4a839855c5e79d91689784e2c7284f3f6288123b',
+  'catapult_revision': '52ad7cf544050f01f47c070716b9dae8eb9fab2b',
   # 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': '6f0c3181a39f6fdfbaea35689d8126e0361da071',
+  'crossbench_revision': '47e5e3d994ddcc2319cec110ce670ccc1baa3223',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -391,7 +391,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': '682af1f84e33247088b8ca38e189f734ff9923ef',
+  'devtools_frontend_revision': '28fbee169c8a26c6c2ed2ec2550465fe3d697a34',
   # 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.
@@ -415,7 +415,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': '0f24b334d63efffa544d1c2fc6d779576a3aa5e9',
+  'dawn_revision': '968646ae6a99b1751d5a7a4298e3bf6976123326',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -519,7 +519,7 @@
   # 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':    'c8248a038fe70b7495d1d52fde500cf2dd6fae82',
+  'llvm_libc_revision':    'd7bdad4ef86b827a96469b1dfdfcfa1218930e59',
   # 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.
@@ -527,7 +527,7 @@
 
   # If you change this, also update the libc++ revision in
   # //buildtools/deps_revisions.gni.
-  'libcxx_revision':       'a01c02c9d4acbdae3b7e8a2f3ee58579a9c29f96',
+  'libcxx_revision':       'a9cc573e7c591795d11b72d8323ba0e573b55bd6',
 
   # GN CIPD package version.
   'gn_version': 'git_revision:487f8353f15456474437df32bb186187b0940b45',
@@ -837,143 +837,143 @@
     'objects': [
       {
         # The Android libclang_rt.builtins libraries are currently only included in the Linux clang package.
-        'object_name': 'Linux_x64/clang-llvmorg-21-init-10502-gb2e2ae87-1.tar.xz',
-        'sha256sum': '557ae6a59be1eb465d0e726766b757132f0ff5f1a13bda0a871887421a59497d',
-        'size_bytes': 55589384,
-        'generation': 1746143871500001,
+        'object_name': 'Linux_x64/clang-llvmorg-21-init-10851-gcd6c4b61-1.tar.xz',
+        'sha256sum': 'ecd163a9ba1a622fd761ff426c73b6ff95b3697cb18baa9183a4b414072d8633',
+        'size_bytes': 55589720,
+        'generation': 1746525620539676,
         'condition': '(host_os == "linux" or checkout_android) and non_git_source',
       },
       {
-        'object_name': 'Linux_x64/clang-tidy-llvmorg-21-init-10502-gb2e2ae87-1.tar.xz',
-        'sha256sum': 'd940c80b7c1358e99759b90fd3ec3bee4099ee5bdc7858132dfb9474504b5baf',
-        'size_bytes': 13553540,
-        'generation': 1746143871510594,
+        'object_name': 'Linux_x64/clang-tidy-llvmorg-21-init-10851-gcd6c4b61-1.tar.xz',
+        'sha256sum': '2cb4311b17b400ae969d691e90576d1e110d555cb366e0a007b01272f2231e4e',
+        'size_bytes': 13570756,
+        'generation': 1746525620814582,
         'condition': 'host_os == "linux" and checkout_clang_tidy and non_git_source',
       },
       {
-        'object_name': 'Linux_x64/clangd-llvmorg-21-init-10502-gb2e2ae87-1.tar.xz',
-        'sha256sum': 'b138ec884298dd0601232dd98f92d2697e64eefe2ac5b4eee383eefa8909ed15',
-        'size_bytes': 13783716,
-        'generation': 1746143871533160,
+        'object_name': 'Linux_x64/clangd-llvmorg-21-init-10851-gcd6c4b61-1.tar.xz',
+        'sha256sum': '4758d370c079cb70da49ba510e434871c2f4f2cb96ad5e4eeb523d669ccd016b',
+        'size_bytes': 13824764,
+        'generation': 1746525620948078,
         'condition': 'host_os == "linux" and checkout_clangd and non_git_source',
       },
       {
-        'object_name': 'Linux_x64/llvm-code-coverage-llvmorg-21-init-10502-gb2e2ae87-1.tar.xz',
-        'sha256sum': '98c252d7459a0645fb2daacb95b996fab9325072b29f5ef752e5864d7cea44d2',
-        'size_bytes': 2292976,
-        'generation': 1746143871601054,
+        'object_name': 'Linux_x64/llvm-code-coverage-llvmorg-21-init-10851-gcd6c4b61-1.tar.xz',
+        'sha256sum': '97d1fe6e3081f938c0bd691197f1326e804c5859644a3550259fef223c856138',
+        'size_bytes': 2295328,
+        'generation': 1746525621371092,
         'condition': 'host_os == "linux" and checkout_clang_coverage_tools and non_git_source',
       },
       {
-        'object_name': 'Linux_x64/llvmobjdump-llvmorg-21-init-10502-gb2e2ae87-1.tar.xz',
-        'sha256sum': '0bf6b769345d7234e16ce57e839732d3be94ab1043a55d0aae2cb3f20399f7bd',
-        'size_bytes': 5697144,
-        'generation': 1746143871548416,
+        'object_name': 'Linux_x64/llvmobjdump-llvmorg-21-init-10851-gcd6c4b61-1.tar.xz',
+        'sha256sum': 'df8518b635e60716b430321800cbbb083dde39a3fa856b0a865aefd7dd915a81',
+        'size_bytes': 5699744,
+        'generation': 1746525621090221,
         'condition': '((checkout_linux or checkout_mac or checkout_android) and host_os == "linux") and non_git_source',
       },
       {
-        'object_name': 'Mac/clang-llvmorg-21-init-10502-gb2e2ae87-1.tar.xz',
-        'sha256sum': '29482bbf5baff85fe278d12a7456d517e10497a888f3082b49043a1f73c0def4',
-        'size_bytes': 52061888,
-        'generation': 1746143873509655,
+        'object_name': 'Mac/clang-llvmorg-21-init-10851-gcd6c4b61-1.tar.xz',
+        'sha256sum': '220dfa6acb4468dfa83f353215ff741ca2b30738f028dc2f8a7b3b9c5f45bb14',
+        'size_bytes': 52096988,
+        'generation': 1746525623855538,
         'condition': 'host_os == "mac" and host_cpu == "x64"',
       },
       {
-        'object_name': 'Mac/clang-mac-runtime-library-llvmorg-21-init-10502-gb2e2ae87-1.tar.xz',
-        'sha256sum': '167dcfa307f1a6e0d5ac26701be84b6a0acc2dc9ebf48d2601c37f89579fac90',
-        'size_bytes': 992296,
-        'generation': 1746143880809968,
+        'object_name': 'Mac/clang-mac-runtime-library-llvmorg-21-init-10851-gcd6c4b61-1.tar.xz',
+        'sha256sum': '41f00c834c5f4f81dfa07477d79a64fff17531759d6df7256a72111590645ae8',
+        'size_bytes': 993908,
+        'generation': 1746525645453067,
         'condition': 'checkout_mac and not host_os == "mac"',
       },
       {
-        'object_name': 'Mac/clang-tidy-llvmorg-21-init-10502-gb2e2ae87-1.tar.xz',
-        'sha256sum': '2503c1f5c1d304aaf30da2ccc6ffb0834a409429d5754780f1b80afbffc9bd5d',
-        'size_bytes': 13636716,
-        'generation': 1746143873535744,
+        'object_name': 'Mac/clang-tidy-llvmorg-21-init-10851-gcd6c4b61-1.tar.xz',
+        'sha256sum': '4160f7a53e99c7c6fe4e589d20874b12a068625d92209bafdf3ccb1edeec37cf',
+        'size_bytes': 13670156,
+        'generation': 1746525624439549,
         'condition': 'host_os == "mac" and host_cpu == "x64" and checkout_clang_tidy',
       },
       {
-        'object_name': 'Mac/clangd-llvmorg-21-init-10502-gb2e2ae87-1.tar.xz',
-        'sha256sum': '04a7e47676d58e10fc5ca28f87035a452c5b6d959c5f965a2d7efae7d72d2424',
-        'size_bytes': 15070916,
-        'generation': 1746143873533731,
+        'object_name': 'Mac/clangd-llvmorg-21-init-10851-gcd6c4b61-1.tar.xz',
+        'sha256sum': '04a3d9cb8e8742c5807d302b6f46e1dcfcdd36ad55ae8f9894c558f28db93f08',
+        'size_bytes': 15092288,
+        'generation': 1746525624237513,
         'condition': 'host_os == "mac" and host_cpu == "x64" and checkout_clangd',
       },
       {
-        'object_name': 'Mac/llvm-code-coverage-llvmorg-21-init-10502-gb2e2ae87-1.tar.xz',
-        'sha256sum': '97680b6f03a575050b941ead3ee816bf697b159c0b3304d29af7d0fee6cbfdd3',
-        'size_bytes': 2266332,
-        'generation': 1746143873609231,
+        'object_name': 'Mac/llvm-code-coverage-llvmorg-21-init-10851-gcd6c4b61-1.tar.xz',
+        'sha256sum': 'effe34835ea45e43b21d9aab0790f21b0291966614ddda23ab37eeea26a188ff',
+        'size_bytes': 2270324,
+        'generation': 1746525624667951,
         'condition': 'host_os == "mac" and host_cpu == "x64" and checkout_clang_coverage_tools',
       },
       {
-        'object_name': 'Mac_arm64/clang-llvmorg-21-init-10502-gb2e2ae87-1.tar.xz',
-        'sha256sum': '8ab032725c51a542f7e54b8795952039b0b910f3f5315bca13ae669febaa425f',
-        'size_bytes': 44082720,
-        'generation': 1746143882532819,
+        'object_name': 'Mac_arm64/clang-llvmorg-21-init-10851-gcd6c4b61-1.tar.xz',
+        'sha256sum': 'f0c7db116dd1dc93f8935baf3ee130f4f2eb72c4953d0e132dea473a030194ba',
+        'size_bytes': 44103784,
+        'generation': 1746525647078692,
         'condition': 'host_os == "mac" and host_cpu == "arm64"',
       },
       {
-        'object_name': 'Mac_arm64/clang-tidy-llvmorg-21-init-10502-gb2e2ae87-1.tar.xz',
-        'sha256sum': '35679319e006d4fdc27aa5e62e1637db33026ef06c2cfa36abe14aa4113f34ab',
-        'size_bytes': 11794244,
-        'generation': 1746143882559932,
+        'object_name': 'Mac_arm64/clang-tidy-llvmorg-21-init-10851-gcd6c4b61-1.tar.xz',
+        'sha256sum': '32e49f6d1339be7c0387e404930aafb25c460b9396f1f5801a6fdd107fcbacb0',
+        'size_bytes': 11823236,
+        'generation': 1746525647324075,
         'condition': 'host_os == "mac" and host_cpu == "arm64" and checkout_clang_tidy',
       },
       {
-        'object_name': 'Mac_arm64/clangd-llvmorg-21-init-10502-gb2e2ae87-1.tar.xz',
-        'sha256sum': '6ecfc7eee127d298d55757d405bc29d3197d60b8316d51355eca218442c55b90',
-        'size_bytes': 12067876,
-        'generation': 1746143882556967,
+        'object_name': 'Mac_arm64/clangd-llvmorg-21-init-10851-gcd6c4b61-1.tar.xz',
+        'sha256sum': '7f77dbff038c35c2a688b4c8d05fa2b22cecc7d175659dbe88c88f952fa63fbd',
+        'size_bytes': 12089948,
+        'generation': 1746525647467078,
         'condition': 'host_os == "mac" and host_cpu == "arm64" and checkout_clangd',
       },
       {
-        'object_name': 'Mac_arm64/llvm-code-coverage-llvmorg-21-init-10502-gb2e2ae87-1.tar.xz',
-        'sha256sum': '09f89d979a09cad96b4d81090bec5cc3e7c12c7b5431ab21842d47b67b93122a',
-        'size_bytes': 1975672,
-        'generation': 1746143882686932,
+        'object_name': 'Mac_arm64/llvm-code-coverage-llvmorg-21-init-10851-gcd6c4b61-1.tar.xz',
+        'sha256sum': '71c858f90180069f7ef0b16f8e7869ed319bae00c984179cbdd25956fa6c73ff',
+        'size_bytes': 1979552,
+        'generation': 1746525647890816,
         'condition': 'host_os == "mac" and host_cpu == "arm64" and checkout_clang_coverage_tools',
       },
       {
-        'object_name': 'Win/clang-llvmorg-21-init-10502-gb2e2ae87-1.tar.xz',
-        'sha256sum': 'e9b3c67428ee5fff61ab2e229da2ecd807cceb5e48983baa2aa31ae675059931',
-        'size_bytes': 47070352,
-        'generation': 1746143892525539,
+        'object_name': 'Win/clang-llvmorg-21-init-10851-gcd6c4b61-1.tar.xz',
+        'sha256sum': 'd04ead6c2da3e38558d6bb19e06b05aa6dfa6b4b82b678c25295c803bb378c86',
+        'size_bytes': 47099456,
+        'generation': 1746525671111178,
         'condition': 'host_os == "win"',
       },
       {
-        'object_name': 'Win/clang-tidy-llvmorg-21-init-10502-gb2e2ae87-1.tar.xz',
-        'sha256sum': 'b7ff1c51e8f91ffe5bab9db99529f802114ce62ec588d0953d3a548424520836',
-        'size_bytes': 13437572,
-        'generation': 1746143892543580,
+        'object_name': 'Win/clang-tidy-llvmorg-21-init-10851-gcd6c4b61-1.tar.xz',
+        'sha256sum': '3b026fb8d3363b04a7434d0df710c2ebf8074e9aca7a236447cc0efb0718ec82',
+        'size_bytes': 13470524,
+        'generation': 1746525671342028,
         'condition': 'host_os == "win" and checkout_clang_tidy',
       },
       {
-        'object_name': 'Win/clang-win-runtime-library-llvmorg-21-init-10502-gb2e2ae87-1.tar.xz',
-        'sha256sum': '1ed7bf852f8255bf35335c14110c959743e17903e0020c776be3b8d80a564ae9',
-        'size_bytes': 2484344,
-        'generation': 1746143899795872,
+        'object_name': 'Win/clang-win-runtime-library-llvmorg-21-init-10851-gcd6c4b61-1.tar.xz',
+        'sha256sum': 'b6a49e302e5c9d562e575e1c1431777d90d4950dc7e6bba9b1c21d0ceba93c29',
+        'size_bytes': 2484356,
+        'generation': 1746525692368377,
         'condition': 'checkout_win and not host_os == "win"',
       },
       {
-        'object_name': 'Win/clangd-llvmorg-21-init-10502-gb2e2ae87-1.tar.xz',
-        'sha256sum': '90f9035116a665e8a620b259d299c845be42b68c7dce8a6439ba189d550bd6ca',
-        'size_bytes': 13855648,
-        'generation': 1746143892528921,
+        'object_name': 'Win/clangd-llvmorg-21-init-10851-gcd6c4b61-1.tar.xz',
+        'sha256sum': '51953564440fd82547a27b1fe0cd76d9a0c200162ee7530fe14d805511613be7',
+        'size_bytes': 13886984,
+        'generation': 1746525671497511,
        'condition': 'host_os == "win" and checkout_clangd',
       },
       {
-        'object_name': 'Win/llvm-code-coverage-llvmorg-21-init-10502-gb2e2ae87-1.tar.xz',
-        'sha256sum': '100dc0773b81d1a65b40356038d2c85e5aabd613b2e5830846d60c9fa2ca04bf',
-        'size_bytes': 2374764,
-        'generation': 1746143892621367,
+        'object_name': 'Win/llvm-code-coverage-llvmorg-21-init-10851-gcd6c4b61-1.tar.xz',
+        'sha256sum': 'e5e5a685fde6a4ddd211913ccf6f8e62ac7e6567fec26d68d5bff3aeac42c330',
+        'size_bytes': 2376076,
+        'generation': 1746525671928280,
         'condition': 'host_os == "win" and checkout_clang_coverage_tools',
       },
       {
-        'object_name': 'Win/llvmobjdump-llvmorg-21-init-10502-gb2e2ae87-1.tar.xz',
-        'sha256sum': '104ef589d1d1d9a7f1acd38e25c418b12de8b685c4f55f45f626cc64f3f1cc98',
-        'size_bytes': 5685804,
-        'generation': 1746143892549487,
+        'object_name': 'Win/llvmobjdump-llvmorg-21-init-10851-gcd6c4b61-1.tar.xz',
+        'sha256sum': 'd02d624d3ecfe5dbaa2e93b2da5a6443e4f25b9fe2fab43b8b540fccc91e4770',
+        'size_bytes': 5687508,
+        'generation': 1746525671637063,
         'condition': '(checkout_linux or checkout_mac or checkout_android) and host_os == "win"',
       },
     ]
@@ -984,31 +984,31 @@
     'bucket': 'chromium-browser-clang',
     'objects': [
       {
-        'object_name': 'Linux_x64/rust-toolchain-3350c1eb3fd8fe1bee1ed4c76944d707bd256876-1-llvmorg-21-init-10502-gb2e2ae87.tar.xz',
-        'sha256sum': 'b7b13f2fbd22c5415abec739570acd74066361ce3c82313660be65ab67f1a075',
-        'size_bytes': 118912744,
-        'generation': 1746143864066683,
+        'object_name': 'Linux_x64/rust-toolchain-4a0969e06dbeaaa43914d2d00b2e843d49aa3886-1-llvmorg-21-init-10851-gcd6c4b61.tar.xz',
+        'sha256sum': '2d8498b5dcab2954c8212564798252c3edc9c0181ea8b05d3d6ae42b7b6adf7d',
+        'size_bytes': 118439104,
+        'generation': 1746525613682798,
         'condition': 'host_os == "linux" and non_git_source',
       },
       {
-        'object_name': 'Mac/rust-toolchain-3350c1eb3fd8fe1bee1ed4c76944d707bd256876-1-llvmorg-21-init-10502-gb2e2ae87.tar.xz',
-        'sha256sum': '322dc36f74871d746545a1bb75470968315bce5ed4b385f43033e376f4c12a9b',
-        'size_bytes': 112193460,
-        'generation': 1746143865768180,
+        'object_name': 'Mac/rust-toolchain-4a0969e06dbeaaa43914d2d00b2e843d49aa3886-1-llvmorg-21-init-10851-gcd6c4b61.tar.xz',
+        'sha256sum': '93e57858344a978ce2df2d9926c541d8e25cd9def0054328dba4ac13ff633d34',
+        'size_bytes': 111691112,
+        'generation': 1746525615270255,
         'condition': 'host_os == "mac" and host_cpu == "x64"',
       },
       {
-        'object_name': 'Mac_arm64/rust-toolchain-3350c1eb3fd8fe1bee1ed4c76944d707bd256876-1-llvmorg-21-init-10502-gb2e2ae87.tar.xz',
-        'sha256sum': '2a95ec6fe7623c43e87f06829bae17c45f1c94c19db4c8f085d77cb948c0ee3f',
-        'size_bytes': 101896012,
-        'generation': 1746143867490688,
+        'object_name': 'Mac_arm64/rust-toolchain-4a0969e06dbeaaa43914d2d00b2e843d49aa3886-1-llvmorg-21-init-10851-gcd6c4b61.tar.xz',
+        'sha256sum': '4c1f9d7ff8c1febdb755ad971b0ae8d9b3c5bed5b30b96ea00caa784dfb86427',
+        'size_bytes': 101778572,
+        'generation': 1746525616855514,
         'condition': 'host_os == "mac" and host_cpu == "arm64"',
       },
       {
-        'object_name': 'Win/rust-toolchain-3350c1eb3fd8fe1bee1ed4c76944d707bd256876-1-llvmorg-21-init-10502-gb2e2ae87.tar.xz',
-        'sha256sum': 'da814703ae38af456eeeb63985198cb7ec48740b0a143249830fc880d633f80b',
-        'size_bytes': 193736940,
-        'generation': 1746143869370201,
+        'object_name': 'Win/rust-toolchain-4a0969e06dbeaaa43914d2d00b2e843d49aa3886-1-llvmorg-21-init-10851-gcd6c4b61.tar.xz',
+        'sha256sum': '7d2484e229f4015d3711f1294d11048764ec22ebaf8266701abf350cd51c897b',
+        'size_bytes': 193318196,
+        'generation': 1746525618539172,
         'condition': 'host_os == "win"',
       },
     ],
@@ -1475,7 +1475,7 @@
     'packages': [
       {
         'package': 'chromium/chrome/test/data/variations/cipd',
-        'version': 'VHYCX9xYCTReHB7RcGQeEqjdvegilaW1svSJscjtDX4C',
+        'version': 'ReWAdAhFsmTN1Z6g09iwNFqpNxVIeGzINfzDf4EumCUC',
       },
     ],
     'dep_type': 'cipd',
@@ -1486,12 +1486,12 @@
 
   'src/clank': {
     'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' +
-    'a85f502de34463769977268999f0dbce0321efa6',
+    '11c3547fd4f0706067ad840ff190038861085741',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
   'src/docs/website': {
-    'url': Var('chromium_git') + '/website.git' + '@' + 'e157e12d99cfc729a970b474344673c44e2d2c9c',
+    'url': Var('chromium_git') + '/website.git' + '@' + 'b2558d301fc88f8e4671024c47960ff1ee82cf34',
   },
 
   'src/ios/third_party/earl_grey2/src': {
@@ -1716,7 +1716,7 @@
       'packages': [
           {
                'package': 'chromium/third_party/android_build_tools/error_prone',
-               'version': '2t_u1XEjof609BQE4czlCQzEqRliKS_qY35FOu_VckgC',
+               'version': 'FLtgIzcLepAc87BaSXIELZIJPT4yxnr8K3MmFaEHrK8C',
           },
       ],
       'condition': 'checkout_android and non_git_source',
@@ -1738,7 +1738,7 @@
       'packages': [
           {
                'package': 'chromium/third_party/android_build_tools/lint',
-               'version': '0xqmMJ1qdP4NPN5s9VmWiNhchvNz_RLBrjXN4jUKN3gC',
+               'version': '2fKjMen4v_UU1CDYJqhwJCr6uh-L5w25n9lAKx6SlP0C',
           },
       ],
       'condition': 'checkout_android and non_git_source',
@@ -1749,7 +1749,7 @@
       'packages': [
           {
                'package': 'chromium/third_party/android_build_tools/manifest_merger',
-               'version': '7lstFdOjh1bFmfSZ8le-aZ1a1COQtwirGzuEWa43tnUC',
+               'version': 'GnPGgBK8kiq32GuJB3iI20yJbFjgt2dqHsmwoPShJ-4C',
           },
       ],
       'condition': 'checkout_android and non_git_source',
@@ -1978,7 +1978,7 @@
 
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '30d9438e7baf0c045f973de2f517b51554d14d7d',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '18580cf9c407a3c23aa072b9d745832516de699e',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -2229,7 +2229,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/kotlin_stdlib',
-              'version': '74gAxjfyJIVJGIA4z0V-jCmMW6A6dDJSoWMh7qhlzp4C',
+              'version': 'aHCAN6w_ZZCJL7MOPlDbEieRsiUs6ftT33fCI4bbwVwC',
           },
       ],
       'condition': 'checkout_android and non_git_source',
@@ -2894,7 +2894,7 @@
     Var('chromium_git') + '/webpagereplay.git' + '@' + Var('webpagereplay_revision'),
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '626c9f19118aac2e0faf45db73e9101d4502190c',
+    Var('webrtc_git') + '/src.git' + '@' + 'd69d0808c37b4ea338e59a58837ef180023316a6',
 
   # 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.
@@ -3071,7 +3071,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/projector_app/app',
-        'version': 'Gji5w3Fe9lCNNUKPYZBBjSkEtcijgDLpAH48woRMNU8C',
+        'version': '6XoJDL99IbFbTSQxQLQaQODFYghbzVxlKzfDpAbi65EC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -4591,7 +4591,7 @@
 
   'src/components/autofill/core/browser/form_parsing/internal_resources': {
       'url': Var('chrome_git') + '/chrome/components/autofill_regex_patterns.git' + '@' +
-        '8f3290e53c07fe9527fc48952edde535c3cfa13a',
+        'ee4b7f29de55d7867a004208580a994e5da35ed0',
       'condition': 'checkout_src_internal',
   },
 
@@ -4650,7 +4650,7 @@
 
   'src/components/test/data/autofill/heuristics-json/internal': {
       'url': Var('chrome_git') + '/chrome/test/autofill/structured_forms.git' + '@' +
-        'dbd13cb361518dac753112204eacd1ce9e1fab84',
+        'cdb496540ac02f8d98cfc04097e3e10a197f18f4',
       'condition': 'checkout_chromium_autofill_test_dependencies',
   },
 
@@ -4680,7 +4680,7 @@
 
   'src/ios_internal':  {
       'url': Var('chrome_git') + '/chrome/ios_internal.git' + '@' +
-        '56c2b01d07aad9c323ad655a8cb07b849923ac99',
+        'c32f19290092f9592d3be32943becfab3f52a9cd',
       'condition': 'checkout_ios and checkout_src_internal',
   },
 
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 574fe38a..1e7b7fe0 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -967,6 +967,7 @@
             r'android_webview/browser/ip_protection/.*',
             r'chrome/browser/ip_protection/.*',
             r'components/ip_protection/.*',
+            r'net/quic/dedicated_web_transport_http3_client\.cc',
 
             # Needed to use MediaPipe API.
             r'components/media_effects/.*\.cc',
diff --git a/android_webview/browser/aw_content_browser_client.cc b/android_webview/browser/aw_content_browser_client.cc
index 559c17c..cab4c11f 100644
--- a/android_webview/browser/aw_content_browser_client.cc
+++ b/android_webview/browser/aw_content_browser_client.cc
@@ -662,8 +662,7 @@
       (delegate) ? delegate->isModalContextMenu() : false;
 }
 
-std::vector<std::unique_ptr<content::NavigationThrottle>>
-AwContentBrowserClient::CreateThrottlesForNavigation(
+void AwContentBrowserClient::CreateThrottlesForNavigation(
     content::NavigationThrottleRegistry& registry) {
   // We allow intercepting only navigations within main frames. This
   // is used to post onPageStarted. We handle shouldOverrideUrlLoading
@@ -711,8 +710,6 @@
           &navigation_handle, urlClassifier));
     }
   }
-
-  return {};
 }
 
 std::unique_ptr<content::PrefetchServiceDelegate>
diff --git a/android_webview/browser/aw_content_browser_client.h b/android_webview/browser/aw_content_browser_client.h
index 13fb940..d2ed3814 100644
--- a/android_webview/browser/aw_content_browser_client.h
+++ b/android_webview/browser/aw_content_browser_client.h
@@ -149,8 +149,7 @@
       content::WebContents* web_contents,
       content::SiteInstance& main_frame_site,
       blink::web_pref::WebPreferences* web_prefs) override;
-  std::vector<std::unique_ptr<content::NavigationThrottle>>
-  CreateThrottlesForNavigation(
+  void CreateThrottlesForNavigation(
       content::NavigationThrottleRegistry& registry) override;
   std::unique_ptr<content::PrefetchServiceDelegate>
   CreatePrefetchServiceDelegate(
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/SharedWebViewChromium.java b/android_webview/glue/java/src/com/android/webview/chromium/SharedWebViewChromium.java
index 81135bf..d367f6a 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/SharedWebViewChromium.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/SharedWebViewChromium.java
@@ -18,6 +18,8 @@
 import org.chromium.content_public.browser.MessagePayload;
 import org.chromium.content_public.browser.MessagePort;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.Callable;
 
 /**
@@ -227,4 +229,29 @@
     }
 
     public void configureBaseline(int baseline) {}
+
+    public List<String> addJavascriptInterfaces(
+            List<Object> objects, List<String> names, List<List<String>> sitePatterns) {
+        // This is called specifically from the WebViewBuilder API which always builds
+        // and configures on the UI thread specifically. If we are not on the UI thread,
+        // this is an issue and should be reported back.
+        // Executing on the UI thread means we can return our validation results
+        // synchronously.
+        if (checkNeedsPost()) {
+            throw new IllegalStateException("WebView must not be configured outside of UI Thread");
+        }
+        assert objects.size() == names.size() && names.size() == sitePatterns.size();
+
+        // TODO: Add support to JS injection code in content to handle bulk push of
+        // patterns. JNIZero currently doesn't support multi dimensional arrays so
+        // List<List<String>> is a problem.
+        List<String> badPatterns = new ArrayList<>();
+
+        for (int i = 0; i < objects.size(); i++) {
+            badPatterns.addAll(
+                    mAwContents.addJavascriptInterface(
+                            objects.get(i), names.get(i), sitePatterns.get(i)));
+        }
+        return badPatterns;
+    }
 }
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 27ffd8c..babba53a 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
@@ -236,6 +236,9 @@
                 AndroidAutofillFeatures.ANDROID_AUTOFILL_VIRTUAL_VIEW_STRUCTURE_ANDROID_IN_CCT_NAME,
                 "Disables checking AutofilManager#isEnabled too early. Mainly affects CCTs."),
         Flag.baseFeature(
+                AndroidAutofillFeatures.ANDROID_AUTOFILL_LAZY_FRAMEWORK_WRAPPER_NAME,
+                "Enable lazily initializing framework Autofill wrapper."),
+        Flag.baseFeature(
                 AutofillFeatures.AUTOFILL_ACCEPT_DOM_MUTATION_AFTER_AUTOFILL_SUBMISSION,
                 "Accepts DOM_MUTATION_AFTER_AUTOFILL submissions detected on password forms."),
         Flag.baseFeature(
diff --git a/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/WebViewBuilderBoundaryInterface.java b/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/WebViewBuilderBoundaryInterface.java
index a2ae6f92..459ac03 100644
--- a/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/WebViewBuilderBoundaryInterface.java
+++ b/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/WebViewBuilderBoundaryInterface.java
@@ -15,6 +15,8 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.function.BiConsumer;
 import java.util.function.Consumer;
 
@@ -35,20 +37,38 @@
     @Target(ElementType.TYPE_USE)
     @IntDef({
         ConfigField.BASELINE,
+        ConfigField.JAVASCRIPT_INTERFACE,
     })
     @Retention(RetentionPolicy.SOURCE)
     @interface ConfigField {
         int BASELINE = 0;
+        int JAVASCRIPT_INTERFACE = 1;
     }
 
     class Config implements Consumer<BiConsumer<@ConfigField Integer, Object>> {
         public int baseline = Baseline.DEFAULT;
+        List<Object> mJavascriptInterfaceObjects = new ArrayList<>();
+        List<String> mJavascriptInterfaceNames = new ArrayList<>();
+        List<List<String>> mJavascriptInterfaceSitePatterns = new ArrayList<>();
+
+        public void addJavascriptInterface(Object object, String name, List<String> sitePatterns) {
+            mJavascriptInterfaceObjects.add(object);
+            mJavascriptInterfaceNames.add(name);
+            mJavascriptInterfaceSitePatterns.add(sitePatterns);
+        }
 
         // This method handles reading all config in AndroidX to transfer over to Chromium.
         // It's job is to essentially "serialize" the fields into known keys.
         @Override
         public void accept(BiConsumer<@ConfigField Integer, Object> chromiumConfig) {
             chromiumConfig.accept(ConfigField.BASELINE, baseline);
+            chromiumConfig.accept(
+                    ConfigField.JAVASCRIPT_INTERFACE,
+                    new Object[] {
+                        mJavascriptInterfaceObjects,
+                        mJavascriptInterfaceNames,
+                        mJavascriptInterfaceSitePatterns
+                    });
         }
     }
 
diff --git a/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebViewBuilderAdapter.java b/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebViewBuilderAdapter.java
index fdfea93..5818f7a 100644
--- a/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebViewBuilderAdapter.java
+++ b/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebViewBuilderAdapter.java
@@ -7,12 +7,15 @@
 import android.content.Context;
 import android.webkit.WebView;
 
+import androidx.annotation.Nullable;
+
 import com.android.webview.chromium.SharedWebViewChromium;
 import com.android.webview.chromium.WebkitToSharedGlueConverter;
 
 import org.chromium.support_lib_boundary.WebViewBuilderBoundaryInterface;
 import org.chromium.support_lib_boundary.WebViewBuilderBoundaryInterface.ConfigField;
 
+import java.util.List;
 import java.util.function.BiConsumer;
 import java.util.function.Consumer;
 
@@ -36,6 +39,9 @@
 
     static class Builder implements BiConsumer<@ConfigField Integer, Object> {
         private Integer mBaseline;
+        private @Nullable List<Object> mJavascriptInterfaceObjects;
+        private @Nullable List<String> mJavascriptInterfaceNames;
+        private @Nullable List<List<String>> mJavascriptInterfaceSitePatterns;
 
         @Override
         public void accept(@ConfigField Integer key, Object value) {
@@ -46,6 +52,15 @@
                             mBaseline = (Integer) value;
                             break;
                         }
+                    case ConfigField.JAVASCRIPT_INTERFACE:
+                        {
+                            Object[] interfaceParams = (Object[]) value;
+                            mJavascriptInterfaceObjects = (List<Object>) interfaceParams[0];
+                            mJavascriptInterfaceNames = (List<String>) interfaceParams[1];
+                            mJavascriptInterfaceSitePatterns =
+                                    (List<List<String>>) interfaceParams[2];
+                            break;
+                        }
                 }
             } catch (ClassCastException e) {
                 throw new InternalApiMismatchException(key + " was not configured correctly", e);
@@ -64,6 +79,15 @@
                 sharedWebViewChromium.configureBaseline(mBaseline);
             }
 
+            if (mJavascriptInterfaceObjects != null
+                    && mJavascriptInterfaceNames != null
+                    && mJavascriptInterfaceSitePatterns != null) {
+                sharedWebViewChromium.addJavascriptInterfaces(
+                        mJavascriptInterfaceObjects,
+                        mJavascriptInterfaceNames,
+                        mJavascriptInterfaceSitePatterns);
+            }
+
             return webview;
         }
     }
diff --git a/android_webview/test/data/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt b/android_webview/test/data/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
index 882fcda..c8e9897 100644
--- a/android_webview/test/data/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
+++ b/android_webview/test/data/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -5049,6 +5049,14 @@
     getter isComposing
     method constructor
     method getTargetRanges
+interface IntegrityViolationReportBody : ReportBody
+    attribute @@toStringTag
+    getter blockedURL
+    getter destination
+    getter documentURL
+    getter reportOnly
+    method constructor
+    method toJSON
 interface IntersectionObserver
     attribute @@toStringTag
     getter delay
diff --git a/android_webview/tools/run_cts.pydeps b/android_webview/tools/run_cts.pydeps
index 9f26c3bf..a80607f7 100644
--- a/android_webview/tools/run_cts.pydeps
+++ b/android_webview/tools/run_cts.pydeps
@@ -74,6 +74,7 @@
 //third_party/catapult/devil/devil/devil_env.py
 //third_party/catapult/devil/devil/utils/__init__.py
 //third_party/catapult/devil/devil/utils/cmd_helper.py
+//third_party/catapult/devil/devil/utils/host_utils.py
 //third_party/catapult/devil/devil/utils/lazy/__init__.py
 //third_party/catapult/devil/devil/utils/lazy/weak_constant.py
 //third_party/catapult/devil/devil/utils/logging_common.py
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 40ecc18..fa001839 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -3800,7 +3800,6 @@
     "app_menu/notification_overflow_view_unittest.cc",
     "assistant/assistant_alarm_timer_controller_unittest.cc",
     "assistant/assistant_controller_impl_unittest.cc",
-    "assistant/assistant_interaction_controller_impl_unittest.cc",
     "assistant/assistant_notification_controller_impl_unittest.cc",
     "assistant/assistant_setup_controller_unittest.cc",
     "assistant/assistant_state_controller_unittest.cc",
@@ -5516,7 +5515,6 @@
     "//components/desks_storage:test_support",
     "//components/drive",
     "//components/drive:test_support",
-    "//components/exo/wayland:test_controller_stub",
     "//components/exo/wayland:ui_controls_protocol_stub",
     "//components/manta/proto",
     "//components/prefs:test_support",
diff --git a/ash/accelerators/accelerator_table_unittest.cc b/ash/accelerators/accelerator_table_unittest.cc
index 05757eb..43c64dff 100644
--- a/ash/accelerators/accelerator_table_unittest.cc
+++ b/ash/accelerators/accelerator_table_unittest.cc
@@ -8,7 +8,7 @@
 #include <tuple>
 
 #include "ash/public/cpp/accelerators.h"
-#include "base/hash/md5.h"
+#include "ash/test/ash_test_util.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -19,9 +19,9 @@
 
 // The number of non-Search-based accelerators.
 constexpr int kNonSearchAcceleratorsNum = 113;
-// The hash of non-Search-based accelerators. See HashAcceleratorData().
+// The hash of non-Search-based accelerators.
 constexpr char kNonSearchAcceleratorsHash[] =
-    "d8d437fd800f34f648c5bc4bb47926b0";
+    "911675569c0f8f713f08027577f21eb620e16996f21f3aa172a6c805b9124ad8";
 
 struct Cmp {
   bool operator()(const AcceleratorData& lhs,
@@ -42,18 +42,6 @@
       (accelerator.modifiers & ui::EF_COMMAND_DOWN) ? "true" : "false");
 }
 
-std::string HashAcceleratorData(
-    const std::vector<AcceleratorData>& accelerators) {
-  base::MD5Context context;
-  base::MD5Init(&context);
-  for (const AcceleratorData& accelerator : accelerators) {
-    base::MD5Update(&context, AcceleratorDataToString(accelerator));
-  }
-  base::MD5Digest digest;
-  base::MD5Final(&digest, &context);
-  return MD5DigestToBase16(digest);
-}
-
 }  // namespace
 
 TEST(AcceleratorTableTest, CheckDuplicatedAccelerators) {
@@ -155,8 +143,8 @@
 
   std::stable_sort(non_search_accelerators.begin(),
                    non_search_accelerators.end(), Cmp());
-  const std::string non_search_accelerators_hash =
-      HashAcceleratorData(non_search_accelerators);
+  const std::string non_search_accelerators_hash = ash::StableHashOfCollection(
+      non_search_accelerators, AcceleratorDataToString);
 
   EXPECT_EQ(non_search_accelerators_hash, kNonSearchAcceleratorsHash)
       << "New accelerators must use the Search key. Please talk to the UX "
diff --git a/ash/app_list/model/app_list_model_export.h b/ash/app_list/model/app_list_model_export.h
index d3ee4be..c377831 100644
--- a/ash/app_list/model/app_list_model_export.h
+++ b/ash/app_list/model/app_list_model_export.h
@@ -10,11 +10,7 @@
 
 #if defined(COMPONENT_BUILD)
 
-#if defined(APP_LIST_MODEL_IMPLEMENTATION)
 #define APP_LIST_MODEL_EXPORT __attribute__((visibility("default")))
-#else
-#define APP_LIST_MODEL_EXPORT
-#endif
 
 #else  // defined(COMPONENT_BUILD)
 #define APP_LIST_MODEL_EXPORT
diff --git a/ash/app_list/views/search_box_view_unittest.cc b/ash/app_list/views/search_box_view_unittest.cc
index 27453c42..273f998 100644
--- a/ash/app_list/views/search_box_view_unittest.cc
+++ b/ash/app_list/views/search_box_view_unittest.cc
@@ -772,40 +772,6 @@
   EXPECT_EQ(ax::mojom::DefaultActionVerb::kClick, data.GetDefaultActionVerb());
 }
 
-class SearchBoxViewAssistantButtonTest : public SearchBoxViewTest {
- public:
-  SearchBoxViewAssistantButtonTest() = default;
-  SearchBoxViewAssistantButtonTest(const SearchBoxViewAssistantButtonTest&) =
-      delete;
-  SearchBoxViewAssistantButtonTest& operator=(
-      const SearchBoxViewAssistantButtonTest&) = delete;
-  ~SearchBoxViewAssistantButtonTest() override = default;
-
-  // Overridden from testing::Test
-  void SetUp() override {
-    SearchBoxViewTest::SetUp();
-    GetSearchModel()->search_box()->SetShowAssistantButton(true);
-  }
-};
-
-// Tests that the assistant button is visible by default.
-TEST_F(SearchBoxViewAssistantButtonTest, AssistantButtonVisibleByDefault) {
-  EXPECT_TRUE(view()->edge_button_container()->GetVisible());
-  EXPECT_TRUE(view()->assistant_button()->GetVisible());
-}
-
-// Tests that the assistant button is invisible after typing in the search box,
-// and comes back when search box is empty.
-TEST_F(SearchBoxViewAssistantButtonTest,
-       AssistantButtonChangeVisibilityWithTyping) {
-  KeyPress(ui::VKEY_A);
-  EXPECT_FALSE(view()->edge_button_container()->GetVisible());
-
-  KeyPress(ui::VKEY_BACK);
-  EXPECT_TRUE(view()->edge_button_container()->GetVisible());
-  EXPECT_TRUE(view()->assistant_button()->GetVisible());
-}
-
 class SearchBoxViewFilterButtonTest : public SearchBoxViewTest {
  public:
   SearchBoxViewFilterButtonTest() {
@@ -1206,7 +1172,6 @@
     non_zero_duration_mode_ =
         std::make_unique<ui::ScopedAnimationDurationScaleMode>(
             ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
-    GetSearchModel()->search_box()->SetShowAssistantButton(true);
   }
 
   std::unique_ptr<ui::ScopedAnimationDurationScaleMode> non_zero_duration_mode_;
@@ -1221,7 +1186,6 @@
   // hidden.
   EXPECT_FALSE(search_box->filter_and_close_button_container()->GetVisible());
   EXPECT_TRUE(search_box->edge_button_container()->GetVisible());
-  EXPECT_TRUE(search_box->assistant_button()->GetVisible());
 
   // Set search box to active state.
   search_box->SetSearchBoxActive(true, ui::EventType::kMousePressed);
@@ -1235,15 +1199,6 @@
       ui::LayerAnimationElement::AnimatableProperty::OPACITY));
   EXPECT_EQ(close_animator->GetTargetOpacity(), 1.0f);
 
-  // Assistant button should be fading out.
-  EXPECT_TRUE(search_box->edge_button_container()->GetVisible());
-  EXPECT_TRUE(search_box->assistant_button()->GetVisible());
-  auto* assistant_animator =
-      search_box->edge_button_container()->layer()->GetAnimator();
-  EXPECT_TRUE(assistant_animator->IsAnimatingProperty(
-      ui::LayerAnimationElement::AnimatableProperty::OPACITY));
-  EXPECT_EQ(assistant_animator->GetTargetOpacity(), 0.0f);
-
   // Set search box to inactive state, hiding the close button.
   search_box->SetSearchBoxActive(false, ui::EventType::kMousePressed);
 
@@ -1252,14 +1207,6 @@
   EXPECT_TRUE(close_animator->IsAnimatingProperty(
       ui::LayerAnimationElement::AnimatableProperty::OPACITY));
   EXPECT_EQ(close_animator->GetTargetOpacity(), 0.0f);
-
-  // Assistant button should be fading in.
-  EXPECT_TRUE(search_box->edge_button_container()->GetVisible());
-  EXPECT_TRUE(search_box->assistant_button()->GetVisible());
-  ASSERT_TRUE(assistant_animator);
-  EXPECT_TRUE(assistant_animator->IsAnimatingProperty(
-      ui::LayerAnimationElement::AnimatableProperty::OPACITY));
-  EXPECT_EQ(assistant_animator->GetTargetOpacity(), 1.0f);
 }
 
 // Test that activating and deactivating the search box causes the search icon
diff --git a/ash/app_menu/app_menu_export.h b/ash/app_menu/app_menu_export.h
index 3ba9e92..3065e2a 100644
--- a/ash/app_menu/app_menu_export.h
+++ b/ash/app_menu/app_menu_export.h
@@ -10,11 +10,7 @@
 
 #if defined(COMPONENT_BUILD)
 
-#if defined(APP_MENU_IMPLEMENTATION)
 #define APP_MENU_EXPORT __attribute__((visibility("default")))
-#else
-#define APP_MENU_EXPORT
-#endif
 
 #else  // defined(COMPONENT_BUILD)
 #define APP_MENU_EXPORT
diff --git a/ash/ash_export.h b/ash/ash_export.h
index 8e3cd9c4..bc2e7be 100644
--- a/ash/ash_export.h
+++ b/ash/ash_export.h
@@ -10,11 +10,7 @@
 
 #if defined(COMPONENT_BUILD)
 
-#if defined(ASH_IMPLEMENTATION)
 #define ASH_EXPORT __attribute__((visibility("default")))
-#else
-#define ASH_EXPORT
-#endif
 
 #else  // defined(COMPONENT_BUILD)
 #define ASH_EXPORT
diff --git a/ash/assistant/assistant_interaction_controller_impl_unittest.cc b/ash/assistant/assistant_interaction_controller_impl_unittest.cc
deleted file mode 100644
index e5636d33..0000000
--- a/ash/assistant/assistant_interaction_controller_impl_unittest.cc
+++ /dev/null
@@ -1,353 +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.
-
-#include "ash/assistant/assistant_interaction_controller_impl.h"
-
-#include <algorithm>
-#include <map>
-
-#include "ash/assistant/assistant_suggestions_controller_impl.h"
-#include "ash/assistant/model/assistant_interaction_model.h"
-#include "ash/assistant/model/assistant_interaction_model_observer.h"
-#include "ash/assistant/model/assistant_response.h"
-#include "ash/assistant/model/assistant_response_observer.h"
-#include "ash/assistant/model/ui/assistant_card_element.h"
-#include "ash/assistant/model/ui/assistant_error_element.h"
-#include "ash/assistant/model/ui/assistant_ui_element.h"
-#include "ash/assistant/test/assistant_ash_test_base.h"
-#include "ash/assistant/ui/assistant_view_ids.h"
-#include "ash/assistant/ui/main_stage/assistant_error_element_view.h"
-#include "ash/constants/ash_features.h"
-#include "ash/public/cpp/app_list/app_list_features.h"
-#include "ash/public/cpp/ash_web_view.h"
-#include "ash/public/cpp/assistant/controller/assistant_interaction_controller.h"
-#include "ash/public/cpp/assistant/controller/assistant_suggestions_controller.h"
-#include "ash/test/fake_android_intent_helper.h"
-#include "ash/test/test_ash_web_view.h"
-#include "base/functional/bind.h"
-#include "base/run_loop.h"
-#include "base/test/scoped_feature_list.h"
-#include "base/time/time.h"
-#include "chromeos/ash/services/assistant/public/cpp/assistant_service.h"
-#include "chromeos/ash/services/assistant/public/cpp/features.h"
-#include "chromeos/ash/services/assistant/test_support/mock_assistant_interaction_subscriber.h"
-#include "testing/gmock/include/gmock/gmock.h"
-
-namespace ash {
-
-namespace {
-
-using assistant::AndroidAppInfo;
-using assistant::Assistant;
-using assistant::AssistantInteractionMetadata;
-using assistant::AssistantInteractionSubscriber;
-using assistant::AssistantInteractionType;
-using assistant::AssistantQuerySource;
-using assistant::AssistantSuggestion;
-using assistant::AssistantSuggestionType;
-using assistant::MockAssistantInteractionSubscriber;
-using assistant::ScopedAssistantInteractionSubscriber;
-
-using ::testing::Invoke;
-using ::testing::Mock;
-using ::testing::Return;
-using ::testing::StrictMock;
-
-// Mocks -----------------------------------------------------------------------
-
-class AssistantInteractionSubscriberMock
-    : public AssistantInteractionSubscriber {
- public:
-  explicit AssistantInteractionSubscriberMock(Assistant* service) {
-    scoped_subscriber_.Observe(service);
-  }
-
-  ~AssistantInteractionSubscriberMock() override = default;
-
-  MOCK_METHOD(void,
-              OnInteractionStarted,
-              (const AssistantInteractionMetadata&),
-              (override));
-
- private:
-  ScopedAssistantInteractionSubscriber scoped_subscriber_{this};
-};
-
-// AssistantInteractionControllerImplTest --------------------------------------
-
-class AssistantInteractionControllerImplTest : public AssistantAshTestBase {
- public:
-  AssistantInteractionControllerImplTest() = default;
-
-  AssistantInteractionControllerImpl* interaction_controller() {
-    return static_cast<AssistantInteractionControllerImpl*>(
-        AssistantInteractionController::Get());
-  }
-
-  AssistantSuggestionsControllerImpl* suggestion_controller() {
-    return static_cast<AssistantSuggestionsControllerImpl*>(
-        AssistantSuggestionsController::Get());
-  }
-
-  const AssistantInteractionModel* interaction_model() {
-    return interaction_controller()->GetModel();
-  }
-
-  void StartInteraction() {
-    interaction_controller()->OnInteractionStarted(
-        AssistantInteractionMetadata());
-  }
-
-  AndroidAppInfo CreateAndroidAppInfo(const std::string& app_name = "unknown") {
-    AndroidAppInfo result;
-    result.localized_app_name = app_name;
-    return result;
-  }
-};
-
-AssistantCardElement* GetAssistantCardElement(
-    const std::vector<std::unique_ptr<AssistantUiElement>>& ui_elements) {
-  if (ui_elements.size() != 1lu ||
-      ui_elements.front()->type() != AssistantUiElementType::kCard) {
-    return nullptr;
-  }
-
-  return static_cast<AssistantCardElement*>(ui_elements.front().get());
-}
-
-}  // namespace
-
-TEST_F(AssistantInteractionControllerImplTest,
-       ShouldBecomeActiveWhenInteractionStarts) {
-  EXPECT_EQ(interaction_model()->interaction_state(),
-            InteractionState::kInactive);
-
-  interaction_controller()->OnInteractionStarted(
-      AssistantInteractionMetadata());
-
-  EXPECT_EQ(interaction_model()->interaction_state(),
-            InteractionState::kActive);
-}
-
-TEST_F(AssistantInteractionControllerImplTest,
-       ShouldBeNoOpWhenOpenAppIsCalledWhileInactive) {
-  EXPECT_EQ(interaction_model()->interaction_state(),
-            InteractionState::kInactive);
-
-  FakeAndroidIntentHelper fake_helper;
-  fake_helper.AddApp("app-name", "app-intent");
-  interaction_controller()->OnOpenAppResponse(CreateAndroidAppInfo("app-name"));
-
-  EXPECT_FALSE(fake_helper.last_launched_android_intent().has_value());
-}
-
-TEST_F(AssistantInteractionControllerImplTest,
-       ShouldBeNoOpWhenOpenAppIsCalledForUnknownAndroidApp) {
-  StartInteraction();
-  FakeAndroidIntentHelper fake_helper;
-  interaction_controller()->OnOpenAppResponse(
-      CreateAndroidAppInfo("unknown-app-name"));
-
-  EXPECT_FALSE(fake_helper.last_launched_android_intent().has_value());
-}
-
-TEST_F(AssistantInteractionControllerImplTest,
-       ShouldLaunchAppAndReturnSuccessWhenOpenAppIsCalled) {
-  const std::string app_name = "AppName";
-  const std::string intent = "intent://AppName";
-
-  StartInteraction();
-  FakeAndroidIntentHelper fake_helper;
-  fake_helper.AddApp(app_name, intent);
-
-  interaction_controller()->OnOpenAppResponse(CreateAndroidAppInfo(app_name));
-
-  EXPECT_EQ(intent, fake_helper.last_launched_android_intent());
-}
-
-TEST_F(AssistantInteractionControllerImplTest,
-       ShouldAddSchemeToIntentWhenLaunchingAndroidApp) {
-  const std::string app_name = "AppName";
-  const std::string intent = "#Intent-without-a-scheme";
-  const std::string intent_with_scheme = "intent://" + intent;
-
-  StartInteraction();
-  FakeAndroidIntentHelper fake_helper;
-  fake_helper.AddApp(app_name, intent);
-
-  interaction_controller()->OnOpenAppResponse(CreateAndroidAppInfo(app_name));
-
-  EXPECT_EQ(intent_with_scheme, fake_helper.last_launched_android_intent());
-}
-
-TEST_F(AssistantInteractionControllerImplTest,
-       ShouldCorrectlyMapSuggestionTypeToQuerySource) {
-  // Mock Assistant interaction subscriber.
-  StrictMock<AssistantInteractionSubscriberMock> mock(assistant_service());
-
-  // Configure the expected mappings between suggestion type and query source.
-  const std::map<AssistantSuggestionType, AssistantQuerySource>
-      types_to_sources = {{AssistantSuggestionType::kConversationStarter,
-                           AssistantQuerySource::kConversationStarter},
-                          {AssistantSuggestionType::kBetterOnboarding,
-                           AssistantQuerySource::kBetterOnboarding},
-                          {AssistantSuggestionType::kUnspecified,
-                           AssistantQuerySource::kSuggestionChip}};
-
-  // Iterate over all expected mappings.
-  for (const auto& type_to_source : types_to_sources) {
-    base::RunLoop run_loop;
-
-    // Confirm subscribers are delivered the expected query source...
-    EXPECT_CALL(mock, OnInteractionStarted)
-        .WillOnce(Invoke([&](const AssistantInteractionMetadata& metadata) {
-          EXPECT_EQ(type_to_source.second, metadata.source);
-          run_loop.QuitClosure().Run();
-        }));
-
-    AssistantSuggestion suggestion{/*id=*/base::UnguessableToken::Create(),
-                                   /*type=*/type_to_source.first,
-                                   /*text=*/""};
-    const_cast<AssistantSuggestionsModel*>(suggestion_controller()->GetModel())
-        ->SetConversationStarters({suggestion});
-
-    // ...when an Assistant suggestion of a given type is pressed.
-    interaction_controller()->OnSuggestionPressed(suggestion.id);
-
-    run_loop.Run();
-  }
-}
-
-TEST_F(AssistantInteractionControllerImplTest, ShouldDisplayGenericErrorOnce) {
-  StartInteraction();
-
-  // Call OnTtsStarted twice to mimic the behavior of libassistant when network
-  // is disconnected.
-  interaction_controller()->OnTtsStarted(/*due_to_error=*/true);
-  interaction_controller()->OnTtsStarted(/*due_to_error=*/true);
-
-  base::RunLoop().RunUntilIdle();
-
-  auto& ui_elements =
-      interaction_controller()->GetModel()->response()->GetUiElements();
-
-  EXPECT_EQ(ui_elements.size(), 1ul);
-  EXPECT_EQ(ui_elements.front()->type(), AssistantUiElementType::kError);
-
-  base::RunLoop().RunUntilIdle();
-
-  interaction_controller()->OnInteractionFinished(
-      assistant::AssistantInteractionResolution::kError);
-
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_EQ(ui_elements.size(), 1ul);
-  EXPECT_EQ(ui_elements.front()->type(), AssistantUiElementType::kError);
-}
-
-TEST_F(AssistantInteractionControllerImplTest,
-       ShouldUpdateTimeOfLastInteraction) {
-  MockAssistantInteractionSubscriber mock_subscriber;
-  ScopedAssistantInteractionSubscriber scoped_subscriber{&mock_subscriber};
-  scoped_subscriber.Observe(assistant_service());
-
-  base::RunLoop run_loop;
-  base::Time actual_time_of_last_interaction;
-  EXPECT_CALL(mock_subscriber, OnInteractionStarted)
-      .WillOnce(Invoke([&](const AssistantInteractionMetadata& metadata) {
-        actual_time_of_last_interaction = base::Time::Now();
-        run_loop.QuitClosure().Run();
-      }));
-
-  ShowAssistantUi();
-  MockTextInteraction().WithTextResponse("<Any-Text-Response>");
-  run_loop.Run();
-
-  auto actual = interaction_controller()->GetTimeDeltaSinceLastInteraction();
-  auto expected = base::Time::Now() - actual_time_of_last_interaction;
-
-  EXPECT_NEAR(actual.InSeconds(), expected.InSeconds(), 1);
-}
-
-TEST_F(AssistantInteractionControllerImplTest, CompactBubbleLauncher) {
-  static constexpr int kStandardLayoutAshWebViewWidth = 592;
-  static constexpr int kNarrowLayoutAshWebViewWidth = 496;
-
-  UpdateDisplay("1200x800");
-  ShowAssistantUi();
-  StartInteraction();
-
-  interaction_controller()->OnHtmlResponse("<html></html>", "fallback");
-
-  base::RunLoop().RunUntilIdle();
-
-  AssistantCardElement* card_element = GetAssistantCardElement(
-      interaction_controller()->GetModel()->response()->GetUiElements());
-  ASSERT_TRUE(card_element);
-  EXPECT_EQ(card_element->viewport_width(), 638);
-  EXPECT_EQ(
-      page_view()->GetViewByID(AssistantViewID::kAshWebView)->size().width(),
-      kStandardLayoutAshWebViewWidth);
-
-  ASSERT_TRUE(page_view()->GetViewByID(AssistantViewID::kAshWebView) !=
-              nullptr);
-  TestAshWebView* ash_web_view = static_cast<TestAshWebView*>(
-      page_view()->GetViewByID(AssistantViewID::kAshWebView));
-  // max_size and min_size in AshWebView::InitParams are different from the view
-  // size. min_size affects to the size of rendered content, i.e. renderer will
-  // try to render the content to the size. But View::Size() doesn't.
-  ASSERT_TRUE(ash_web_view->init_params_for_testing().max_size);
-  ASSERT_TRUE(ash_web_view->init_params_for_testing().min_size);
-  EXPECT_EQ(ash_web_view->init_params_for_testing().max_size.value().width(),
-            kStandardLayoutAshWebViewWidth);
-  EXPECT_EQ(ash_web_view->init_params_for_testing().min_size.value().width(),
-            kStandardLayoutAshWebViewWidth);
-
-  CloseAssistantUi();
-
-  // Change work area width < 1200 and confirm that the viewport width gets
-  // updated to narrow layout one.
-  UpdateDisplay("1199x800");
-  ShowAssistantUi();
-  StartInteraction();
-
-  interaction_controller()->OnHtmlResponse("<html></html>", "fallback");
-
-  base::RunLoop().RunUntilIdle();
-
-  card_element = GetAssistantCardElement(
-      interaction_controller()->GetModel()->response()->GetUiElements());
-  ASSERT_TRUE(card_element);
-  ASSERT_TRUE(page_view()->GetViewByID(AssistantViewID::kAshWebView) !=
-              nullptr);
-  EXPECT_EQ(card_element->viewport_width(), 542);
-  EXPECT_EQ(
-      page_view()->GetViewByID(AssistantViewID::kAshWebView)->size().width(),
-      kNarrowLayoutAshWebViewWidth);
-
-  ASSERT_TRUE(page_view()->GetViewByID(AssistantViewID::kAshWebView) !=
-              nullptr);
-  ash_web_view = static_cast<TestAshWebView*>(
-      page_view()->GetViewByID(AssistantViewID::kAshWebView));
-  ASSERT_TRUE(ash_web_view->init_params_for_testing().max_size);
-  ASSERT_TRUE(ash_web_view->init_params_for_testing().min_size);
-  EXPECT_EQ(ash_web_view->init_params_for_testing().max_size.value().width(),
-            kNarrowLayoutAshWebViewWidth);
-  EXPECT_EQ(ash_web_view->init_params_for_testing().min_size.value().width(),
-            kNarrowLayoutAshWebViewWidth);
-}
-
-TEST_F(AssistantInteractionControllerImplTest, FixedZoomLevel) {
-  ShowAssistantUi();
-  StartInteraction();
-
-  interaction_controller()->OnHtmlResponse("<html></html>", "fallback");
-
-  base::RunLoop().RunUntilIdle();
-
-  TestAshWebView* ash_web_view = static_cast<TestAshWebView*>(
-      page_view()->GetViewByID(AssistantViewID::kAshWebView));
-  EXPECT_TRUE(ash_web_view->init_params_for_testing().fix_zoom_level_to_one);
-}
-}  // namespace ash
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index 6a5a0639..50a5918 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -1429,6 +1429,11 @@
              "HealthdInternalsTabs",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
+// Enables Kiosk session for the Helium android app.
+BASE_FEATURE(kHeliumArcvmKiosk,
+             "HeliumArcvmKiosk",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 // If enabled, the Help app will render the App Detail Page and entry point.
 BASE_FEATURE(kHelpAppAppDetailPage,
              "HelpAppAppDetailPage",
@@ -3928,6 +3933,10 @@
   return base::FeatureList::IsEnabled(kHealthdInternalsTabs);
 }
 
+bool IsHeliumArcvmKioskEnabled() {
+  return base::FeatureList::IsEnabled(kHeliumArcvmKiosk);
+}
+
 bool IsHibernateEnabled() {
   return base::FeatureList::IsEnabled(kHibernate);
 }
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h
index 15fe4bb..8e7c59d 100644
--- a/ash/constants/ash_features.h
+++ b/ash/constants/ash_features.h
@@ -508,6 +508,8 @@
 COMPONENT_EXPORT(ASH_CONSTANTS)
 BASE_DECLARE_FEATURE(kHealthdInternalsTabs);
 COMPONENT_EXPORT(ASH_CONSTANTS)
+BASE_DECLARE_FEATURE(kHeliumArcvmKiosk);
+COMPONENT_EXPORT(ASH_CONSTANTS)
 BASE_DECLARE_FEATURE(kHelpAppAppDetailPage);
 COMPONENT_EXPORT(ASH_CONSTANTS)
 BASE_DECLARE_FEATURE(kHelpAppAppsList);
@@ -1246,6 +1248,7 @@
 COMPONENT_EXPORT(ASH_CONSTANTS)
 bool AreAnyGlanceablesTimeManagementViewsEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool AreHealthdInternalsTabsEnabled();
+COMPONENT_EXPORT(ASH_CONSTANTS) bool IsHeliumArcvmKioskEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsHibernateEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsHideShelfControlsInTabletModeEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsHoldingSpaceSuggestionsEnabled();
diff --git a/ash/keyboard/ui/keyboard_export.h b/ash/keyboard/ui/keyboard_export.h
index cedeeec..a0e455c8 100644
--- a/ash/keyboard/ui/keyboard_export.h
+++ b/ash/keyboard/ui/keyboard_export.h
@@ -18,11 +18,7 @@
 #endif  // defined(KEYBOARD_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(KEYBOARD_IMPLEMENTATION)
 #define KEYBOARD_EXPORT __attribute__((visibility("default")))
-#else
-#define KEYBOARD_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/ash/public/cpp/accelerator_actions_unittest.cc b/ash/public/cpp/accelerator_actions_unittest.cc
index 389344718..c8418d9 100644
--- a/ash/public/cpp/accelerator_actions_unittest.cc
+++ b/ash/public/cpp/accelerator_actions_unittest.cc
@@ -6,10 +6,9 @@
 
 #include "ash/constants/ash_switches.h"
 #include "ash/test/ash_test_base.h"
+#include "ash/test/ash_test_util.h"
 #include "base/command_line.h"
 #include "base/containers/fixed_flat_map.h"
-#include "base/hash/md5.h"
-#include "base/hash/md5_boringssl.h"
 #include "base/test/metrics/histogram_enum_reader.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -18,18 +17,19 @@
 namespace {
 
 // The total number of accelerator actions.
-constexpr int kAcceleratorActionsTotalNum = 171;
+constexpr size_t kAcceleratorActionsTotalNum = 171;
 // The toal number of debug accelerators, these will not be used for hashing.
-constexpr int kDebugAcceleratorActionsNum = 28;
+constexpr size_t kDebugAcceleratorActionsNum = 28;
 // The hash of accelerator actions. Please update this when adding a new
 // accelerator action.
-constexpr char kAcceleratorActionsHash[] = "58afc0af7632d35b39039ab49c1359bd";
+constexpr char kAcceleratorActionsHash[] =
+    "0827c8b3db8a74c4c8f080814060465fdf54f0f9bc2cc913cb549b8df6f67bb3";
 
 // Define the mapping between an AcceleratorAction and its string name.
 // Example:
 //   AcceleratorAction::kDevToggleUnifiedDesktop -> "DevToggleUnifiedDesktop".
 constexpr static auto kAcceleratorActionToName =
-    base::MakeFixedFlatMap<AcceleratorAction, const char*>({
+    base::MakeFixedFlatMap<AcceleratorAction, std::string_view>({
 #define ACCELERATOR_ACTION_ENTRY(action) \
   {AcceleratorAction::k##action, #action},
 #define ACCELERATOR_ACTION_ENTRY_FIXED_VALUE(action, value) \
@@ -100,29 +100,16 @@
       "`kDebugAcceleratorActionsNum` (if applicable).";
 
   // First check that the size of the enum is correct.
-  const int current_actions_size = kAcceleratorActionToName.size();
-  EXPECT_EQ(current_actions_size, kAcceleratorActionsTotalNum)
-      << kCommonMessage;
+  ASSERT_EQ(kAcceleratorActionToName.size(), kAcceleratorActionsTotalNum);
+  const size_t kAcceleratorsToHashNum =
+      kAcceleratorActionsTotalNum - kDebugAcceleratorActionsNum;
+  const std::string hash = ash::StableHashOfCollection(
+      base::span(kAcceleratorActionToName).first<kAcceleratorsToHashNum>(),
+      [](const auto& item) { return item.second; });
 
-  // Then check that the hash is correct.
-  base::MD5Context context;
-  base::MD5Init(&context);
-  int iter_count = 0;
-  for (const auto iter : kAcceleratorActionToName) {
-    base::MD5Update(&context, iter.second);
-    // Only hash up non-debug accelerator actions.
-    if (++iter_count >= current_actions_size - kDebugAcceleratorActionsNum) {
-      break;
-    }
-  }
-
-  base::MD5Digest digest;
-  base::MD5Final(&digest, &context);
-  const std::string current_hash = MD5DigestToBase16(digest);
-
-  EXPECT_EQ(current_hash, kAcceleratorActionsHash)
+  EXPECT_EQ(hash, kAcceleratorActionsHash)
       << kCommonMessage << " Please update kAcceleratorActionsHash to: \n"
-      << current_hash << "\n";
+      << hash << "\n";
 }
 
 INSTANTIATE_TEST_SUITE_P(
diff --git a/ash/shelf/shelf_widget.cc b/ash/shelf/shelf_widget.cc
index 861494e..0e2fed7e 100644
--- a/ash/shelf/shelf_widget.cc
+++ b/ash/shelf/shelf_widget.cc
@@ -504,16 +504,12 @@
       -shelf->SelectValueForShelfAlignment(0, 0, safety_margin)));
   opaque_back_ground_layer->SetBounds(opaque_background_bounds);
 
-  const bool is_vertical_alignment_in_overview =
-      in_overview_mode && !shelf->IsHorizontalAlignment();
-
-  // Show rounded corners except in maximized (which includes split view) mode,
-  // or whenever we are "in app", or the shelf is on the vertical alignment in
-  // overview mode.
+  // Do not show rounded corners when the background is in maximized mode (app
+  // window covers entire background, including splitscreen), whenever we are
+  // "in app", or in overview mode (vertical or horizontal).
   if (background_type == ShelfBackgroundType::kMaximized ||
       background_type == ShelfBackgroundType::kInApp ||
-      (tablet_mode && (in_app || split_view)) ||
-      is_vertical_alignment_in_overview) {
+      background_type == ShelfBackgroundType::kOverview) {
     opaque_background_.SetRoundedCornerRadius(0);
   } else {
     opaque_background_.SetRoundedCornerRadius(radius);
diff --git a/ash/test/ash_test_util.h b/ash/test/ash_test_util.h
index 407ecdd4..b0ced26b5 100644
--- a/ash/test/ash_test_util.h
+++ b/ash/test/ash_test_util.h
@@ -5,12 +5,21 @@
 #ifndef ASH_TEST_ASH_TEST_UTIL_H_
 #define ASH_TEST_ASH_TEST_UTIL_H_
 
+#include <array>
 #include <cstddef>
+#include <iterator>
+#include <ranges>
+#include <string>
 #include <string_view>
+#include <utility>
 #include <variant>
 
+#include "base/containers/span.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
 #include "chromeos/ui/frame/caption_buttons/frame_size_button.h"
 #include "chromeos/ui/frame/multitask_menu/multitask_menu_metrics.h"
+#include "crypto/hash.h"
 #include "services/data_decoder/public/mojom/image_decoder.mojom-shared.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/aura/window.h"
@@ -123,6 +132,36 @@
 // NOTE: This function causes an infinite loop if the target widget never shows.
 views::Widget* FindWidgetWithNameAndWaitIfNeeded(const std::string& name);
 
+// Given a range r with elements r0, r1, ..., rn and an optional projection p
+// (which defaults to the identity), return a stable hash of the concatenation
+// of p(r0), p(r1), ..., p(rn). This is used in some tests to ensure that global
+// data tables are not changed without consulting the owners of those tables or
+// that other invariants are not violated.
+//
+// For example, use this like:
+//   std::vector<MyType> things;
+//   std::string hash = StableHashOfCollection(things, [](const MyType& thing) {
+//     return thing.ToString();
+//   };
+//
+// The projection must return something that base::as_byte_span() accepts - a
+// string, vector of bytes, or many other range-like types are acceptable.
+//
+// This hash is guaranteed stable: the same values, supplied in the same order,
+// will hash to the same value between test runs.
+template <typename Range, typename Proj = std::identity>
+  requires std::ranges::range<Range> &&
+           std::indirectly_unary_invocable<Proj, std::ranges::iterator_t<Range>>
+std::string StableHashOfCollection(const Range& range, Proj proj = {}) {
+  crypto::hash::Hasher hasher(crypto::hash::HashKind::kSha256);
+  for (const auto& element : range) {
+    hasher.Update(base::as_byte_span(proj(element)));
+  }
+  std::array<uint8_t, crypto::hash::kSha256Size> hash;
+  hasher.Finish(hash);
+  return base::ToLowerASCII(base::HexEncode(base::as_string_view(hash)));
+}
+
 }  // namespace ash
 
 #endif  // ASH_TEST_ASH_TEST_UTIL_H_
diff --git a/ash/webui/shortcut_customization_ui/backend/accelerator_layout_table_unittest.cc b/ash/webui/shortcut_customization_ui/backend/accelerator_layout_table_unittest.cc
index 230bfef..132f8f8 100644
--- a/ash/webui/shortcut_customization_ui/backend/accelerator_layout_table_unittest.cc
+++ b/ash/webui/shortcut_customization_ui/backend/accelerator_layout_table_unittest.cc
@@ -9,9 +9,8 @@
 #include "ash/public/cpp/accelerator_actions.h"
 #include "ash/public/cpp/accelerators.h"
 #include "ash/public/mojom/accelerator_info.mojom-shared.h"
+#include "ash/test/ash_test_util.h"
 #include "base/containers/contains.h"
-#include "base/hash/md5.h"
-#include "base/hash/md5_boringssl.h"
 #include "base/strings/strcat.h"
 #include "base/strings/stringprintf.h"
 #include "chromeos/ash/services/assistant/public/cpp/features.h"
@@ -25,7 +24,8 @@
 // The total number of Ash accelerators.
 constexpr int kAshAcceleratorsTotalNum = 160;
 // The hash of Ash accelerators.
-constexpr char kAshAcceleratorsHash[] = "864aa0f17421e1ce783c85780a7cf357";
+constexpr char kAshAcceleratorsHash[] =
+    "7c9f5d090e6be1c01bcfca53b67a79956737dbba149b4e683b44c6ace07e509d";
 
 std::string ToActionName(ash::AcceleratorAction action) {
   return base::StrCat(
@@ -60,19 +60,6 @@
   }
 };
 
-std::string HashAshAcceleratorData(
-    const std::vector<ash::AcceleratorData>& accelerators) {
-  base::MD5Context context;
-  base::MD5Init(&context);
-  for (const auto& accelerator : accelerators) {
-    base::MD5Update(&context, AshAcceleratorDataToString(accelerator));
-  }
-
-  base::MD5Digest digest;
-  base::MD5Final(&digest, &context);
-  return MD5DigestToBase16(digest);
-}
-
 class AcceleratorLayoutMetadataTest : public testing::Test {
  public:
   AcceleratorLayoutMetadataTest() = default;
@@ -182,7 +169,7 @@
   std::stable_sort(ash_accelerators.begin(), ash_accelerators.end(),
                    AshAcceleratorDataCmp());
   const std::string ash_accelerators_hash =
-      HashAshAcceleratorData(ash_accelerators);
+      ash::StableHashOfCollection(ash_accelerators, AshAcceleratorDataToString);
   EXPECT_EQ(ash_accelerators_hash, kAshAcceleratorsHash)
       << kCommonMessage << "kAshAcceleratorsHash=\"" << ash_accelerators_hash
       << "\"\n";
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 02ac517..3b5e3da6 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -4573,7 +4573,7 @@
       "android/java/src/org/chromium/base/process_launcher/ChildServiceConnectionDelegate.java",
       "android/java/src/org/chromium/base/process_launcher/ChildServiceConnectionFactory.java",
       "android/java/src/org/chromium/base/process_launcher/ChildServiceConnectionImpl.java",
-      "android/java/src/org/chromium/base/process_launcher/FileDescriptorInfo.java",
+      "android/java/src/org/chromium/base/process_launcher/RebindServiceConnection.java",
     ]
   }
 
@@ -4596,6 +4596,7 @@
     # do no append to this list.
     sources = [
       "android/java/src/org/chromium/base/ApkAssets.java",
+      "android/java/src/org/chromium/base/BaseFeatureList.java",
       "android/java/src/org/chromium/base/BaseFeatureMap.java",
       "android/java/src/org/chromium/base/BinderCallsListener.java",
       "android/java/src/org/chromium/base/CallbackController.java",
@@ -5302,7 +5303,9 @@
   android_aidl("process_launcher_aidl") {
     import_include = [ "android/java/src" ]
     sources = [
+      "android/java/src/org/chromium/base/process_launcher/IChildProcessArgs.aidl",
       "android/java/src/org/chromium/base/process_launcher/IChildProcessService.aidl",
+      "android/java/src/org/chromium/base/process_launcher/IFileDescriptorInfo.aidl",
       "android/java/src/org/chromium/base/process_launcher/IParentProcess.aidl",
     ]
   }
diff --git a/base/allocator/partition_allocator/buckets.md b/base/allocator/partition_allocator/buckets.md
index ae3536b..97ce6f11 100644
--- a/base/allocator/partition_allocator/buckets.md
+++ b/base/allocator/partition_allocator/buckets.md
@@ -77,6 +77,12 @@
 manner up to nearly 256 bytes. This pattern then repeats for sizes above 256
 bytes, then 512 bytes, and so forth.
 
+||Order-Index 0|Order-Index 1|Order-Index 2|Order-Index 3|Order-Index 4|Order-Index 5|Order-Index 6|Order-Index 7|
+|-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
+|Order  8 (2⁷)|121-128|129-144|145-160|161-176|177-192|193-208|209-224|225-240|
+|Order  9 (2⁸)|241-256|257-288|289-320|321-352|353-384|385-416|417-448|449-480|
+|Order 10 (2⁹)|481-512|513-576|577-640|641-704|705-768|769-832|833-896|897-960|
+
 ## Neutral Bucket Distribution
 
 The Neutral Bucket Distribution offers a sparser alternative, derived from the
@@ -97,7 +103,7 @@
 
 ### 8 Bytes Alignment (Typically 32-bit Systems)
 
-| Index | Size | Bucket Distribution | Note |
+| Index | Size | Bucket Distribution | Originating Formula |
 | -: | -: | :- | :- |
 |   0 |      8 | `kNeutral` and `kDenser` | linear [8 x 1] |
 |   1 |     16 | `kNeutral` and `kDenser` | linear [8 x 2] |
@@ -217,11 +223,11 @@
 | 115 | 786432 | `kNeutral` and `kDenser` | exponential [2¹⁹ x (1 + ½)] |
 | 116 | 851968 | `kDenser` only           | exponential [2¹⁹ x (1 + ⅝)] |
 | 117 | 917504 | `kNeutral` and `kDenser` | exponential [2¹⁹ x (1 + ¾)] |
-| 118 | 983040 | `kDenser` only           | exponential [2¹⁹ x (1 + ⅞)] |
+| 118 | 983040 | `kNeutral` and `kDenser` | exponential [2¹⁹ x (1 + ⅞)] |
 
-### 8 Bytes Alignment (Typically 64-bit Systems)
+### 16 Bytes Alignment (Typically 64-bit Systems)
 
-| Index | Size | Bucket Distribution | Note |
+| Index | Size | Bucket Distribution | Originating Formula |
 | -: | -: | :- | :- |
 |   0 |     16 | `kNeutral` and `kDenser` | linear [16 x 1] |
 |   1 |     32 | `kNeutral` and `kDenser` | linear [16 x 2] |
diff --git a/base/allocator/partition_allocator/src/partition_alloc/page_allocator_unittest.cc b/base/allocator/partition_allocator/src/partition_alloc/page_allocator_unittest.cc
index c136658..5c5bc31 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/page_allocator_unittest.cc
+++ b/base/allocator/partition_allocator/src/partition_alloc/page_allocator_unittest.cc
@@ -561,7 +561,13 @@
   FreePages(buffer, size);
 }
 
-TEST(PartitionAllocPageAllocatorTest, DecommitAndZero) {
+// TODO(crbug.com/416151077): Enabled the test on UBSan.
+#if defined(UNDEFINED_SANITIZER)
+#define MAYBE_DecommitAndZero DISABLED_DecommitAndZero
+#else
+#define MAYBE_DecommitAndZero DecommitAndZero
+#endif
+TEST(PartitionAllocPageAllocatorTest, MAYBE_DecommitAndZero) {
   size_t size = PageAllocationGranularity();
   uintptr_t buffer = AllocPages(size, PageAllocationGranularity(),
                                 PageAccessibilityConfiguration(
diff --git a/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/component_export.h b/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/component_export.h
index 36c74fa..94a565b 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/component_export.h
+++ b/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/component_export.h
@@ -39,7 +39,7 @@
 #define PA_COMPONENT_IMPORT_ANNOTATION __declspec(dllimport)
 #else  // defined(WIN32)
 #define PA_COMPONENT_EXPORT_ANNOTATION __attribute__((visibility("default")))
-#define PA_COMPONENT_IMPORT_ANNOTATION
+#define PA_COMPONENT_IMPORT_ANNOTATION __attribute__((visibility("default")))
 #endif  // defined(WIN32)
 #else   // defined(COMPONENT_BUILD)
 #define PA_COMPONENT_EXPORT_ANNOTATION
diff --git a/base/android/base_feature_map.cc b/base/android/base_feature_map.cc
index 48c27157..c1cfeb15 100644
--- a/base/android/base_feature_map.cc
+++ b/base/android/base_feature_map.cc
@@ -19,6 +19,7 @@
     &features::kBackgroundNotPerceptibleBinding,
     &features::kPostPowerMonitorBroadcastReceiverInitToBackground,
     &features::kPostGetMyMemoryStateToBackground,
+    &features::kUseSharedRebindServiceConnection,
 };
 
 // static
diff --git a/base/android/content_uri_utils.cc b/base/android/content_uri_utils.cc
index be84f86..59bc6cc 100644
--- a/base/android/content_uri_utils.cc
+++ b/base/android/content_uri_utils.cc
@@ -54,13 +54,28 @@
   }
 }
 
-int OpenContentUri(const FilePath& content_uri, uint32_t open_flags) {
-  JNIEnv* env = base::android::AttachCurrentThread();
+ScopedJavaLocalRef<jobject> OpenContentUri(const FilePath& content_uri,
+                                           uint32_t open_flags) {
+  JNIEnv* env = android::AttachCurrentThread();
   auto mode = TranslateOpenFlagsToJavaMode(open_flags);
   CHECK(mode.has_value()) << "Unsupported flags=0x" << std::hex << open_flags;
   return Java_ContentUriUtils_openContentUri(env, content_uri.value(), *mode);
 }
 
+int ContentUriGetFd(const JavaRef<jobject>& java_parcel_file_descriptor) {
+  if (!java_parcel_file_descriptor) {
+    return -1;
+  }
+  JNIEnv* env = android::AttachCurrentThread();
+  int fd = Java_ContentUriUtils_getFd(env, java_parcel_file_descriptor);
+  return dup(fd);
+}
+
+void ContentUriClose(const JavaRef<jobject>& java_parcel_file_descriptor) {
+  JNIEnv* env = android::AttachCurrentThread();
+  Java_ContentUriUtils_close(env, java_parcel_file_descriptor);
+}
+
 bool ContentUriGetFileInfo(const FilePath& content_uri,
                            FileEnumerator::FileInfo* info) {
   JNIEnv* env = android::AttachCurrentThread();
diff --git a/base/android/content_uri_utils.h b/base/android/content_uri_utils.h
index 9663cd4..d0f8b58 100644
--- a/base/android/content_uri_utils.h
+++ b/base/android/content_uri_utils.h
@@ -10,6 +10,7 @@
 #include <string>
 #include <vector>
 
+#include "base/android/scoped_java_ref.h"
 #include "base/base_export.h"
 #include "base/files/file.h"
 #include "base/files/file_enumerator.h"
@@ -28,10 +29,22 @@
 BASE_EXPORT std::optional<std::string> TranslateOpenFlagsToJavaMode(
     uint32_t open_flags);
 
-// Opens a content URI and returns the file descriptor to the caller.
+// Opens a content URI and returns the Java ParcelFileDescriptor to the caller
+// which will own the object.  Callers should call ContentUriGetFd() to get the
+// platform file descriptor, and call ContentUriClose() when the file is closed.
 // `open_flags` is a bitmap of File::FLAG_* values.
-// Returns -1 if the URI is invalid.
-int OpenContentUri(const FilePath& content_uri, uint32_t open_flags);
+// Returns null if the URI is invalid.
+base::android::ScopedJavaLocalRef<jobject> OpenContentUri(
+    const FilePath& content_uri,
+    uint32_t open_flags);
+
+// Returns the platform FD or -1 if `java_parcel_file_descriptor` is invalid.
+int ContentUriGetFd(
+    const base::android::JavaRef<jobject>& java_parcel_file_descriptor);
+
+// Closes `java_parcel_file_descriptor`.
+void ContentUriClose(
+    const base::android::JavaRef<jobject>& java_parcel_file_descriptor);
 
 // Returns true if file exists and results are populated, else returns false.
 bool ContentUriGetFileInfo(const FilePath& content_uri,
diff --git a/base/android/java/src/org/chromium/base/BaseFeatureList.java b/base/android/java/src/org/chromium/base/BaseFeatureList.java
new file mode 100644
index 0000000..e1750dbbb
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/BaseFeatureList.java
@@ -0,0 +1,22 @@
+// 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.base;
+
+import org.chromium.build.annotations.NullMarked;
+
+/** Convenience static methods to access {@link BaseFeatureMap}. */
+@NullMarked
+public class BaseFeatureList {
+    private BaseFeatureList() {}
+
+    public static final MutableFlagWithSafeDefault sUseSharedRebindServiceConnection =
+            new MutableFlagWithSafeDefault(
+                    BaseFeatureMap.getInstance(),
+                    BaseFeatures.USE_SHARED_REBIND_SERVICE_CONNECTION,
+                    false);
+
+    public static final MutableIntParamWithSafeDefault sMaxDeferredSharedRebindServiceConnection =
+            sUseSharedRebindServiceConnection.newIntParam("max-deferred-bindings", 0);
+}
diff --git a/base/android/java/src/org/chromium/base/ContentUriUtils.java b/base/android/java/src/org/chromium/base/ContentUriUtils.java
index ed630ed..630a4b42 100644
--- a/base/android/java/src/org/chromium/base/ContentUriUtils.java
+++ b/base/android/java/src/org/chromium/base/ContentUriUtils.java
@@ -12,6 +12,7 @@
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.Build;
+import android.os.ParcelFileDescriptor;
 import android.provider.DocumentsContract;
 import android.provider.MediaStore;
 import android.text.TextUtils;
@@ -55,13 +56,20 @@
      * @return file descriptor upon success, or -1 otherwise.
      */
     @CalledByNative
-    public static int openContentUri(
+    public static @Nullable ParcelFileDescriptor openContentUri(
             @JniType("std::string") String uriString, @JniType("std::string") String mode) {
         AssetFileDescriptor afd = getAssetFileDescriptor(uriString, mode);
-        if (afd != null) {
-            return afd.getParcelFileDescriptor().detachFd();
-        }
-        return -1;
+        return afd != null ? afd.getParcelFileDescriptor() : null;
+    }
+
+    @CalledByNative
+    private static int getFd(ParcelFileDescriptor parcelFileDescriptor) {
+        return parcelFileDescriptor.getFd();
+    }
+
+    @CalledByNative
+    private static void close(ParcelFileDescriptor parcelFileDescriptor) {
+        StreamUtil.closeQuietly(parcelFileDescriptor);
     }
 
     /**
diff --git a/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java b/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
index af32e358f..a8f0dd8 100644
--- a/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
+++ b/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
@@ -364,13 +364,15 @@
         /**
          * Optionally puts the RELRO section information so that it can be memory-mapped in another
          * process reading the bundle.
+         *
          * @param bundle Where to serialize.
          */
-        public void putSharedRelrosToBundle(Bundle bundle) {
+        public @Nullable Bundle getSharedRelrosBundle() {
             assert mInitDone;
             if (useChromiumLinker()) {
-                getLinker().putSharedRelrosToBundle(bundle);
+                return getLinker().getSharedRelrosBundle();
             }
+            return null;
         }
 
         private String creationAsString() {
diff --git a/base/android/java/src/org/chromium/base/library_loader/Linker.java b/base/android/java/src/org/chromium/base/library_loader/Linker.java
index 7be4710b..076ba81 100644
--- a/base/android/java/src/org/chromium/base/library_loader/Linker.java
+++ b/base/android/java/src/org/chromium/base/library_loader/Linker.java
@@ -67,7 +67,7 @@
  *   initialization runs implicitly as part of loading the library. In this case the behaviour is of
  *   a producer.
  *
- * - After loading the native library as a RELRO producer, the putSharedRelrosToBundle() becomes
+ * - After loading the native library as a RELRO producer, the getSharedRelroBundle() becomes
  *   available to then send the Bundle to Linkers in other processes, consumed
  *   by takeSharedRelrosFromBundle().
  */
@@ -81,10 +81,6 @@
     // Constant guarding debug logging.
     private static final boolean DEBUG = LibraryLoader.DEBUG;
 
-    // Constants used to pass the shared RELRO Bundle through Binder.
-    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
-    static final String SHARED_RELROS = "org.chromium.base.android.linker.shared_relros";
-
     private static final String BASE_LOAD_ADDRESS =
             "org.chromium.base.android.linker.base_load_address";
 
@@ -405,39 +401,40 @@
 
     /**
      * Serializes information about the RELRO region to be passed to a Linker in another process.
-     * @param bundle The Bundle to serialize to.
+     *
+     * @param args The IChildProcessArgs to serialize to.
      */
-    void putSharedRelrosToBundle(Bundle bundle) {
+    @Nullable Bundle getSharedRelrosBundle() {
         Bundle relros = null;
         synchronized (mLock) {
-            if (DEBUG) Log.i(TAG, "putSharedRelrosToBundle: state=%d", mState);
+            if (DEBUG) Log.i(TAG, "getSharedRelrosBundle: state=%d", mState);
             if (mState == State.DONE_PROVIDE_RELRO) {
                 assert mRelroProducer;
                 relros = assumeNonNull(mLocalLibInfo).toBundle();
             }
-            bundle.putBundle(SHARED_RELROS, relros);
             if (DEBUG && relros != null) {
                 assert mLocalLibInfo != null;
                 Log.i(
                         TAG,
-                        "putSharedRelrosToBundle() puts mLoadAddress=0x%x, mLoadSize=%d, "
+                        "getSharedRelrosBundle() puts mLoadAddress=0x%x, mLoadSize=%d, "
                                 + "mRelroFd=%d",
                         mLocalLibInfo.mLoadAddress,
                         mLocalLibInfo.mLoadSize,
                         mLocalLibInfo.mRelroFd);
             }
         }
+        return relros;
     }
 
     /**
-     * Deserializes the RELRO region information that was marshalled by
-     * {@link #putLoadAddressToBundle(Bundle)} and wakes up the threads waiting for it to replace
-     * the RELRO section in this process with shared memory.
+     * Deserializes the RELRO region information that was marshalled by {@link
+     * #putLoadAddressToBundle(Bundle)} and wakes up the threads waiting for it to replace the RELRO
+     * section in this process with shared memory.
+     *
      * @param bundle The Bundle to extract the information from.
      */
-    void takeSharedRelrosFromBundle(Bundle bundle) {
-        if (DEBUG) Log.i(TAG, "called takeSharedRelrosFromBundle(%s)", bundle);
-        Bundle relros = bundle.getBundle(SHARED_RELROS);
+    void takeSharedRelrosFromBundle(Bundle relros) {
+        if (DEBUG) Log.i(TAG, "called takeSharedRelrosFromBundle(%s)", relros);
         if (relros == null) return;
         LibInfo newRemote = LibInfo.fromBundle(relros);
         if (newRemote == null) return;
diff --git a/base/android/java/src/org/chromium/base/process_launcher/ChildProcessConnection.java b/base/android/java/src/org/chromium/base/process_launcher/ChildProcessConnection.java
index 0c29acd5..a1f07e5 100644
--- a/base/android/java/src/org/chromium/base/process_launcher/ChildProcessConnection.java
+++ b/base/android/java/src/org/chromium/base/process_launcher/ChildProcessConnection.java
@@ -21,6 +21,7 @@
 import androidx.annotation.IntDef;
 import androidx.annotation.VisibleForTesting;
 
+import org.chromium.base.BaseFeatureList;
 import org.chromium.base.BaseFeatureMap;
 import org.chromium.base.BaseFeatures;
 import org.chromium.base.BuildInfo;
@@ -185,6 +186,7 @@
     // Cache BackgroundNotPerceptibleBinding feature flag value.
     private static @Nullable Boolean sUseBackgroundNotPerceptibleBinding;
 
+    private static @Nullable RebindServiceConnection sRebindServiceConnection;
     // Lock to protect all the fields that can be accessed outside launcher thread.
     private final Object mBindingStateLock = new Object();
 
@@ -192,6 +194,7 @@
     private final Executor mLauncherExecutor;
     private ComponentName mServiceName;
     private final @Nullable ComponentName mFallbackServiceName;
+    private @Nullable Intent mBindIntent;
 
     // Parameters passed to the child process through the service binding intent.
     // If the service gets recreated by the framework the intent will be reused, so these parameters
@@ -203,15 +206,15 @@
     private final boolean mBindToCaller;
 
     private static class ConnectionParams {
-        final Bundle mConnectionBundle;
+        final IChildProcessArgs mChildProcessArgs;
         final @Nullable List<IBinder> mClientInterfaces;
         final @Nullable IBinder mBinderBox;
 
         ConnectionParams(
-                Bundle connectionBundle,
+                IChildProcessArgs childProcessArgs,
                 @Nullable List<IBinder> clientInterfaces,
                 @Nullable IBinder binderBox) {
-            mConnectionBundle = connectionBundle;
+            mChildProcessArgs = childProcessArgs;
             mClientInterfaces = clientInterfaces;
             mBinderBox = binderBox;
         }
@@ -281,8 +284,8 @@
     // inconvenient to log some histogram where this information is available.
     private final boolean mIsSandboxedForHistograms;
 
-    // Use Context.BIND_EXTERNAL_SERVICE flag for this service.
-    private final boolean mBindAsExternalService;
+    // The service binding flags for the default binding (i.e. visible binding).
+    private final int mDefaultBindFlags;
 
     // Strong binding will make the service priority equal to the priority of the activity.
     private ChildServiceConnection mStrongBinding;
@@ -389,7 +392,11 @@
         mIsSandboxedForHistograms = isSandboxedForHistograms;
         // Incremental install does not work with isolatedProcess, and externalService requires
         // isolatedProcess, so both need to be turned off for incremental install.
-        mBindAsExternalService = bindAsExternalService && !BuildConfig.IS_INCREMENTAL_INSTALL;
+        mDefaultBindFlags =
+                Context.BIND_AUTO_CREATE
+                        | ((bindAsExternalService && !BuildConfig.IS_INCREMENTAL_INSTALL)
+                                ? Context.BIND_EXTERNAL_SERVICE
+                                : 0);
         if (connectionFactory == null) {
             mConnectionFactory =
                     new ChildServiceConnectionFactory() {
@@ -443,39 +450,35 @@
     }
 
     private void createBindings(ComponentName serviceName) {
-        Intent intent = new Intent();
-        intent.setComponent(serviceName);
+        mBindIntent = new Intent();
+        mBindIntent.setComponent(serviceName);
         if (mServiceBundle != null) {
-            intent.putExtras(mServiceBundle);
+            mBindIntent.putExtras(mServiceBundle);
         }
 
-        int defaultFlags =
-                Context.BIND_AUTO_CREATE
-                        | (mBindAsExternalService ? Context.BIND_EXTERNAL_SERVICE : 0);
-
         mVisibleBinding =
                 mConnectionFactory.createConnection(
-                        intent, defaultFlags, mConnectionDelegate, mInstanceName);
+                        mBindIntent, mDefaultBindFlags, mConnectionDelegate, mInstanceName);
         if (supportNotPerceptibleBinding()) {
-            int flags = defaultFlags | Context.BIND_NOT_PERCEPTIBLE;
+            int flags = mDefaultBindFlags | Context.BIND_NOT_PERCEPTIBLE;
             if (useBackgroundNotPerceptibleBinding()) {
                 flags |= Context.BIND_NOT_FOREGROUND;
             }
             mNotPerceptibleBinding =
                     mConnectionFactory.createConnection(
-                            intent, flags, mConnectionDelegate, mInstanceName);
+                            mBindIntent, flags, mConnectionDelegate, mInstanceName);
         }
 
         mStrongBinding =
                 mConnectionFactory.createConnection(
-                        intent,
-                        defaultFlags | Context.BIND_IMPORTANT,
+                        mBindIntent,
+                        mDefaultBindFlags | Context.BIND_IMPORTANT,
                         mConnectionDelegate,
                         mInstanceName);
         mWaivedBinding =
                 mConnectionFactory.createConnection(
-                        intent,
-                        defaultFlags | Context.BIND_WAIVE_PRIORITY,
+                        mBindIntent,
+                        mDefaultBindFlags | Context.BIND_WAIVE_PRIORITY,
                         mConnectionDelegate,
                         mInstanceName);
     }
@@ -569,14 +572,26 @@
         assert isRunningOnLauncherThread();
         if (!isConnected()) return;
         assert mWaivedBinding.isBound();
-        mWaivedBinding.bindServiceConnection();
+        if (BaseFeatureList.sUseSharedRebindServiceConnection.isEnabled()) {
+            if (sRebindServiceConnection == null) {
+                sRebindServiceConnection =
+                        new RebindServiceConnection(
+                                BaseFeatureList.sMaxDeferredSharedRebindServiceConnection
+                                        .getValue());
+            }
+            assert mBindIntent != null;
+            sRebindServiceConnection.rebind(
+                    mBindIntent, mDefaultBindFlags | Context.BIND_WAIVE_PRIORITY, mInstanceName);
+        } else {
+            mWaivedBinding.bindServiceConnection();
+        }
     }
 
     /**
      * Sets-up the connection after it was started with start().
      *
-     * @param connectionBundle a bundle passed to the service that can be used to pass various
-     *     parameters to the service
+     * @param childProcessArgs an aidl interface with all miscellaneous parameters for the child
+     *     process connection.
      * @param clientInterfaces optional client specified interfaces that the child can use to
      *     communicate with the parent process
      * @param binderBox optional binder box the child can use to unpack additional binders
@@ -585,7 +600,7 @@
      * @param zygoteInfoCallback will be called exactly once after the connection is set up
      */
     public void setupConnection(
-            Bundle connectionBundle,
+            IChildProcessArgs childProcessArgs,
             @Nullable List<IBinder> clientInterfaces,
             @Nullable IBinder binderBox,
             ConnectionCallback connectionCallback,
@@ -600,7 +615,7 @@
         try (TraceEvent te = TraceEvent.scoped("ChildProcessConnection.setupConnection")) {
             mConnectionCallback = connectionCallback;
             mZygoteInfoCallback = zygoteInfoCallback;
-            mConnectionParams = new ConnectionParams(connectionBundle, clientInterfaces, binderBox);
+            mConnectionParams = new ConnectionParams(childProcessArgs, clientInterfaces, binderBox);
             // Run the setup if the service is already connected. If not, doConnectionSetup() will
             // be called from onServiceConnected().
             if (mServiceConnectComplete) {
@@ -882,7 +897,7 @@
                     };
             try {
                 mService.setupConnection(
-                        mConnectionParams.mConnectionBundle,
+                        mConnectionParams.mChildProcessArgs,
                         parentProcess,
                         mConnectionParams.mClientInterfaces,
                         mConnectionParams.mBinderBox);
@@ -1015,6 +1030,8 @@
         if (mNotPerceptibleBinding != null) {
             mNotPerceptibleBinding.retire();
         }
+        // We must clear shared waived binding when we unbind a waived binding.
+        clearSharedWaivedBinding();
         mWaivedBinding.retire();
         createBindings(mFallbackServiceName);
     }
@@ -1026,6 +1043,8 @@
         mConnectionParams = null;
         mUnbound = true;
         mStrongBinding.unbindServiceConnection();
+        // We must clear shared waived binding when we unbind a waived binding.
+        clearSharedWaivedBinding();
         mWaivedBinding.unbindServiceConnection();
         if (mNotPerceptibleBinding != null) {
             mNotPerceptibleBinding.unbindServiceConnection();
@@ -1046,16 +1065,22 @@
         }
     }
 
-    public void updateGroupImportance(int group, int importanceInGroup) {
+    private void clearSharedWaivedBinding() {
+        assert isRunningOnLauncherThread();
+        if (sRebindServiceConnection != null) {
+            sRebindServiceConnection.unbind();
+        }
+    }
+
+    public boolean updateGroupImportance(int group, int importanceInGroup) {
         assert isRunningOnLauncherThread();
         assert group != 0 || importanceInGroup == 0;
         if (mGroup != group || mImportanceInGroup != importanceInGroup) {
             mGroup = group;
             mImportanceInGroup = importanceInGroup;
-            if (isConnected()) {
-                mWaivedBinding.updateGroupImportance(group, importanceInGroup);
-            }
+            return isConnected() && mWaivedBinding.updateGroupImportance(group, importanceInGroup);
         }
+        return false;
     }
 
     public int getGroup() {
diff --git a/base/android/java/src/org/chromium/base/process_launcher/ChildProcessConstants.java b/base/android/java/src/org/chromium/base/process_launcher/ChildProcessConstants.java
index 8cc6e3c..7f150e0 100644
--- a/base/android/java/src/org/chromium/base/process_launcher/ChildProcessConstants.java
+++ b/base/android/java/src/org/chromium/base/process_launcher/ChildProcessConstants.java
@@ -16,16 +16,6 @@
     public static final String EXTRA_BIND_TO_CALLER =
             "org.chromium.base.process_launcher.extra.bind_to_caller";
 
-    // Below are the names for the items placed in the Bundle passed in the
-    // IChildProcessService.setupConnection call, once the connection has been established.
-
-    // Key for the command line.
-    public static final String EXTRA_COMMAND_LINE =
-            "org.chromium.base.process_launcher.extra.command_line";
-
-    // Key for the file descriptors that should be mapped in the child process.
-    public static final String EXTRA_FILES = "org.chromium.base.process_launcher.extra.extraFiles";
-
     // Key for the browser package name.
     public static final String EXTRA_BROWSER_PACKAGE_NAME =
             "org.chromium.base.process_launcher.extra.browser_package_name";
diff --git a/base/android/java/src/org/chromium/base/process_launcher/ChildProcessLauncher.java b/base/android/java/src/org/chromium/base/process_launcher/ChildProcessLauncher.java
index 6327a9c..73c889e 100644
--- a/base/android/java/src/org/chromium/base/process_launcher/ChildProcessLauncher.java
+++ b/base/android/java/src/org/chromium/base/process_launcher/ChildProcessLauncher.java
@@ -57,14 +57,16 @@
 
         /**
          * Called before setup is called on the connection.
-         * @param connectionBundle the bundle passed to the {@link ChildProcessService} in the
-         * setup call. Clients can add their own extras to the bundle.
+         *
+         * @param childProcessArgs the aidl parcelable passed to the {@link ChildProcessService} in
+         *     the setup call.
          */
-        public void onBeforeConnectionSetup(Bundle connectionBundle) {}
+        public void onBeforeConnectionSetup(IChildProcessArgs childProcessArgs) {}
 
         /**
          * Called when the connection was successfully established, meaning the setup call on the
          * service was successful.
+         *
          * @param connection the connection over which the setup call was made.
          */
         public void onConnectionEstablished(ChildProcessConnection connection) {}
@@ -95,7 +97,7 @@
     private final Delegate mDelegate;
 
     private final String[] mCommandLine;
-    private final FileDescriptorInfo[] mFilesToBeMapped;
+    private final IFileDescriptorInfo[] mFilesToBeMapped;
 
     // The allocator used to create the connection.
     private final ChildConnectionAllocator mConnectionAllocator;
@@ -126,7 +128,7 @@
             Handler launcherHandler,
             Delegate delegate,
             String[] commandLine,
-            FileDescriptorInfo[] filesToBeMapped,
+            IFileDescriptorInfo[] filesToBeMapped,
             ChildConnectionAllocator connectionAllocator,
             @Nullable List<IBinder> clientInterfaces,
             @Nullable IBinder binderBox) {
@@ -258,10 +260,10 @@
                         onServiceConnected(connection);
                     }
                 };
-        Bundle connectionBundle = createConnectionBundle();
-        mDelegate.onBeforeConnectionSetup(connectionBundle);
+        IChildProcessArgs connectionArgs = createConnectionArgs();
+        mDelegate.onBeforeConnectionSetup(connectionArgs);
         mConnection.setupConnection(
-                connectionBundle,
+                connectionArgs,
                 getClientInterfaces(),
                 getBinderBox(),
                 connectionCallback,
@@ -280,7 +282,7 @@
 
         // Proactively close the FDs rather than waiting for the GC to do it.
         try {
-            for (FileDescriptorInfo fileInfo : mFilesToBeMapped) {
+            for (IFileDescriptorInfo fileInfo : mFilesToBeMapped) {
                 fileInfo.fd.close();
             }
         } catch (IOException ioe) {
@@ -306,11 +308,11 @@
         return mLauncherHandler.getLooper() == Looper.myLooper();
     }
 
-    private Bundle createConnectionBundle() {
-        Bundle bundle = new Bundle();
-        bundle.putStringArray(ChildProcessConstants.EXTRA_COMMAND_LINE, mCommandLine);
-        bundle.putParcelableArray(ChildProcessConstants.EXTRA_FILES, mFilesToBeMapped);
-        return bundle;
+    private IChildProcessArgs createConnectionArgs() {
+        IChildProcessArgs args = new IChildProcessArgs();
+        args.commandLine = mCommandLine;
+        args.fileDescriptorInfos = mFilesToBeMapped;
+        return args;
     }
 
     private void onChildProcessDied() {
diff --git a/base/android/java/src/org/chromium/base/process_launcher/ChildProcessService.java b/base/android/java/src/org/chromium/base/process_launcher/ChildProcessService.java
index 1862aed..d4c5f63 100644
--- a/base/android/java/src/org/chromium/base/process_launcher/ChildProcessService.java
+++ b/base/android/java/src/org/chromium/base/process_launcher/ChildProcessService.java
@@ -16,7 +16,6 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
-import android.os.Parcelable;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
@@ -102,7 +101,7 @@
     private String @Nullable [] mCommandLineParams;
 
     // File descriptors that should be registered natively.
-    private FileDescriptorInfo @Nullable [] mFdInfos;
+    private IFileDescriptorInfo @Nullable [] mFdInfos;
 
     @GuardedBy("mLibraryInitializedLock")
     private boolean mLibraryInitialized;
@@ -160,7 +159,7 @@
 
                 @Override
                 public void setupConnection(
-                        Bundle args,
+                        IChildProcessArgs args,
                         IParentProcess parentProcess,
                         List<IBinder> callbacks,
                         IBinder binderBox)
@@ -186,8 +185,7 @@
                         m.initInChildProcess();
                         // In a number of cases the app zygote decides not to produce a RELRO FD.
                         // The bundle will tell the receiver to silently ignore it.
-                        relroBundle = new Bundle();
-                        m.putSharedRelrosToBundle(relroBundle);
+                        relroBundle = m.getSharedRelrosBundle();
                     }
                     // After finishSetupConnection() the parent process will stop accepting
                     // |relroBundle| from this process to ensure that another FD to shared memory
@@ -332,7 +330,7 @@
             long[] regionOffsets = new long[mFdInfos.length];
             long[] regionSizes = new long[mFdInfos.length];
             for (int i = 0; i < mFdInfos.length; i++) {
-                FileDescriptorInfo fdInfo = mFdInfos[i];
+                IFileDescriptorInfo fdInfo = mFdInfos[i];
                 String key = idsToKeys != null ? idsToKeys.get(fdInfo.id) : null;
                 if (key != null) {
                     keys[i] = key;
@@ -426,27 +424,19 @@
     }
 
     private void processConnectionBundle(
-            Bundle bundle, List<IBinder> clientInterfaces, IBinder binderBox) {
-        // Required to unparcel FileDescriptorInfo.
-        ClassLoader classLoader = getApplicationContext().getClassLoader();
-        bundle.setClassLoader(classLoader);
+            IChildProcessArgs args, List<IBinder> clientInterfaces, IBinder binderBox) {
         synchronized (mMainThread) {
             if (mCommandLineParams == null) {
-                mCommandLineParams =
-                        bundle.getStringArray(ChildProcessConstants.EXTRA_COMMAND_LINE);
+                mCommandLineParams = args.commandLine;
                 mMainThread.notifyAll();
             }
             // We must have received the command line by now
             assert mCommandLineParams != null;
-            Parcelable[] fdInfosAsParcelable =
-                    bundle.getParcelableArray(ChildProcessConstants.EXTRA_FILES);
-            if (fdInfosAsParcelable != null) {
-                // For why this arraycopy is necessary:
-                // http://stackoverflow.com/questions/8745893/i-dont-get-why-this-classcastexception-occurs
-                mFdInfos = new FileDescriptorInfo[fdInfosAsParcelable.length];
-                System.arraycopy(fdInfosAsParcelable, 0, mFdInfos, 0, fdInfosAsParcelable.length);
+            IFileDescriptorInfo[] fdInfos = args.fileDescriptorInfos;
+            if (fdInfos != null) {
+                mFdInfos = fdInfos;
             }
-            mDelegate.onConnectionSetup(bundle, clientInterfaces, binderBox);
+            mDelegate.onConnectionSetup(args, clientInterfaces, binderBox);
             mMainThread.notifyAll();
         }
     }
diff --git a/base/android/java/src/org/chromium/base/process_launcher/ChildProcessServiceDelegate.java b/base/android/java/src/org/chromium/base/process_launcher/ChildProcessServiceDelegate.java
index 2bb8eed6..00069eba 100644
--- a/base/android/java/src/org/chromium/base/process_launcher/ChildProcessServiceDelegate.java
+++ b/base/android/java/src/org/chromium/base/process_launcher/ChildProcessServiceDelegate.java
@@ -34,7 +34,7 @@
      * @param binderBox an optional binder box which may contain other binders to be unpacked
      */
     void onConnectionSetup(
-            Bundle connectionBundle, List<IBinder> clientInterfaces, IBinder binderBox);
+            IChildProcessArgs connectionArgs, List<IBinder> clientInterfaces, IBinder binderBox);
 
     /**
      * Called when the delegate should load the native library.
diff --git a/base/android/java/src/org/chromium/base/process_launcher/ChildServiceConnection.java b/base/android/java/src/org/chromium/base/process_launcher/ChildServiceConnection.java
index 0b4a281..aca11232 100644
--- a/base/android/java/src/org/chromium/base/process_launcher/ChildServiceConnection.java
+++ b/base/android/java/src/org/chromium/base/process_launcher/ChildServiceConnection.java
@@ -15,7 +15,14 @@
 
     boolean isBound();
 
-    void updateGroupImportance(int group, int importanceInGroup);
+    /**
+     * Calls `Context.updateServiceGroup()` if possible.
+     *
+     * <p>Returns `true` if the call succeeds.
+     *
+     * <p>Note that we need to rebind a service binding for the process to apply the change of this.
+     */
+    boolean updateGroupImportance(int group, int importanceInGroup);
 
     void retire();
 }
diff --git a/base/android/java/src/org/chromium/base/process_launcher/ChildServiceConnectionImpl.java b/base/android/java/src/org/chromium/base/process_launcher/ChildServiceConnectionImpl.java
index a3576825..73375d08 100644
--- a/base/android/java/src/org/chromium/base/process_launcher/ChildServiceConnectionImpl.java
+++ b/base/android/java/src/org/chromium/base/process_launcher/ChildServiceConnectionImpl.java
@@ -83,7 +83,7 @@
     }
 
     @Override
-    public void updateGroupImportance(int group, int importanceInGroup) {
+    public boolean updateGroupImportance(int group, int importanceInGroup) {
         // ChildProcessConnection checks there is a real connection to the service before calling
         // this, and this `isBound` check should in theory be unnecessary. However this is still
         // tripped on some devices where another service connection bound successfully but this
@@ -91,20 +91,19 @@
         // behavior and is not handled. However, avoid crashing in `updateServiceGroup` by doing
         // this check here.
         if (!isBound()) {
-            return;
+            return false;
         }
         if (BindService.supportVariableConnections()) {
             try {
                 mContext.updateServiceGroup(this, group, importanceInGroup);
+                return true;
             } catch (IllegalArgumentException e) {
                 // There is an unavoidable race here binding might be removed for example due to a
                 // crash, which has not been processed on the launcher thread.
                 // Ignore these. See crbug.com/1026626 and crbug.com/1026626 for context.
-                return;
             }
-            BindService.doBindService(
-                    mContext, mBindIntent, this, mBindFlags, mHandler, mExecutor, mInstanceName);
         }
+        return false;
     }
 
     @Override
diff --git a/base/android/java/src/org/chromium/base/process_launcher/FileDescriptorInfo.aidl b/base/android/java/src/org/chromium/base/process_launcher/FileDescriptorInfo.aidl
deleted file mode 100644
index 033b792..0000000
--- a/base/android/java/src/org/chromium/base/process_launcher/FileDescriptorInfo.aidl
+++ /dev/null
@@ -1,7 +0,0 @@
-// Copyright 2016 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.base.process_launcher;
-
-parcelable FileDescriptorInfo;
diff --git a/base/android/java/src/org/chromium/base/process_launcher/FileDescriptorInfo.java b/base/android/java/src/org/chromium/base/process_launcher/FileDescriptorInfo.java
deleted file mode 100644
index 5ecf70c..0000000
--- a/base/android/java/src/org/chromium/base/process_launcher/FileDescriptorInfo.java
+++ /dev/null
@@ -1,70 +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.
-
-package org.chromium.base.process_launcher;
-
-import android.os.Parcel;
-import android.os.ParcelFileDescriptor;
-import android.os.Parcelable;
-
-import org.chromium.build.annotations.NullMarked;
-import org.chromium.build.annotations.UsedByReflection;
-
-import javax.annotation.concurrent.Immutable;
-
-/**
- * Parcelable class that contains file descriptor and file region information to be passed to child
- * processes.
- */
-@Immutable
-@UsedByReflection("child_process_launcher_helper_android.cc")
-@NullMarked
-public final class FileDescriptorInfo implements Parcelable {
-    public final int id;
-    public final ParcelFileDescriptor fd;
-    public final long offset;
-    public final long size;
-
-    public FileDescriptorInfo(int id, ParcelFileDescriptor fd, long offset, long size) {
-        this.id = id;
-        this.fd = fd;
-        this.offset = offset;
-        this.size = size;
-    }
-
-    FileDescriptorInfo(Parcel in) {
-        id = in.readInt();
-        ParcelFileDescriptor gotFd = in.readParcelable(ParcelFileDescriptor.class.getClassLoader());
-        assert gotFd != null;
-        fd = gotFd;
-        offset = in.readLong();
-        size = in.readLong();
-    }
-
-    @Override
-    public int describeContents() {
-        return CONTENTS_FILE_DESCRIPTOR;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(id);
-        dest.writeParcelable(fd, CONTENTS_FILE_DESCRIPTOR);
-        dest.writeLong(offset);
-        dest.writeLong(size);
-    }
-
-    public static final Parcelable.Creator<FileDescriptorInfo> CREATOR =
-            new Parcelable.Creator<FileDescriptorInfo>() {
-                @Override
-                public FileDescriptorInfo createFromParcel(Parcel in) {
-                    return new FileDescriptorInfo(in);
-                }
-
-                @Override
-                public FileDescriptorInfo[] newArray(int size) {
-                    return new FileDescriptorInfo[size];
-                }
-            };
-}
diff --git a/base/android/java/src/org/chromium/base/process_launcher/IChildProcessArgs.aidl b/base/android/java/src/org/chromium/base/process_launcher/IChildProcessArgs.aidl
new file mode 100644
index 0000000..0e56472
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/process_launcher/IChildProcessArgs.aidl
@@ -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.
+
+package org.chromium.base.process_launcher;
+
+import org.chromium.base.process_launcher.IFileDescriptorInfo;
+
+parcelable IChildProcessArgs {
+  int cpuCount;
+  long cpuFeatures;
+  String[] commandLine;
+  IFileDescriptorInfo[] fileDescriptorInfos;
+  // TODO(crbug.com/414609682): Convert this to something which is compatible with NDK aidl.
+  Bundle relroBundle;
+}
diff --git a/base/android/java/src/org/chromium/base/process_launcher/IChildProcessService.aidl b/base/android/java/src/org/chromium/base/process_launcher/IChildProcessService.aidl
index 02dddd10..b57f7e28 100644
--- a/base/android/java/src/org/chromium/base/process_launcher/IChildProcessService.aidl
+++ b/base/android/java/src/org/chromium/base/process_launcher/IChildProcessService.aidl
@@ -4,9 +4,9 @@
 
 package org.chromium.base.process_launcher;
 
-import android.content.pm.ApplicationInfo;
-import android.os.Bundle;
+import android.os.PersistableBundle;
 
+import org.chromium.base.process_launcher.IChildProcessArgs;
 import org.chromium.base.process_launcher.IParentProcess;
 
 interface IChildProcessService {
@@ -22,8 +22,8 @@
   ApplicationInfo getAppInfo();
 
   // Sets up the initial IPC channel.
-  oneway void setupConnection(in Bundle args, IParentProcess parentProcess,
-          in List<IBinder> clientInterfaces, in IBinder binderBox);
+  oneway void setupConnection(in IChildProcessArgs args, in IParentProcess parentProcess,
+           in @nullable List<IBinder> clientInterfaces, in @nullable IBinder binderBox);
 
   // Forcefully kills the child process.
   oneway void forceKill();
diff --git a/base/android/java/src/org/chromium/base/process_launcher/IFileDescriptorInfo.aidl b/base/android/java/src/org/chromium/base/process_launcher/IFileDescriptorInfo.aidl
new file mode 100644
index 0000000..19b62999
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/process_launcher/IFileDescriptorInfo.aidl
@@ -0,0 +1,12 @@
+// 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.base.process_launcher;
+
+parcelable IFileDescriptorInfo {
+  int id;
+  ParcelFileDescriptor fd;
+  long offset;
+  long size;
+}
diff --git a/base/android/java/src/org/chromium/base/process_launcher/RebindServiceConnection.java b/base/android/java/src/org/chromium/base/process_launcher/RebindServiceConnection.java
new file mode 100644
index 0000000..71b21fa
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/process_launcher/RebindServiceConnection.java
@@ -0,0 +1,111 @@
+// 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.base.process_launcher;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.IBinder;
+
+import org.chromium.base.ContextUtils;
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
+
+import java.util.concurrent.Executor;
+
+/**
+ * RebindServiceConnection is to update a bound process in the LRU of the ProcessList of
+ * OomAdjuster.
+ *
+ * <p>`Context.updateServiceGroup()` requires application to rebind the process to apply the change.
+ * Also rebinding a process moves the process to the more recent position in the ProcessList even
+ * without `Context.updateServiceGroup()`.
+ *
+ * <p>This RebindServiceConnection is a workaround for 2 issues of rebinding ServiceConnection.
+ *
+ * <p>* Poorly differentiated concepts of a binding and a connection.
+ *
+ * <p>In the public API, a `ServiceConnection` uniquely identifies a binding. However, AMS has an
+ * additional `ConnectionRecord` concept, where for a given `ServiceConnection`, each
+ * `Context.bindService()` call creates is a new `ConnectionRecord`. This means that repeatedly
+ * re-binding with a given `ServiceConnection` will cause `ConnectionRecord` objects to accumulate
+ * in AMS, and there is a hard limit how many `ConnectionRecord`s a process can have. Unfortunately,
+ * the only way to free `ConnectionRecord` objects is to unbind the entire connection. This means
+ * it's necessary to use a dedicated `ServiceConnection` to trigger re-binding for
+ * `Context.updateServiceGroup()`, as the unbindings necessary to free `ConnectionRecord`s would
+ * interfere with the lifecycle management of a normal binding.
+ *
+ * <p>* Accumulation of un-GC'ed Binder proxies
+ *
+ * <p>`LoadedApk$ServiceDispatcher$InnerConnection` is created per new `Context` and
+ * `ServiceConnection` combination on `Context.bindService()` in the Android SDK. The inner
+ * connection represents a binder proxy and is GC-ed when GC happened on AMS. Though Binder Proxy is
+ * GC-ed eventually, if there are too many calls of pairs of `Context.bindService()` and
+ * `Context.unbindService()` in a short term even with the same `ServiceConnection`, Chrome can be
+ * killed due to too many Binder Proxy remaining.
+ *
+ * <p>To prevent the first issue, `ChildProcessConnection` uses this short living service connection
+ * `RebindServiceConnection` and `RebindServiceConnection.rebind()` unbinds itself just after
+ * binding it if maxDeferredConnections is zero.
+ *
+ * <p>If the second issue is problematic, RebindServiceConnection supports deferring unbinding so
+ * that it can be reused for multiple processes without increasing the Binder Proxy by setting
+ * maxDeferredConnections as non-zero. The `ConnectionRecord`s in AMS are cleared when the number of
+ * deferred connections reaches to maxDeferredConnections.
+ *
+ * <p>When `ChildProcessConnection` wants to kill the process (e.g. `unbind()`), keeping the
+ * deferred connections prevents the process from being freed by AMS. `ChildProcessConnection` have
+ * to call `RebindServiceConnection.unbind()` to clear all deferred connections when
+ * `ChildProcessConnection` unbinds the waived binding to avoid the leak.
+ */
+@NullMarked
+/* package */ final class RebindServiceConnection implements ServiceConnection {
+    private final int mMaxDeferredConnections;
+    private int mDeferredConnections;
+    private final Handler mHandler;
+    private final Executor mExecutor;
+
+    RebindServiceConnection(int maxDeferredConnections) {
+        mMaxDeferredConnections = maxDeferredConnections;
+        mHandler = new Handler();
+        mExecutor =
+                (Runnable runnable) -> {
+                    // We don't mind if the callback is executed on any thread because
+                    // RebindServiceConnection does nothing on its callbacks.
+                    runnable.run();
+                };
+    }
+
+    void rebind(Intent bindIntent, int bindFlags, @Nullable String instanceName) {
+        BindService.doBindService(
+                ContextUtils.getApplicationContext(),
+                bindIntent,
+                this,
+                bindFlags,
+                mHandler,
+                mExecutor,
+                instanceName);
+        if (mDeferredConnections >= mMaxDeferredConnections) {
+            ContextUtils.getApplicationContext().unbindService(this);
+            mDeferredConnections = 0;
+        } else {
+            mDeferredConnections += 1;
+        }
+    }
+
+    void unbind() {
+        if (mDeferredConnections > 0) {
+            ContextUtils.getApplicationContext().unbindService(this);
+            mDeferredConnections = 0;
+        }
+    }
+
+    @Override
+    public void onServiceConnected(ComponentName name, IBinder service) {}
+
+    @Override
+    public void onServiceDisconnected(ComponentName name) {}
+}
diff --git a/base/android/junit/src/org/chromium/base/library_loader/LinkerTest.java b/base/android/junit/src/org/chromium/base/library_loader/LinkerTest.java
index 92a21ea..f610302 100644
--- a/base/android/junit/src/org/chromium/base/library_loader/LinkerTest.java
+++ b/base/android/junit/src/org/chromium/base/library_loader/LinkerTest.java
@@ -164,14 +164,12 @@
         // Create the bundle following the _internal_ format of the Linker. Not great, but shorter
         // than factoring out this logic from the Linker only for testing.
         Bundle relros = libInfo.toBundle();
-        Bundle b = new Bundle();
-        b.putBundle(Linker.SHARED_RELROS, relros);
 
         // Exercise.
         linker.ensureInitialized(
                 /* asRelroProducer= */ false, PreferAddress.RESERVE_HINT, someAddress);
         linker.pretendLibraryIsLoadedForTesting();
-        linker.takeSharedRelrosFromBundle(b);
+        linker.takeSharedRelrosFromBundle(relros);
 
         // Verify.
         Assert.assertEquals(
diff --git a/base/android/junit/src/org/chromium/base/process_launcher/ChildProcessConnectionTest.java b/base/android/junit/src/org/chromium/base/process_launcher/ChildProcessConnectionTest.java
index 7b23aca4..614fe7f 100644
--- a/base/android/junit/src/org/chromium/base/process_launcher/ChildProcessConnectionTest.java
+++ b/base/android/junit/src/org/chromium/base/process_launcher/ChildProcessConnectionTest.java
@@ -82,9 +82,10 @@
         }
 
         @Override
-        public void updateGroupImportance(int group, int importanceInGroup) {
+        public boolean updateGroupImportance(int group, int importanceInGroup) {
             mGroup = group;
             mImportanceInGroup = importanceInGroup;
+            return true;
         }
 
         @Override
@@ -173,7 +174,7 @@
                         })
                 .when(mIChildProcessService)
                 .setupConnection(
-                        or(isNull(), any(Bundle.class)),
+                        or(isNull(), any()),
                         or(isNull(), any()),
                         or(isNull(), any()),
                         or(isNull(), any()));
@@ -380,7 +381,7 @@
         assertNotNull(mFirstServiceConnection);
         connection.start(/* useStrongBinding= */ false, /* serviceCallback= */ null);
         connection.setupConnection(
-                /* connectionBundle= */ null,
+                /* childProcessArgs= */ null,
                 /* clientInterfaces= */ null,
                 /* binderBox= */ null,
                 mConnectionCallback,
@@ -399,7 +400,7 @@
         assertNotNull(mFirstServiceConnection);
         connection.start(/* useStrongBinding= */ false, /* serviceCallback= */ null);
         connection.setupConnection(
-                /* connectionBundle= */ null,
+                /* childProcessArgs= */ null,
                 /* clientInterfaces= */ null,
                 /* binderBox= */ null,
                 mConnectionCallback,
@@ -421,7 +422,7 @@
         assertNotNull(mFirstServiceConnection);
         connection.start(/* useStrongBinding= */ false, /* serviceCallback= */ null);
         connection.setupConnection(
-                /* connectionBundle= */ null,
+                /* childProcessArgs= */ null,
                 /* clientInterfaces= */ null,
                 /* binderBox= */ null,
                 mConnectionCallback,
@@ -448,7 +449,7 @@
         assertNotNull(mFirstServiceConnection);
         connection1.start(/* useStrongBinding= */ true, /* serviceCallback= */ null);
         connection1.setupConnection(
-                /* connectionBundle= */ null,
+                /* childProcessArgs= */ null,
                 /* clientInterfaces= */ null,
                 /* binderBox= */ null,
                 mConnectionCallback,
@@ -472,7 +473,7 @@
         assertNotNull(mFirstServiceConnection);
         connection2.start(/* useStrongBinding= */ false, /* serviceCallback= */ null);
         connection2.setupConnection(
-                /* connectionBundle= */ null,
+                /* childProcessArgs= */ null,
                 /* clientInterfaces= */ null,
                 /* binderBox= */ null,
                 mConnectionCallback,
@@ -498,7 +499,7 @@
         assertNotNull(mFirstServiceConnection);
         connection.start(/* useStrongBinding= */ false, /* serviceCallback= */ null);
         connection.setupConnection(
-                /* connectionBundle= */ null,
+                /* childProcessArgs= */ null,
                 /* clientInterfaces= */ null,
                 /* binderBox= */ null,
                 mConnectionCallback,
@@ -529,7 +530,7 @@
         assertNotNull(mFirstServiceConnection);
         connection.start(/* useStrongBinding= */ false, /* serviceCallback= */ null);
         connection.setupConnection(
-                /* connectionBundle= */ null,
+                /* childProcessArgs= */ null,
                 /* clientInterfaces= */ null,
                 /* binderBox= */ null,
                 mConnectionCallback,
@@ -558,7 +559,7 @@
         connection.start(/* useStrongBinding= */ false, /* serviceCallback= */ null);
         mFirstServiceConnection.notifyServiceConnected(mChildProcessServiceBinder);
         connection.setupConnection(
-                /* connectionBundle= */ null,
+                /* childProcessArgs= */ null,
                 /* clientInterfaces= */ null,
                 /* binderBox= */ null,
                 mConnectionCallback,
@@ -577,7 +578,7 @@
         connection.start(/* useStrongBinding= */ false, /* serviceCallback= */ null);
         mFirstServiceConnection.notifyServiceConnected(mChildProcessServiceBinder);
         connection.setupConnection(
-                /* connectionBundle= */ null,
+                /* childProcessArgs= */ null,
                 /* clientInterfaces= */ null,
                 /* binderBox= */ null,
                 mConnectionCallback,
@@ -631,7 +632,7 @@
         connection.start(/* useStrongBinding= */ false, /* serviceCallback= */ null);
         mFirstServiceConnection.notifyServiceConnected(mChildProcessServiceBinder);
         connection.setupConnection(
-                /* connectionBundle= */ null,
+                /* childProcessArgs= */ null,
                 /* clientInterfaces= */ null,
                 /* binderBox= */ null,
                 mConnectionCallback,
@@ -682,7 +683,7 @@
         }
 
         connection.setupConnection(
-                /* connectionBundle= */ null,
+                /* childProcessArgs= */ null,
                 /* clientInterfaces= */ null,
                 /* binderBox= */ null,
                 mConnectionCallback,
diff --git a/base/base_export.h b/base/base_export.h
index 1df5354..dd4c591 100644
--- a/base/base_export.h
+++ b/base/base_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(BASE_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(BASE_IMPLEMENTATION)
 #define BASE_EXPORT __attribute__((visibility("default")))
-#else
-#define BASE_EXPORT
-#endif  // defined(BASE_IMPLEMENTATION)
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/base/component_export.h b/base/component_export.h
index 0c70e8d..649cf45 100644
--- a/base/component_export.h
+++ b/base/component_export.h
@@ -39,7 +39,7 @@
 #define COMPONENT_IMPORT_ANNOTATION __declspec(dllimport)
 #else  // defined(WIN32)
 #define COMPONENT_EXPORT_ANNOTATION __attribute__((visibility("default")))
-#define COMPONENT_IMPORT_ANNOTATION
+#define COMPONENT_IMPORT_ANNOTATION __attribute__((visibility("default")))
 #endif  // defined(WIN32)
 #else   // defined(COMPONENT_BUILD)
 #define COMPONENT_EXPORT_ANNOTATION
diff --git a/base/features.cc b/base/features.cc
index 79791ac..97ca3bd 100644
--- a/base/features.cc
+++ b/base/features.cc
@@ -118,6 +118,12 @@
 BASE_FEATURE(kPostGetMyMemoryStateToBackground,
              "PostGetMyMemoryStateToBackground",
              FEATURE_ENABLED_BY_DEFAULT);
+
+// Use shared service connection to rebind a service binding to update the LRU
+// in the ProcessList of OomAdjuster.
+BASE_FEATURE(kUseSharedRebindServiceConnection,
+             "UseSharedRebindServiceConnection",
+             FEATURE_DISABLED_BY_DEFAULT);
 #endif  // BUILDFLAG(IS_ANDROID)
 
 void Init(EmitThreadControllerProfilerMetadata
diff --git a/base/features.h b/base/features.h
index 0e4c48d..eb3fd68 100644
--- a/base/features.h
+++ b/base/features.h
@@ -37,6 +37,7 @@
 BASE_EXPORT BASE_DECLARE_FEATURE(
     kPostPowerMonitorBroadcastReceiverInitToBackground);
 BASE_EXPORT BASE_DECLARE_FEATURE(kPostGetMyMemoryStateToBackground);
+BASE_EXPORT BASE_DECLARE_FEATURE(kUseSharedRebindServiceConnection);
 #endif
 
 #if BUILDFLAG(ENABLE_MUTEX_PRIORITY_INHERITANCE)
diff --git a/base/files/file.cc b/base/files/file.cc
index 3847acd..e62d6d40 100644
--- a/base/files/file.cc
+++ b/base/files/file.cc
@@ -57,10 +57,15 @@
 
 File::File(File&& other)
     : file_(other.TakePlatformFile()),
+#if BUILDFLAG(IS_ANDROID)
+      java_parcel_file_descriptor_(
+          std::move(other.java_parcel_file_descriptor_)),
+#endif
       path_(other.path_),
       error_details_(other.error_details()),
       created_(other.created()),
-      async_(other.async_) {}
+      async_(other.async_) {
+}
 
 File::~File() {
   // Go through the AssertIOAllowed logic.
@@ -70,6 +75,9 @@
 File& File::operator=(File&& other) {
   Close();
   SetPlatformFile(other.TakePlatformFile());
+#if BUILDFLAG(IS_ANDROID)
+  java_parcel_file_descriptor_ = std::move(other.java_parcel_file_descriptor_);
+#endif
   path_ = other.path_;
   error_details_ = other.error_details();
   created_ = other.created();
diff --git a/base/files/file.h b/base/files/file.h
index e666c56..9ca6817 100644
--- a/base/files/file.h
+++ b/base/files/file.h
@@ -20,6 +20,10 @@
 #include "base/trace_event/base_tracing_forward.h"
 #include "build/build_config.h"
 
+#if BUILDFLAG(IS_ANDROID)
+#include "base/android/scoped_java_ref.h"
+#endif
+
 struct stat;
 
 namespace base {
@@ -426,6 +430,13 @@
 
   ScopedPlatformFile file_;
 
+#if BUILDFLAG(IS_ANDROID)
+  // Keeps the Java ParcelFileDescriptor alive when `this` wraps a file from an
+  // Android content provider (i.e. a content URI). Close() is called on the
+  // object when the file is closed.
+  base::android::ScopedJavaGlobalRef<jobject> java_parcel_file_descriptor_;
+#endif
+
   // Platform path to `file_`. Set if `this` wraps a file from an Android
   // content provider (i.e. a content URI) or if tracing is enabled in
   // `Initialize()`.
diff --git a/base/files/file_posix.cc b/base/files/file_posix.cc
index ba5e066..aa4fbd4 100644
--- a/base/files/file_posix.cc
+++ b/base/files/file_posix.cc
@@ -258,6 +258,11 @@
 
   SCOPED_FILE_TRACE("Close");
   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
+#if BUILDFLAG(IS_ANDROID)
+  if (java_parcel_file_descriptor_) {
+    internal::ContentUriClose(java_parcel_file_descriptor_);
+  }
+#endif
   file_.reset();
 }
 
@@ -633,7 +638,8 @@
 
 #if BUILDFLAG(IS_ANDROID)
   if (path.IsContentUri()) {
-    int fd = internal::OpenContentUri(path, flags);
+    java_parcel_file_descriptor_ = internal::OpenContentUri(path, flags);
+    int fd = internal::ContentUriGetFd(java_parcel_file_descriptor_);
     if (fd < 0) {
       error_details_ = FILE_ERROR_FAILED;
       return;
diff --git a/base/i18n/base_i18n_export.h b/base/i18n/base_i18n_export.h
index 72afb46c..ced66202 100644
--- a/base/i18n/base_i18n_export.h
+++ b/base/i18n/base_i18n_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(BASE_I18N_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(BASE_I18N_IMPLEMENTATION)
 #define BASE_I18N_EXPORT __attribute__((visibility("default")))
-#else
-#define BASE_I18N_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/base/test/android/java/src/org/chromium/base/ITestCallback.aidl b/base/test/android/java/src/org/chromium/base/ITestCallback.aidl
index e7936d1..a64c999 100644
--- a/base/test/android/java/src/org/chromium/base/ITestCallback.aidl
+++ b/base/test/android/java/src/org/chromium/base/ITestCallback.aidl
@@ -5,7 +5,6 @@
 package org.chromium.base;
 
 import org.chromium.base.ITestController;
-import org.chromium.base.process_launcher.FileDescriptorInfo;
 
 /**
  * This interface is called by the child process to pass its controller to its parent.
diff --git a/base/test/android/java/src/org/chromium/base/ITestController.aidl b/base/test/android/java/src/org/chromium/base/ITestController.aidl
index bdcae2e..3cb964f 100644
--- a/base/test/android/java/src/org/chromium/base/ITestController.aidl
+++ b/base/test/android/java/src/org/chromium/base/ITestController.aidl
@@ -4,8 +4,6 @@
 
 package org.chromium.base;
 
-import org.chromium.base.process_launcher.FileDescriptorInfo;
-
 /**
  * This interface is used to control child processes.
  */
diff --git a/base/test/android/java/src/org/chromium/base/MultiprocessTestClientLauncher.java b/base/test/android/java/src/org/chromium/base/MultiprocessTestClientLauncher.java
index 146efeb1..37d69ac 100644
--- a/base/test/android/java/src/org/chromium/base/MultiprocessTestClientLauncher.java
+++ b/base/test/android/java/src/org/chromium/base/MultiprocessTestClientLauncher.java
@@ -18,8 +18,8 @@
 import org.chromium.base.process_launcher.ChildConnectionAllocator;
 import org.chromium.base.process_launcher.ChildProcessConnection;
 import org.chromium.base.process_launcher.ChildProcessLauncher;
-import org.chromium.base.process_launcher.FileDescriptorInfo;
 import org.chromium.base.process_launcher.IChildProcessService;
+import org.chromium.base.process_launcher.IFileDescriptorInfo;
 
 import java.io.IOException;
 import java.util.Arrays;
@@ -135,7 +135,7 @@
     private Integer mMainReturnCode;
 
     private MultiprocessTestClientLauncher(
-            String[] commandLine, FileDescriptorInfo[] filesToMap, IBinder binderBox) {
+            String[] commandLine, IFileDescriptorInfo[] filesToMap, IBinder binderBox) {
         assert isRunningOnLauncherThread();
 
         if (sConnectionAllocator == null) {
@@ -218,7 +218,7 @@
     @CalledByNative
     private static int launchClient(
             final String[] commandLine,
-            final FileDescriptorInfo[] filesToMap,
+            final IFileDescriptorInfo[] filesToMap,
             final IBinder binderBox) {
         assert Looper.myLooper() != Looper.getMainLooper();
 
@@ -247,7 +247,7 @@
     }
 
     private static MultiprocessTestClientLauncher createAndStartLauncherOnLauncherThread(
-            String[] commandLine, FileDescriptorInfo[] filesToMap, final IBinder binderBox) {
+            String[] commandLine, IFileDescriptorInfo[] filesToMap, final IBinder binderBox) {
         assert isRunningOnLauncherThread();
 
         MultiprocessTestClientLauncher launcher =
@@ -351,10 +351,10 @@
 
     /** Does not take ownership of of fds. */
     @CalledByNative
-    private static FileDescriptorInfo[] makeFdInfoArray(int[] keys, int[] fds) {
-        FileDescriptorInfo[] fdInfos = new FileDescriptorInfo[keys.length];
+    private static IFileDescriptorInfo[] makeFdInfoArray(int[] keys, int[] fds) {
+        IFileDescriptorInfo[] fdInfos = new IFileDescriptorInfo[keys.length];
         for (int i = 0; i < keys.length; i++) {
-            FileDescriptorInfo fdInfo = makeFdInfo(keys[i], fds[i]);
+            IFileDescriptorInfo fdInfo = makeFdInfo(keys[i], fds[i]);
             if (fdInfo == null) {
                 Log.e(TAG, "Failed to make file descriptor (" + keys[i] + ", " + fds[i] + ").");
                 return null;
@@ -364,7 +364,7 @@
         return fdInfos;
     }
 
-    private static FileDescriptorInfo makeFdInfo(int id, int fd) {
+    private static IFileDescriptorInfo makeFdInfo(int id, int fd) {
         ParcelFileDescriptor parcelableFd = null;
         try {
             parcelableFd = ParcelFileDescriptor.fromFd(fd);
@@ -372,7 +372,10 @@
             Log.e(TAG, "Invalid FD provided for process connection, aborting connection.", e);
             return null;
         }
-        return new FileDescriptorInfo(id, parcelableFd, /* offset= */ 0, /* size= */ 0);
+        IFileDescriptorInfo fdInfo = new IFileDescriptorInfo();
+        fdInfo.id = id;
+        fdInfo.fd = parcelableFd;
+        return fdInfo;
     }
 
     private static boolean isRunningOnLauncherThread() {
diff --git a/base/test/android/java/src/org/chromium/base/MultiprocessTestClientServiceDelegate.java b/base/test/android/java/src/org/chromium/base/MultiprocessTestClientServiceDelegate.java
index cf902ec..554841ce0 100644
--- a/base/test/android/java/src/org/chromium/base/MultiprocessTestClientServiceDelegate.java
+++ b/base/test/android/java/src/org/chromium/base/MultiprocessTestClientServiceDelegate.java
@@ -13,6 +13,7 @@
 import org.chromium.base.library_loader.LibraryLoader;
 import org.chromium.base.library_loader.LibraryProcessType;
 import org.chromium.base.process_launcher.ChildProcessServiceDelegate;
+import org.chromium.base.process_launcher.IChildProcessArgs;
 import org.chromium.native_test.MainRunner;
 
 import java.util.List;
@@ -49,7 +50,7 @@
 
     @Override
     public void onConnectionSetup(
-            Bundle connectionBundle, List<IBinder> callbacks, IBinder binderBox) {
+            IChildProcessArgs args, List<IBinder> callbacks, IBinder binderBox) {
         mTestCallback = ITestCallback.Stub.asInterface(callbacks.get(0));
         mBinderBox = binderBox;
     }
diff --git a/base/test/android/javatests/src/org/chromium/base/process_launcher/TestChildProcessConnection.java b/base/test/android/javatests/src/org/chromium/base/process_launcher/TestChildProcessConnection.java
index 376ff6f..9fe0b962 100644
--- a/base/test/android/javatests/src/org/chromium/base/process_launcher/TestChildProcessConnection.java
+++ b/base/test/android/javatests/src/org/chromium/base/process_launcher/TestChildProcessConnection.java
@@ -30,7 +30,9 @@
         }
 
         @Override
-        public void updateGroupImportance(int group, int importanceInGroup) {}
+        public boolean updateGroupImportance(int group, int importanceInGroup) {
+            return true;
+        }
 
         @Override
         public void retire() {}
diff --git a/base/test/android/javatests/src/org/chromium/base/test/transit/ActivityElement.java b/base/test/android/javatests/src/org/chromium/base/test/transit/ActivityElement.java
index 52f3af6..60154a5 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/transit/ActivityElement.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/transit/ActivityElement.java
@@ -11,7 +11,9 @@
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
 
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
  * Represents an {@link Activity} that needs to exist to consider the Station active.
@@ -31,7 +33,8 @@
 
     @Override
     public @Nullable ConditionWithResult<ActivityT> createEnterCondition() {
-        return new ActivityExistsCondition();
+        // Can be overridden with requireToBeInSameTask() or requireToBeInNewTask().
+        return new ActivityExistsInNewTaskCondition();
     }
 
     @Override
@@ -39,47 +42,154 @@
         return null;
     }
 
-    private class ActivityExistsCondition extends ConditionWithResult<ActivityT> {
+    void requireToBeInSameTask(Activity activity) {
+        replaceEnterCondition(new ActivityExistsInSameTaskCondition(activity));
+    }
 
-        public ActivityExistsCondition() {
+    void requireToBeInNewTask() {
+        replaceEnterCondition(new ActivityExistsInNewTaskCondition());
+    }
+
+    void requireNoParticularTask() {
+        replaceEnterCondition(new ActivityExistsInAnyTaskCondition());
+    }
+
+    private abstract class ActivityExistsCondition extends ConditionWithResult<ActivityT> {
+        private ActivityExistsCondition() {
             super(/* isRunOnUiThread= */ false);
         }
 
         @Override
         protected ConditionStatusWithResult<ActivityT> resolveWithSuppliers() {
-            ActivityT candidate = null;
+            ActivityT candidateMatchingClass = null;
+            ActivityT candidateMatchingClassAndTask = null;
+            String reasonForTaskIdDifference = "";
             List<Activity> allActivities = ApplicationStatus.getRunningActivities();
             for (Activity activity : allActivities) {
                 if (mActivityClass.equals(activity.getClass())) {
                     ActivityT matched = mActivityClass.cast(activity);
-                    if (candidate != null) {
-                        return error("%s matched two Activities: %s, %s", this, candidate, matched)
+                    candidateMatchingClass = matched;
+                    reasonForTaskIdDifference = getReasonForTaskIdDifference(matched);
+                    if (reasonForTaskIdDifference != null) {
+                        continue;
+                    }
+                    if (candidateMatchingClassAndTask != null) {
+                        return error(
+                                        "%s matched two Activities: %s, %s",
+                                        this, candidateMatchingClassAndTask, matched)
                                 .withoutResult();
                     }
-                    candidate = matched;
+                    candidateMatchingClassAndTask = matched;
                 }
             }
-            if (candidate == null) {
+            if (candidateMatchingClass == null) {
                 return awaiting("No Activity with expected class").withoutResult();
             }
+            if (candidateMatchingClassAndTask == null) {
+                return awaiting("Activity not in expected task: " + reasonForTaskIdDifference)
+                        .withoutResult();
+            }
 
-            @ActivityState int state = ApplicationStatus.getStateForActivity(candidate);
+            @ActivityState
+            int state = ApplicationStatus.getStateForActivity(candidateMatchingClassAndTask);
             String statusString =
                     String.format(
-                            "matched: %s (state=%s)", candidate, activityStateDescription(state));
+                            "matched: %s (state=%s)",
+                            candidateMatchingClassAndTask, activityStateDescription(state));
             if (state == ActivityState.RESUMED) {
-                return fulfilled(statusString).withResult(candidate);
+                return fulfilled(statusString).withResult(candidateMatchingClassAndTask);
             } else {
                 return awaiting(statusString).withoutResult();
             }
         }
 
+        /**
+         * Return null if |activity| is in the expected task according to the Condition's specific
+         * criteria, or the reason for the difference otherwise.
+         */
+        protected abstract @Nullable String getReasonForTaskIdDifference(ActivityT activity);
+
         @Override
         public String buildDescription() {
             return "Activity exists and is RESUMED: " + mActivityClass.getSimpleName();
         }
     }
 
+    private class ActivityExistsInAnyTaskCondition extends ActivityExistsCondition {
+        @Override
+        protected @Nullable String getReasonForTaskIdDifference(ActivityT activity) {
+            return null;
+        }
+
+        @Override
+        public String buildDescription() {
+            return super.buildDescription() + " in any task";
+        }
+    }
+
+    private class ActivityExistsInSameTaskCondition extends ActivityExistsCondition {
+        private final int mOriginTaskId;
+
+        private ActivityExistsInSameTaskCondition(Activity originActivity) {
+            super();
+            mOriginTaskId = originActivity.getTaskId();
+            assert mOriginTaskId != -1 : "The origin activity was not in any task";
+        }
+
+        @Override
+        protected @Nullable String getReasonForTaskIdDifference(ActivityT activity) {
+            // Ignore Activities in different tasks
+            int activityTaskId = activity.getTaskId();
+            if (activityTaskId == mOriginTaskId) {
+                return null;
+            } else {
+                return String.format(
+                        "Origin's task id: %d, candidate's was different: %d",
+                        mOriginTaskId, activityTaskId);
+            }
+        }
+
+        @Override
+        public String buildDescription() {
+            return super.buildDescription() + " in the same task as previous Station";
+        }
+    }
+
+    private class ActivityExistsInNewTaskCondition extends ActivityExistsCondition {
+        private final Map<Integer, Station<?>> mExistingTaskIds;
+
+        private ActivityExistsInNewTaskCondition() {
+            super();
+
+            // Store all task ids of Activities known to Public Transit.
+            mExistingTaskIds = new HashMap<>();
+            for (Station<?> activeStation : TrafficControl.getActiveStations()) {
+                ActivityElement<?> knownActivityElement = activeStation.getActivityElement();
+                if (knownActivityElement != null) {
+                    mExistingTaskIds.put(knownActivityElement.get().getTaskId(), activeStation);
+                }
+            }
+        }
+
+        @Override
+        protected @Nullable String getReasonForTaskIdDifference(ActivityT activity) {
+            // Ignore Activities in known tasks
+            int candidateTaskId = activity.getTaskId();
+            Station<?> stationInSameTask = mExistingTaskIds.get(candidateTaskId);
+            if (stationInSameTask != null) {
+                return String.format(
+                        "%s's Activity was in same task: %d",
+                        stationInSameTask.getName(), candidateTaskId);
+            }
+            return null;
+        }
+
+        @Override
+        public String buildDescription() {
+            return super.buildDescription() + " in a new task";
+        }
+    }
+
     private static String activityStateDescription(@ActivityState int state) {
         return switch (state) {
             case ActivityState.CREATED -> "CREATED";
diff --git a/base/test/android/javatests/src/org/chromium/base/test/transit/BatchedPublicTransitRule.java b/base/test/android/javatests/src/org/chromium/base/test/transit/BatchedPublicTransitRule.java
index 9cef123c..76ea584 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/transit/BatchedPublicTransitRule.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/transit/BatchedPublicTransitRule.java
@@ -13,6 +13,8 @@
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
 
+import java.util.List;
+
 /**
  * Test rule for batched Public Transit tests.
  *
@@ -73,6 +75,11 @@
     public @Nullable T getHomeStation() {
         TransitAsserts.assertCurrentStationType(
                 mHomeStationType, "getting base station", /* allowNull= */ true);
-        return (T) TrafficControl.getActiveStation();
+        List<Station<?>> activeStations = TrafficControl.getActiveStations();
+        if (activeStations.isEmpty()) {
+            return null;
+        } else {
+            return (T) activeStations.get(0);
+        }
     }
 }
diff --git a/base/test/android/javatests/src/org/chromium/base/test/transit/FacilitySwap.java b/base/test/android/javatests/src/org/chromium/base/test/transit/FacilitySwap.java
index bd9add67f..b75a78c 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/transit/FacilitySwap.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/transit/FacilitySwap.java
@@ -36,18 +36,8 @@
 
     @Override
     public String toDebugString() {
-        String facilitiesToExitString;
-        String facilitiesToEnterString;
-        if (mFacilitiesToExit.size() > 1) {
-            facilitiesToExitString = mFacilitiesToExit.toString();
-        } else {
-            facilitiesToExitString = mFacilitiesToExit.get(0).toString();
-        }
-        if (mFacilitiesToEnter.size() > 1) {
-            facilitiesToEnterString = mFacilitiesToEnter.toString();
-        } else {
-            facilitiesToEnterString = mFacilitiesToEnter.get(0).toString();
-        }
+        String facilitiesToExitString = getStateListString(mFacilitiesToExit);
+        String facilitiesToEnterString = getStateListString(mFacilitiesToEnter);
         return String.format(
                 "FacilitySwap %d (from %s to %s)",
                 mId, facilitiesToExitString, facilitiesToEnterString);
diff --git a/base/test/android/javatests/src/org/chromium/base/test/transit/Station.java b/base/test/android/javatests/src/org/chromium/base/test/transit/Station.java
index ac6d401..6be79b0 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/transit/Station.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/transit/Station.java
@@ -100,6 +100,26 @@
         return mId;
     }
 
+    void requireToBeInSameTask(Station<?> originStation) {
+        assertInPhase(Phase.NEW);
+        if (mActivityElement != null) {
+            originStation.assertInPhase(Phase.ACTIVE);
+            ActivityElement<?> originActivityElement = originStation.getActivityElement();
+            if (originActivityElement != null) {
+                mActivityElement.requireToBeInSameTask(originActivityElement.get());
+            } else {
+                mActivityElement.requireNoParticularTask();
+            }
+        }
+    }
+
+    void requireToBeInNewTask() {
+        assertInPhase(Phase.NEW);
+        if (mActivityElement != null) {
+            mActivityElement.requireToBeInNewTask();
+        }
+    }
+
     /**
      * Starts a transition from this origin {@link Station} to another destination {@link Station}.
      * Runs the transition |trigger|, and blocks until the destination {@link Station} is considered
@@ -112,7 +132,9 @@
      * @param <T> the type of the destination {@link Station}.
      */
     public final <T extends Station<?>> T travelToSync(T destination, @Nullable Trigger trigger) {
-        Trip trip = new Trip(this, destination, TransitionOptions.DEFAULT, trigger);
+        destination.requireToBeInSameTask(this);
+        Trip trip =
+                new Trip(List.of(this), List.of(destination), TransitionOptions.DEFAULT, trigger);
         trip.transitionSync();
         return destination;
     }
@@ -120,7 +142,8 @@
     /** Version of #travelToSync() with extra TransitionOptions. */
     public final <T extends Station<?>> T travelToSync(
             T destination, TransitionOptions options, @Nullable Trigger trigger) {
-        Trip trip = new Trip(this, destination, options, trigger);
+        destination.requireToBeInSameTask(this);
+        Trip trip = new Trip(List.of(this), List.of(destination), options, trigger);
         trip.transitionSync();
         return destination;
     }
@@ -298,6 +321,33 @@
     }
 
     /**
+     * Starts a transition into a {@link Station} without leaving the current one.
+     *
+     * <p>Useful for opening a new window.
+     *
+     * <p>Runs the transition |trigger|, and blocks until the destination {@link Station} is
+     * considered ACTIVE (enter Conditions are fulfilled) and the {@link Trip}'s transition
+     * conditions are fulfilled.
+     *
+     * @param destination the {@link Facility} to arrive at.
+     * @param trigger the trigger to start the transition (e.g. clicking a view).
+     * @return the destination {@link Station}, now ACTIVE.
+     * @param <T> the type of the destination {@link Station}.
+     */
+    public static <T extends Station<?>> T spawnSync(T destination, @Nullable Trigger trigger) {
+        return spawnSync(destination, TransitionOptions.DEFAULT, trigger);
+    }
+
+    /** Version of {@link #spawnSync(T, Trigger)} with extra TransitionOptions. */
+    public static <T extends Station<?>> T spawnSync(
+            T destination, TransitionOptions options, @Nullable Trigger trigger) {
+        destination.requireToBeInNewTask();
+        Trip trip = new Trip(List.of(), List.of(destination), options, trigger);
+        trip.transitionSync();
+        return destination;
+    }
+
+    /**
      * Add a Facility which will be entered together with this Station. Both will become ACTIVE in
      * the same Trip.
      */
@@ -317,9 +367,7 @@
     }
 
     /** Get the activity element associate with this station, if there's any. */
-    public ActivityElement<HostActivity> getActivityElement() {
-        assert mActivityElement != null
-                : "Requesting an ActivityElement for a station with no host activity.";
+    public @Nullable ActivityElement<HostActivity> getActivityElement() {
         return mActivityElement;
     }
 
diff --git a/base/test/android/javatests/src/org/chromium/base/test/transit/TrafficControl.java b/base/test/android/javatests/src/org/chromium/base/test/transit/TrafficControl.java
index d5f67f5..6f90792 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/transit/TrafficControl.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/transit/TrafficControl.java
@@ -23,48 +23,52 @@
     private static final List<Pair<String, String>> sAllStationNames = new ArrayList<>();
     private static @Nullable String sCurrentTestCase;
 
-    private static @Nullable Station<?> sActiveStation;
+    private static List<Station<?>> sActiveStations = new ArrayList<>();
 
     static void notifyCreatedStation(Station<?> station) {
         sAllStationNames.add(Pair.create(sCurrentTestCase, station.getName()));
     }
 
     static void notifyEntryPointSentinelStationCreated(EntryPointSentinelStation sentinelStation) {
-        if (sActiveStation != null) {
+        for (Station<?> station : sActiveStations) {
             // Happens when test is batched, but the Activity is not kept between tests; Public
             // Transit's Station/Facility state need to reflect that and start from a new
             // {@link EntryPointSentinelStation}.
-            sActiveStation.setStateTransitioningFrom();
-            sActiveStation.setStateFinished();
+            station.setStateTransitioningFrom();
+            station.setStateFinished();
         }
-        sActiveStation = sentinelStation;
+        sActiveStations.clear();
     }
 
-    static void notifyActiveStationChanged(Station<?> newActiveStation) {
-        assert newActiveStation.getPhase() == Phase.ACTIVE : "New active Station must be ACTIVE";
-        if (sActiveStation != null) {
-            assert sActiveStation.getPhase() != Phase.ACTIVE
+    static void notifyActiveStationsChanged(
+            List<Station<?>> exitedStations, List<Station<?>> enteredStations) {
+        for (Station<?> enteredStation : enteredStations) {
+            assert enteredStation.getPhase() == Phase.ACTIVE : "New active Station must be ACTIVE";
+        }
+        for (Station<?> exitedStation : exitedStations) {
+            assert exitedStation.getPhase() != Phase.ACTIVE
                     : "Previously active station was not ACTIVE";
         }
-        sActiveStation = newActiveStation;
+        sActiveStations.removeAll(exitedStations);
+        sActiveStations.addAll(enteredStations);
     }
 
     /**
-     * Hop off Public Transit - set the active station to null so that a subsequent test can go
-     * through an entry point again on the same process.
+     * Hop off Public Transit - clear the active stations so that a subsequent test can go through
+     * an entry point again on the same process.
      *
      * <p>Useful in Robolectric tests.
      */
     public static void hopOffPublicTransit() {
-        sActiveStation = null;
+        sActiveStations.clear();
     }
 
     public static List<Pair<String, String>> getAllStationsNames() {
         return sAllStationNames;
     }
 
-    public static @Nullable Station<?> getActiveStation() {
-        return sActiveStation;
+    public static List<Station<?>> getActiveStations() {
+        return sActiveStations;
     }
 
     static void onTestStarted(String testName) {
diff --git a/base/test/android/javatests/src/org/chromium/base/test/transit/TransitAsserts.java b/base/test/android/javatests/src/org/chromium/base/test/transit/TransitAsserts.java
index 21036cd90..f2cdc07 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/transit/TransitAsserts.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/transit/TransitAsserts.java
@@ -29,8 +29,15 @@
      *     {@link Facility} was not active.
      */
     public static void assertFinalDestination(
-            Station expectedStation, Facility... expectedFacilities) {
-        Station activeStation = TrafficControl.getActiveStation();
+            Station<?> expectedStation, Facility<?>... expectedFacilities) {
+        List<Station<?>> activeStations = TrafficControl.getActiveStations();
+        if (activeStations.size() != 1) {
+            raiseAssertion(
+                    String.format(
+                            "Expected exactly one active station, but found %d",
+                            activeStations.size()));
+        }
+        Station<?> activeStation = activeStations.get(0);
         if (activeStation != expectedStation) {
             raiseAssertion(
                     String.format(
@@ -45,7 +52,7 @@
                             expectedStation, ConditionalState.phaseToString(phase)));
         }
 
-        for (Facility facility : expectedFacilities) {
+        for (Facility<?> facility : expectedFacilities) {
             phase = facility.getPhase();
             if (phase != Phase.ACTIVE) {
                 raiseAssertion(
@@ -57,6 +64,29 @@
     }
 
     /**
+     * Asserts that the given stations are the final ones in a test method and no further
+     * transitions happened.
+     *
+     * <p>Version of {@link #assertFinalDestination(Station, Facility...)} when ending with multiple
+     * windows.
+     */
+    public static void assertFinalDestinations(Station<?>... expectedStations) {
+        List<Station<?>> activeStations = TrafficControl.getActiveStations();
+        for (Station<?> expectedStation : expectedStations) {
+            if (!activeStations.contains(expectedStation)) {
+                raiseAssertion(
+                        String.format(
+                                "Expected %s to be one of the final destinations, but it was not"
+                                        + " active",
+                                expectedStation));
+            }
+        }
+        if (activeStations.size() > expectedStations.length) {
+            raiseAssertion("Too many stations were active");
+        }
+    }
+
+    /**
      * Asserts the current station is of a given expected type.
      *
      * @param stationType the expected type of {@link Station}
@@ -65,16 +95,30 @@
      */
     public static void assertCurrentStationType(
             Class<? extends Station<?>> stationType, String situation, boolean allowNull) {
-        Station activeStation = TrafficControl.getActiveStation();
-        if ((activeStation == null && !allowNull)
-                || (activeStation != null && !stationType.isInstance(activeStation))) {
+        List<Station<?>> activeStations = TrafficControl.getActiveStations();
+        if (activeStations.size() == 0) {
+            if (!allowNull) {
+                raiseAssertion(
+                        String.format(
+                                "Expected exactly one active station, but found %d",
+                                activeStations.size()));
+            }
+        } else if (activeStations.size() == 1) {
+            Station<?> activeStation = activeStations.get(0);
+            if (!stationType.isInstance(activeStation)) {
+                raiseAssertion(
+                        String.format(
+                                "Expected current station to be of type <%s> at <%s>, but was"
+                                        + " actually of type <%s>",
+                                stationType,
+                                situation,
+                                activeStation != null ? activeStation.getClass() : "null"));
+            }
+        } else { // if (activeStations.size() > 1)
             raiseAssertion(
                     String.format(
-                            "Expected current station to be of type <%s> at <%s>, but was actually"
-                                    + " of type <%s>",
-                            stationType,
-                            situation,
-                            activeStation != null ? activeStation.getClass() : "null"));
+                            "Expected exactly one active station, but found %d",
+                            activeStations.size()));
         }
     }
 
diff --git a/base/test/android/javatests/src/org/chromium/base/test/transit/Transition.java b/base/test/android/javatests/src/org/chromium/base/test/transit/Transition.java
index 07172931..85a83dff 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/transit/Transition.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/transit/Transition.java
@@ -288,4 +288,14 @@
             }
         }
     }
+
+    protected String getStateListString(List<? extends ConditionalState> states) {
+        if (states.isEmpty()) {
+            return "<none>";
+        } else if (states.size() == 1) {
+            return states.get(0).toString();
+        } else {
+            return states.toString();
+        }
+    }
 }
diff --git a/base/test/android/javatests/src/org/chromium/base/test/transit/Trip.java b/base/test/android/javatests/src/org/chromium/base/test/transit/Trip.java
index d2cd3093..9c62a0e 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/transit/Trip.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/transit/Trip.java
@@ -17,47 +17,57 @@
  */
 @NullMarked
 class Trip extends Transition {
-    private final Station mOrigin;
-    private final Station mDestination;
+    private final List<Station<?>> mOrigins;
+    private final List<Station<?>> mDestinations;
 
     /**
-     * Constructor. Trip is instantiated to move from one {@link Station} into another.
+     * Constructor. Trip is instantiated to move between {@link Station}s.
      *
-     * @param origin the {@link Station} to depart from.
-     * @param destination the {@link Station} to travel to.
+     * <p>The initial Transition is entering a single station.
+     *
+     * <p>Besides this, usually trips are triggered from one {@link Station} to another. With
+     * multiwindow, there can be any number of stations being exited and any number of stations
+     * being entered.
+     *
+     * @param origins the {@link Station}s to depart from.
+     * @param destinations the {@link Station}s to travel to.
      * @param options the {@link TransitionOptions}.
      * @param trigger the action that triggers the transition. e.g. clicking a View.
      */
     Trip(
-            Station origin,
-            Station destination,
+            List<Station<?>> origins,
+            List<Station<?>> destinations,
             TransitionOptions options,
             @Nullable Trigger trigger) {
         super(
                 options,
-                getStationPlusFacilitiesWithPhase(origin, Phase.ACTIVE),
-                getStationPlusFacilitiesWithPhase(destination, Phase.NEW),
+                getStationsPlusFacilitiesWithPhase(origins, Phase.ACTIVE),
+                getStationsPlusFacilitiesWithPhase(destinations, Phase.NEW),
                 trigger);
-        mOrigin = origin;
-        mDestination = destination;
+        mOrigins = origins;
+        mDestinations = destinations;
     }
 
-    private static List<? extends ConditionalState> getStationPlusFacilitiesWithPhase(
-            Station station, @Phase int phase) {
+    private static List<? extends ConditionalState> getStationsPlusFacilitiesWithPhase(
+            List<Station<?>> stations, @Phase int phase) {
         List<ConditionalState> allConditionalStates = new ArrayList<>();
-        allConditionalStates.add(station);
-        allConditionalStates.addAll(station.getFacilitiesWithPhase(phase));
+        for (Station<?> station : stations) {
+            allConditionalStates.add(station);
+            allConditionalStates.addAll(station.getFacilitiesWithPhase(phase));
+        }
         return allConditionalStates;
     }
 
     @Override
     protected void onAfterTransition() {
         super.onAfterTransition();
-        TrafficControl.notifyActiveStationChanged(mDestination);
+        TrafficControl.notifyActiveStationsChanged(mOrigins, mDestinations);
     }
 
     @Override
     public String toDebugString() {
-        return String.format("Trip %d (%s to %s)", mId, mOrigin, mDestination);
+        return String.format(
+                "Trip %d (%s to %s)",
+                mId, getStateListString(mOrigins), getStateListString(mDestinations));
     }
 }
diff --git a/base/test/test_proto_loader.h b/base/test/test_proto_loader.h
index 0cc7a9ed..671464d 100644
--- a/base/test/test_proto_loader.h
+++ b/base/test/test_proto_loader.h
@@ -23,11 +23,7 @@
 #endif  // defined(PROTO_TEST_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(PROTO_TEST_IMPLEMENTATION)
 #define PROTO_TEST_EXPORT __attribute__((visibility("default")))
-#else
-#define PROTO_TEST_EXPORT
-#endif
 #endif
 #else  // defined(COMPONENT_BUILD)
 #define PROTO_TEST_EXPORT
diff --git a/base/test/test_trace_processor_export.h b/base/test/test_trace_processor_export.h
index f5191b80..94b5a2a 100644
--- a/base/test/test_trace_processor_export.h
+++ b/base/test/test_trace_processor_export.h
@@ -14,13 +14,7 @@
 #endif  // defined(TEST_TRACE_PROCESSOR_IMPL)
 
 #else  // defined(WIN32)
-
-#if defined(TEST_TRACE_PROCESSOR_IMPL)
 #define TEST_TRACE_PROCESSOR_EXPORT __attribute__((visibility("default")))
-#else
-#define TEST_TRACE_PROCESSOR_EXPORT
-#endif  // defined(TEST_TRACE_PROCESSOR_IMPL)
-
 #endif  // defined(WIN32)
 
 #endif  // BASE_TEST_TEST_TRACE_PROCESSOR_EXPORT_H_
diff --git a/build/android/apk_operations.pydeps b/build/android/apk_operations.pydeps
index 5455106a..cde63ff 100644
--- a/build/android/apk_operations.pydeps
+++ b/build/android/apk_operations.pydeps
@@ -48,6 +48,7 @@
 ../../third_party/catapult/devil/devil/devil_env.py
 ../../third_party/catapult/devil/devil/utils/__init__.py
 ../../third_party/catapult/devil/devil/utils/cmd_helper.py
+../../third_party/catapult/devil/devil/utils/host_utils.py
 ../../third_party/catapult/devil/devil/utils/lazy/__init__.py
 ../../third_party/catapult/devil/devil/utils/lazy/weak_constant.py
 ../../third_party/catapult/devil/devil/utils/logging_common.py
diff --git a/build/android/test_runner.pydeps b/build/android/test_runner.pydeps
index f87ec1b..523ae642 100644
--- a/build/android/test_runner.pydeps
+++ b/build/android/test_runner.pydeps
@@ -78,6 +78,7 @@
 ../../third_party/catapult/devil/devil/utils/__init__.py
 ../../third_party/catapult/devil/devil/utils/cmd_helper.py
 ../../third_party/catapult/devil/devil/utils/file_utils.py
+../../third_party/catapult/devil/devil/utils/host_utils.py
 ../../third_party/catapult/devil/devil/utils/lazy/__init__.py
 ../../third_party/catapult/devil/devil/utils/lazy/weak_constant.py
 ../../third_party/catapult/devil/devil/utils/logging_common.py
diff --git a/build/config/aix/BUILD.gn b/build/config/aix/BUILD.gn
index 6e55c83..975f82e 100644
--- a/build/config/aix/BUILD.gn
+++ b/build/config/aix/BUILD.gn
@@ -29,6 +29,7 @@
     "-fdata-sections",
     "-ffunction-sections",
     "-fno-extern-tls-init",
+    "-fno-reorder-functions",
     "-O3",
 
     # "-Werror"
diff --git a/build/config/compiler/pgo/BUILD.gn b/build/config/compiler/pgo/BUILD.gn
index b5b7703..52c6ced 100644
--- a/build/config/compiler/pgo/BUILD.gn
+++ b/build/config/compiler/pgo/BUILD.gn
@@ -101,12 +101,26 @@
     }
 
     if (_pgo_target != "" && pgo_data_path == "") {
+      common_args = [
+        "--target",
+        _pgo_target,
+      ]
+      if (pgo_override_filename != "") {
+        common_args += [
+          "--override-filename",
+          pgo_override_filename,
+        ]
+
+        # This is slow but it is only expected to be run on the orderfile bots
+        # that require this special step.
+        exec_script("//tools/update_pgo_profiles.py",
+                    common_args + [
+                          "update",
+                          "--gs-url-base=$pgo_gs_bucket/$pgo_gs_bucket_path",
+                        ])
+      }
       pgo_data_path = exec_script("//tools/update_pgo_profiles.py",
-                                  [
-                                    "--target",
-                                    _pgo_target,
-                                    "get_profile_path",
-                                  ],
+                                  common_args + [ "get_profile_path" ],
                                   "value")
     }
     assert(pgo_data_path != "",
diff --git a/build/config/compiler/pgo/pgo.gni b/build/config/compiler/pgo/pgo.gni
index cdb0bbbd..646a0027 100644
--- a/build/config/compiler/pgo/pgo.gni
+++ b/build/config/compiler/pgo/pgo.gni
@@ -39,6 +39,12 @@
 
   # Whether to enable temporal pgo or not (experimental).
   temporal_pgo_profile = false
+
+  # Flags to override the pgo profile with a freshly created one (newer than
+  # the sha1 in the repo).
+  pgo_override_filename = ""
+  pgo_gs_bucket = ""
+  pgo_gs_bucket_path = ""
 }
 
 # TODO(crbug.com/409738601): Remove assert once clang no
diff --git a/build/config/siso/clang_exception.star b/build/config/siso/clang_exception.star
index 0379fae..e265bf9 100644
--- a/build/config/siso/clang_exception.star
+++ b/build/config/siso/clang_exception.star
@@ -52,7 +52,6 @@
             "action_outs": [
                 # keep-sorted start
                 "./obj/content/test/content_unittests/auction_runner_unittest.o",
-                "./obj/v8/v8_compiler/csa-optimize-phase.o",
                 # keep-sorted end
             ],
             "timeout": "4m",
diff --git a/build/config/win/manifest.gni b/build/config/win/manifest.gni
index 805c24d0..2feb96c 100644
--- a/build/config/win/manifest.gni
+++ b/build/config/win/manifest.gni
@@ -90,6 +90,7 @@
                   # We handle UAC by adding explicit .manifest files instead.
                   "/manifestuac:no",
                 ] + manifests
+      inputs = invoker.sources
     }
 
     # This group only exists to add a dep on the invoker's deps and to
@@ -100,7 +101,7 @@
 
       # Apply any dependencies from the invoker to this target, since those
       # dependencies may have created the input manifest files.
-      forward_variables_from(invoker, [ "deps" ])
+      forward_variables_from(invoker, [ "public_deps" ])
     }
   }
 } else {
diff --git a/build/rust/rust_bindgen_generator.gni b/build/rust/rust_bindgen_generator.gni
index c91916b..796d593c 100644
--- a/build/rust/rust_bindgen_generator.gni
+++ b/build/rust/rust_bindgen_generator.gni
@@ -223,21 +223,6 @@
       clang_resource_dir,
     ]
 
-    # The `--sysroot` flag is not working as expected and gets ignored (we don't
-    # fully understand why, see b/328510249). But we add `-isystem` to point at
-    # the headers in the sysroot which are otherwise not found.
-    if (sysroot != "") {
-      if (is_win) {
-        args +=
-            [ "-I" + rebase_path(sysroot + "/usr/include/", root_build_dir) ]
-      } else {
-        args += [
-          "-isystem",
-          rebase_path(sysroot + "/usr/include/", root_build_dir),
-        ]
-      }
-    }
-
     if (is_win) {
       # On Windows we fall back to using system headers from a sysroot from
       # depot_tools. This is negotiated by python scripts and the result is
diff --git a/build/rust/std/rules/BUILD.gn b/build/rust/std/rules/BUILD.gn
index 7beac98..38957e8 100644
--- a/build/rust/std/rules/BUILD.gn
+++ b/build/rust/std/rules/BUILD.gn
@@ -288,221 +288,225 @@
 }
 cargo_crate("compiler_builtins") {
   crate_type = "rlib"
-  crate_root = "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/lib.rs"
+  crate_root = "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/lib.rs"
   sources = [
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/aarch64.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/aarch64_linux.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/arm.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/arm_linux.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/avr.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/float/add.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/float/cmp.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/float/conv.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/float/div.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/float/extend.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/float/mod.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/float/mul.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/float/pow.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/float/sub.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/float/traits.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/float/trunc.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/hexagon.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/int/addsub.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/int/big.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/int/bswap.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/int/leading_zeros.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/int/mod.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/int/mul.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/int/sdiv.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/int/shift.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/int/specialized_div_rem/asymmetric.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/int/specialized_div_rem/binary_long.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/int/specialized_div_rem/delegate.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/int/specialized_div_rem/mod.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/int/specialized_div_rem/norm_shift.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/int/specialized_div_rem/trifecta.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/int/trailing_zeros.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/int/traits.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/int/udiv.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/lib.miri.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/lib.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/macros.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/acos.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/acosf.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/acosh.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/acoshf.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/arch/aarch64.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/arch/i586.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/arch/i686.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/arch/mod.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/arch/wasm32.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/asin.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/asinf.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/asinh.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/asinhf.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/atan.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/atan2.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/atan2f.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/atanf.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/atanh.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/atanhf.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/cbrt.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/cbrtf.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/ceil.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/copysign.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/copysignf.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/copysignf128.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/copysignf16.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/cos.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/cosf.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/cosh.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/coshf.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/erf.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/erff.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/exp.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/exp10.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/exp10f.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/exp2.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/exp2f.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/expf.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/expm1.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/expm1f.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/expo2.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/fabs.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/fabsf.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/fabsf128.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/fabsf16.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/fdim.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/fdimf.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/fdimf128.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/fdimf16.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/floor.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/floorf.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/floorf128.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/floorf16.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/fma.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/fma_wide.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/fmin_fmax.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/fminimum_fmaximum.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/fminimum_fmaximum_num.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/fmod.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/fmodf.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/fmodf128.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/fmodf16.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/frexp.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/frexpf.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/generic/ceil.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/generic/copysign.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/generic/fabs.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/generic/fdim.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/generic/floor.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/generic/fmax.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/generic/fmaximum.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/generic/fmaximum_num.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/generic/fmin.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/generic/fminimum.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/generic/fminimum_num.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/generic/fmod.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/generic/mod.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/generic/rint.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/generic/round.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/generic/scalbn.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/generic/sqrt.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/generic/trunc.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/hypot.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/hypotf.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/ilogb.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/ilogbf.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/j0.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/j0f.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/j1.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/j1f.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/jn.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/jnf.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/k_cos.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/k_cosf.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/k_expo2.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/k_expo2f.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/k_sin.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/k_sinf.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/k_tan.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/k_tanf.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/ldexp.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/ldexpf.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/ldexpf128.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/ldexpf16.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/lgamma.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/lgamma_r.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/lgammaf.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/lgammaf_r.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/log.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/log10.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/log10f.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/log1p.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/log1pf.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/log2.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/log2f.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/logf.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/mod.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/modf.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/modff.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/nextafter.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/nextafterf.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/pow.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/powf.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/rem_pio2.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/rem_pio2_large.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/rem_pio2f.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/remainder.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/remainderf.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/remquo.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/remquof.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/rint.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/round.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/roundeven.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/roundf.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/roundf128.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/roundf16.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/scalbn.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/scalbnf.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/scalbnf128.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/scalbnf16.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/sin.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/sincos.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/sincosf.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/sinf.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/sinh.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/sinhf.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/sqrt.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/sqrtf.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/sqrtf128.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/sqrtf16.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/support/big.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/support/big/tests.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/support/env.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/support/float_traits.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/support/hex_float.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/support/int_traits.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/support/macros.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/support/mod.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/tan.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/tanf.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/tanh.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/tanhf.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/tgamma.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/tgammaf.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/trunc.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/truncf.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/truncf128.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/libm_math/truncf16.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/math/mod.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/mem/impls.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/mem/mod.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/mem/x86_64.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/probestack.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/riscv.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/x86.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/x86_64.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/aarch64.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/aarch64_linux.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/arm.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/arm_linux.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/avr.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/float/add.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/float/cmp.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/float/conv.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/float/div.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/float/extend.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/float/mod.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/float/mul.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/float/pow.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/float/sub.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/float/traits.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/float/trunc.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/hexagon.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/int/addsub.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/int/big.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/int/bswap.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/int/leading_zeros.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/int/mod.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/int/mul.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/int/sdiv.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/int/shift.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/int/specialized_div_rem/asymmetric.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/int/specialized_div_rem/binary_long.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/int/specialized_div_rem/delegate.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/int/specialized_div_rem/mod.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/int/specialized_div_rem/norm_shift.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/int/specialized_div_rem/trifecta.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/int/trailing_zeros.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/int/traits.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/int/udiv.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/lib.miri.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/lib.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/macros.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/acos.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/acosf.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/acosh.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/acoshf.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/arch/aarch64.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/arch/i586.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/arch/mod.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/arch/wasm32.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/arch/x86.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/arch/x86/detect.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/arch/x86/fma.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/asin.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/asinf.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/asinh.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/asinhf.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/atan.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/atan2.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/atan2f.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/atanf.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/atanh.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/atanhf.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/cbrt.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/cbrtf.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/ceil.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/copysign.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/copysignf.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/copysignf128.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/copysignf16.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/cos.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/cosf.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/cosh.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/coshf.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/erf.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/erff.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/exp.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/exp10.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/exp10f.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/exp2.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/exp2f.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/expf.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/expm1.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/expm1f.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/expo2.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/fabs.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/fabsf.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/fabsf128.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/fabsf16.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/fdim.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/fdimf.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/fdimf128.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/fdimf16.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/floor.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/floorf.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/floorf128.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/floorf16.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/fma.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/fmin_fmax.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/fminimum_fmaximum.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/fminimum_fmaximum_num.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/fmod.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/fmodf.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/fmodf128.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/fmodf16.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/frexp.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/frexpf.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/generic/ceil.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/generic/copysign.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/generic/fabs.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/generic/fdim.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/generic/floor.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/generic/fma.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/generic/fma_wide.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/generic/fmax.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/generic/fmaximum.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/generic/fmaximum_num.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/generic/fmin.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/generic/fminimum.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/generic/fminimum_num.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/generic/fmod.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/generic/mod.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/generic/rint.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/generic/round.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/generic/scalbn.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/generic/sqrt.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/generic/trunc.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/hypot.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/hypotf.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/ilogb.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/ilogbf.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/j0.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/j0f.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/j1.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/j1f.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/jn.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/jnf.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/k_cos.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/k_cosf.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/k_expo2.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/k_expo2f.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/k_sin.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/k_sinf.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/k_tan.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/k_tanf.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/ldexp.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/ldexpf.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/ldexpf128.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/ldexpf16.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/lgamma.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/lgamma_r.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/lgammaf.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/lgammaf_r.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/log.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/log10.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/log10f.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/log1p.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/log1pf.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/log2.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/log2f.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/logf.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/mod.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/modf.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/modff.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/nextafter.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/nextafterf.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/pow.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/powf.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/rem_pio2.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/rem_pio2_large.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/rem_pio2f.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/remainder.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/remainderf.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/remquo.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/remquof.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/rint.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/round.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/roundeven.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/roundf.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/roundf128.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/roundf16.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/scalbn.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/scalbnf.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/scalbnf128.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/scalbnf16.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/sin.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/sincos.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/sincosf.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/sinf.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/sinh.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/sinhf.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/sqrt.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/sqrtf.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/sqrtf128.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/sqrtf16.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/support/big.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/support/big/tests.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/support/env.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/support/feature_detect.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/support/float_traits.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/support/hex_float.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/support/int_traits.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/support/macros.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/support/mod.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/tan.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/tanf.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/tanh.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/tanhf.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/tgamma.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/tgammaf.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/trunc.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/truncf.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/truncf128.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/libm_math/truncf16.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/math/mod.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/mem/impls.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/mem/mod.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/mem/x86_64.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/probestack.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/riscv.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/x86.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/x86_64.rs",
   ]
   inputs = []
   no_std = true
@@ -510,7 +514,7 @@
   # Unit tests skipped. Generate with --with-tests to include them.
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.1.156"
+  cargo_pkg_version = "0.1.157"
   cargo_pkg_authors = "Jorge Aparicio <japaricious@gmail.com>"
   cargo_pkg_name = "compiler_builtins"
   cargo_pkg_description = "Compiler intrinsics used by the Rust compiler."
@@ -537,9 +541,9 @@
     "default",
     "rustc-dep-of-std",
   ]
-  build_root = "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/build.rs"
-  build_sources = [ "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/build.rs" ]
-  build_script_inputs = [ "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.156/src/../configure.rs" ]
+  build_root = "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/build.rs"
+  build_sources = [ "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/build.rs" ]
+  build_script_inputs = [ "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.157/src/../configure.rs" ]
   rustenv = [
     "CFG_DISABLE_UNSTABLE_FEATURES=0",
     "STD_ENV_ARCH=$rust_target_arch",
@@ -1242,34 +1246,35 @@
 }
 cargo_crate("hashbrown") {
   crate_type = "rlib"
-  crate_root = "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.2/src/lib.rs"
+  crate_root = "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.3/src/lib.rs"
   sources = [
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.2/src/control/bitmask.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.2/src/control/group/generic.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.2/src/control/group/mod.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.2/src/control/group/neon.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.2/src/control/group/sse2.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.2/src/control/mod.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.2/src/control/tag.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.2/src/external_trait_impls/mod.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.2/src/external_trait_impls/rayon/helpers.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.2/src/external_trait_impls/rayon/map.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.2/src/external_trait_impls/rayon/mod.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.2/src/external_trait_impls/rayon/raw.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.2/src/external_trait_impls/rayon/set.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.2/src/external_trait_impls/rayon/table.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.2/src/external_trait_impls/serde.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.2/src/lib.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.2/src/macros.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.2/src/map.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.2/src/raw/alloc.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.2/src/raw/mod.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.2/src/raw_entry.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.2/src/rustc_entry.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.2/src/scopeguard.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.2/src/set.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.2/src/table.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.2/src/util.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.3/src/control/bitmask.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.3/src/control/group/generic.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.3/src/control/group/lsx.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.3/src/control/group/mod.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.3/src/control/group/neon.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.3/src/control/group/sse2.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.3/src/control/mod.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.3/src/control/tag.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.3/src/external_trait_impls/mod.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.3/src/external_trait_impls/rayon/helpers.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.3/src/external_trait_impls/rayon/map.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.3/src/external_trait_impls/rayon/mod.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.3/src/external_trait_impls/rayon/raw.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.3/src/external_trait_impls/rayon/set.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.3/src/external_trait_impls/rayon/table.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.3/src/external_trait_impls/serde.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.3/src/lib.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.3/src/macros.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.3/src/map.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.3/src/raw/alloc.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.3/src/raw/mod.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.3/src/raw_entry.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.3/src/rustc_entry.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.3/src/scopeguard.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.3/src/set.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.3/src/table.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.15.3/src/util.rs",
   ]
   inputs = []
   no_std = true
@@ -1277,7 +1282,7 @@
   # Unit tests skipped. Generate with --with-tests to include them.
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.15.2"
+  cargo_pkg_version = "0.15.3"
   cargo_pkg_authors = "Amanieu d'Antras <amanieu@gmail.com>"
   cargo_pkg_name = "hashbrown"
   cargo_pkg_description = "A Rust port of Google's SwissTable hash map"
@@ -1307,7 +1312,6 @@
     "compiler_builtins",
     "core",
     "nightly",
-    "raw-entry",
     "rustc-dep-of-std",
     "rustc-internal-api",
   ]
@@ -3041,7 +3045,6 @@
     "//third_party/rust-toolchain/lib/rustlib/src/rust/library/stdarch/crates/std_detect/src/detect/os/linux/aarch64.rs",
     "//third_party/rust-toolchain/lib/rustlib/src/rust/library/stdarch/crates/std_detect/src/detect/os/linux/arm.rs",
     "//third_party/rust-toolchain/lib/rustlib/src/rust/library/stdarch/crates/std_detect/src/detect/os/linux/auxvec.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/stdarch/crates/std_detect/src/detect/os/linux/cpuinfo.rs",
     "//third_party/rust-toolchain/lib/rustlib/src/rust/library/stdarch/crates/std_detect/src/detect/os/linux/loongarch.rs",
     "//third_party/rust-toolchain/lib/rustlib/src/rust/library/stdarch/crates/std_detect/src/detect/os/linux/mips.rs",
     "//third_party/rust-toolchain/lib/rustlib/src/rust/library/stdarch/crates/std_detect/src/detect/os/linux/mod.rs",
diff --git a/build/rust/tests/bindgen_static_fns_test/lib.h b/build/rust/tests/bindgen_static_fns_test/lib.h
index eb070af..bb1abdd 100644
--- a/build/rust/tests/bindgen_static_fns_test/lib.h
+++ b/build/rust/tests/bindgen_static_fns_test/lib.h
@@ -19,11 +19,7 @@
 #endif  // defined(COMPONENT_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(COMPONENT_IMPLEMENTATION)
 #define COMPONENT_EXPORT __attribute__((visibility("default")))
-#else
-#define COMPONENT_EXPORT
-#endif  // defined(COMPONENT_IMPLEMENTATION)
 #endif
 
 #else  // defined(COMPONENT_BUILD)
@@ -46,4 +42,4 @@
 }
 #endif
 
-#endif  //  BUILD_RUST_TESTS_BINDGEN_STATIC_FNS_TEST_LIB_H_
+#endif  // BUILD_RUST_TESTS_BINDGEN_STATIC_FNS_TEST_LIB_H_
diff --git a/build/rust/tests/bindgen_test/lib.h b/build/rust/tests/bindgen_test/lib.h
index a6d686e..835f659 100644
--- a/build/rust/tests/bindgen_test/lib.h
+++ b/build/rust/tests/bindgen_test/lib.h
@@ -21,11 +21,7 @@
 #endif  // defined(COMPONENT_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(COMPONENT_IMPLEMENTATION)
 #define COMPONENT_EXPORT __attribute__((visibility("default")))
-#else
-#define COMPONENT_EXPORT
-#endif  // defined(COMPONENT_IMPLEMENTATION)
 #endif
 
 #else  // defined(COMPONENT_BUILD)
@@ -42,4 +38,4 @@
 }
 #endif
 
-#endif  //  BUILD_RUST_TESTS_BINDGEN_TEST_LIB_H_
+#endif  // BUILD_RUST_TESTS_BINDGEN_TEST_LIB_H_
diff --git a/build/toolchain/apple/linker_driver.py b/build/toolchain/apple/linker_driver.py
index 4bef15b..e502500 100755
--- a/build/toolchain/apple/linker_driver.py
+++ b/build/toolchain/apple/linker_driver.py
@@ -135,6 +135,8 @@
         # may not need to reexport unless LC_REEXPORT_DYLIB is used.
         self._reexport_in_old_module = False
 
+        # temp directory for lto cache.
+        self._object_path_lto = None
 
     def run(self):
         """Runs the linker driver, separating out the main compiler driver's
@@ -274,9 +276,9 @@
         # through the clang driver. See https://crbug.com/1324104
         # The temporary directory for intermediate LTO object files. If it
         # exists, it will clean itself up on script exit.
-        object_path_lto = tempfile.TemporaryDirectory(dir=os.getcwd())
+        self._object_path_lto = tempfile.TemporaryDirectory(dir=os.getcwd())
         self._compiler_driver_args.append('-Wl,-object_path_lto,{}'.format(
-            os.path.relpath(object_path_lto.name)))
+            os.path.relpath(self._object_path_lto.name)))
 
     def check_reexport_in_old_module(self, tocname):
         """Linker driver pre-action for -Wcrl,tocname,<path>.
diff --git a/buildtools/deps_revisions.gni b/buildtools/deps_revisions.gni
index ab4de6f..72dff7b 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 = "a01c02c9d4acbdae3b7e8a2f3ee58579a9c29f96"
+  libcxx_revision = "a9cc573e7c591795d11b72d8323ba0e573b55bd6"
 }
diff --git a/cc/animation/animation_export.h b/cc/animation/animation_export.h
index 4ae15541..d249518 100644
--- a/cc/animation/animation_export.h
+++ b/cc/animation/animation_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(CC_ANIMATION_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(CC_ANIMATION_IMPLEMENTATION)
 #define CC_ANIMATION_EXPORT __attribute__((visibility("default")))
-#else
-#define CC_ANIMATION_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/cc/base/base_export.h b/cc/base/base_export.h
index cd4a3e912..87e41f0 100644
--- a/cc/base/base_export.h
+++ b/cc/base/base_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(CC_BASE_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(CC_BASE_IMPLEMENTATION)
 #define CC_BASE_EXPORT __attribute__((visibility("default")))
-#else
-#define CC_BASE_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/cc/cc_export.h b/cc/cc_export.h
index 9600c19..0c321a92 100644
--- a/cc/cc_export.h
+++ b/cc/cc_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(CC_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(CC_IMPLEMENTATION)
 #define CC_EXPORT __attribute__((visibility("default")))
-#else
-#define CC_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/cc/debug/debug_export.h b/cc/debug/debug_export.h
index 04792aa..7c3bea1a 100644
--- a/cc/debug/debug_export.h
+++ b/cc/debug/debug_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(CC_DEBUG_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(CC_DEBUG_IMPLEMENTATION)
 #define CC_DEBUG_EXPORT __attribute__((visibility("default")))
-#else
-#define CC_DEBUG_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/cc/mojo_embedder/mojo_embedder_export.h b/cc/mojo_embedder/mojo_embedder_export.h
index c6eca8a..825d2439 100644
--- a/cc/mojo_embedder/mojo_embedder_export.h
+++ b/cc/mojo_embedder/mojo_embedder_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(CC_MOJO_EMBEDDER_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(CC_MOJO_EMBEDDER_IMPLEMENTATION)
 #define CC_MOJO_EMBEDDER_EXPORT __attribute__((visibility("default")))
-#else
-#define CC_MOJO_EMBEDDER_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/cc/paint/paint_export.h b/cc/paint/paint_export.h
index 0b14d85..7a935c96 100644
--- a/cc/paint/paint_export.h
+++ b/cc/paint/paint_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(CC_PAINT_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(CC_PAINT_IMPLEMENTATION)
 #define CC_PAINT_EXPORT __attribute__((visibility("default")))
-#else
-#define CC_PAINT_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn
index 97f843f..f4727068 100644
--- a/chrome/BUILD.gn
+++ b/chrome/BUILD.gn
@@ -182,10 +182,7 @@
       if (build_mojo_proxy) {
         data_deps += [ "//mojo/proxy:mojo_proxy" ]
       }
-      deps += [
-        "//components/exo/wayland:test_controller_stub",
-        "//components/exo/wayland:ui_controls_protocol_stub",
-      ]
+      deps += [ "//components/exo/wayland:ui_controls_protocol_stub" ]
     }
 
     if (is_win) {
diff --git a/chrome/VERSION b/chrome/VERSION
index c38e913a..7e2c030 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=138
 MINOR=0
-BUILD=7166
+BUILD=7167
 PATCH=0
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/CloseAllTabsHelper.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/CloseAllTabsHelper.java
index 332674e..e560574 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/CloseAllTabsHelper.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/CloseAllTabsHelper.java
@@ -5,7 +5,6 @@
 package org.chromium.chrome.browser.tasks.tab_management;
 
 import org.chromium.chrome.browser.app.tabmodel.ArchivedTabModelOrchestrator;
-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.TabArchiver;
@@ -31,21 +30,18 @@
                         TabClosureParams.closeAllTabs().hideTabGroups(true).build(),
                         /* allowDialog= */ false);
 
-        Runnable undoRunnable = () -> {};
-        if (ChromeFeatureList.sAndroidTabDeclutter.isEnabled()) {
-            final Profile profile =
-                    tabModelSelector.getCurrentModel().getProfile().getOriginalProfile();
-            List<Integer> previouslyArchivedTabIds =
-                    unarchiveTabsForTabClosure(profile, regularTabCreator);
-            undoRunnable =
-                    () ->
-                            archiveTabsAfterTabClosureUndo(
-                                    profile,
-                                    tabModelSelector
-                                            .getTabGroupModelFilterProvider()
-                                            .getTabGroupModelFilter(/* isIncognito= */ false),
-                                    previouslyArchivedTabIds);
-        }
+        final Profile profile =
+                tabModelSelector.getCurrentModel().getProfile().getOriginalProfile();
+        List<Integer> previouslyArchivedTabIds =
+                unarchiveTabsForTabClosure(profile, regularTabCreator);
+        Runnable undoRunnable =
+                () ->
+                        archiveTabsAfterTabClosureUndo(
+                                profile,
+                                tabModelSelector
+                                        .getTabGroupModelFilterProvider()
+                                        .getTabGroupModelFilter(/* isIncognito= */ false),
+                                previouslyArchivedTabIds);
         tabModelSelector
                 .getModel(/* incognito= */ false)
                 .getTabRemover()
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMessageManager.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMessageManager.java
index 05f5fa1..7a2c4501 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMessageManager.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMessageManager.java
@@ -20,7 +20,6 @@
 import org.chromium.chrome.browser.back_press.BackPressManager;
 import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider;
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.hub.PaneManager;
 import org.chromium.chrome.browser.incognito.reauth.IncognitoReauthManager;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
@@ -303,33 +302,29 @@
         assert profile != null;
         mProfile = profile;
 
-        if (ChromeFeatureList.sAndroidTabDeclutter.isEnabled()) {
-            mArchivedTabsMessageService =
-                    new ArchivedTabsMessageService(
-                            mActivity,
-                            ArchivedTabModelOrchestrator.getForProfile(mProfile),
-                            mBrowserControlsStateProvider,
-                            mTabContentManager,
-                            mTabListMode,
-                            mRootView,
-                            mSnackbarManager,
-                            mRegularTabCreator,
-                            mBackPressManager,
-                            mModalDialogManager,
-                            TrackerFactory.getTrackerForProfile(profile),
-                            () ->
-                                    appendNextMessage(
-                                            MessageService.MessageType.ARCHIVED_TABS_MESSAGE),
-                            mTabListCoordinatorSupplier,
-                            mDesktopWindowStateManager,
-                            mEdgeToEdgeSupplier,
-                            TabGroupSyncServiceFactory.getForProfile(mProfile),
-                            mPaneManagerSupplier,
-                            mTabGroupUiActionHandlerSupplier,
-                            mCurrentTabGroupModelFilterSupplier);
-            addObserver(mArchivedTabsMessageService);
-            mMessageCardProviderCoordinator.subscribeMessageService(mArchivedTabsMessageService);
-        }
+        mArchivedTabsMessageService =
+                new ArchivedTabsMessageService(
+                        mActivity,
+                        ArchivedTabModelOrchestrator.getForProfile(mProfile),
+                        mBrowserControlsStateProvider,
+                        mTabContentManager,
+                        mTabListMode,
+                        mRootView,
+                        mSnackbarManager,
+                        mRegularTabCreator,
+                        mBackPressManager,
+                        mModalDialogManager,
+                        TrackerFactory.getTrackerForProfile(profile),
+                        () -> appendNextMessage(MessageService.MessageType.ARCHIVED_TABS_MESSAGE),
+                        mTabListCoordinatorSupplier,
+                        mDesktopWindowStateManager,
+                        mEdgeToEdgeSupplier,
+                        TabGroupSyncServiceFactory.getForProfile(mProfile),
+                        mPaneManagerSupplier,
+                        mTabGroupUiActionHandlerSupplier,
+                        mCurrentTabGroupModelFilterSupplier);
+        addObserver(mArchivedTabsMessageService);
+        mMessageCardProviderCoordinator.subscribeMessageService(mArchivedTabsMessageService);
 
         IphMessageService iphMessageService =
                 new IphMessageService(profile, mTabGridIphDialogCoordinator);
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabsSettings.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabsSettings.java
index ca18375..e6bfa5f 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabsSettings.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabsSettings.java
@@ -102,10 +102,6 @@
 
     private void configureTabArchiveSettings() {
         Preference tabArchiveSettingsPref = findPreference(PREF_TAB_ARCHIVE_SETTINGS);
-        if (!ChromeFeatureList.isEnabled(ChromeFeatureList.ANDROID_TAB_DECLUTTER)) {
-            tabArchiveSettingsPref.setVisible(false);
-            return;
-        }
 
         TabArchiveSettings archiveSettings =
                 new TabArchiveSettings(ChromeSharedPreferences.getInstance());
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabsSettingsUnitTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabsSettingsUnitTest.java
index 335ba54e..ce839a7 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabsSettingsUnitTest.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabsSettingsUnitTest.java
@@ -66,7 +66,6 @@
     ChromeFeatureList.TAB_GROUP_SYNC_ANDROID,
     ChromeFeatureList.TAB_GROUP_SYNC_AUTO_OPEN_KILL_SWITCH
 })
-@DisableFeatures(ChromeFeatureList.ANDROID_TAB_DECLUTTER)
 public class TabsSettingsUnitTest {
     @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
 
@@ -184,16 +183,6 @@
     }
 
     @Test
-    @DisableFeatures(ChromeFeatureList.ANDROID_TAB_DECLUTTER)
-    public void testArchiveSettingsHiddenWhenFeatureOff() {
-        TabsSettings tabsSettings = launchFragment();
-        Preference archiveSettinsEntryPoint =
-                tabsSettings.findPreference(TabsSettings.PREF_TAB_ARCHIVE_SETTINGS);
-        assertFalse(archiveSettinsEntryPoint.isVisible());
-    }
-
-    @Test
-    @EnableFeatures(ChromeFeatureList.ANDROID_TAB_DECLUTTER)
     public void testArchiveSettingsTitleAndSummary() {
         TabArchiveSettings archiveSettings =
                 new TabArchiveSettings(ChromeSharedPreferences.getInstance());
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsDialogCoordinatorTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsDialogCoordinatorTest.java
index a08bb71e..dcad9dbe 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsDialogCoordinatorTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsDialogCoordinatorTest.java
@@ -49,14 +49,12 @@
 import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.DoNotBatch;
 import org.chromium.base.test.util.Feature;
-import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.base.test.util.HistogramWatcher;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.base.test.util.UserActionTester;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.app.tabmodel.ArchivedTabModelOrchestrator;
 import org.chromium.chrome.browser.app.tabmodel.ArchivedTabModelOrchestrator.Observer;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.layouts.LayoutTestUtils;
 import org.chromium.chrome.browser.layouts.LayoutType;
@@ -81,7 +79,6 @@
 @RunWith(ChromeJUnit4ClassRunner.class)
 @DoNotBatch(reason = "TODO(crbug.com/348068134): Batch this test suite.")
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
-@EnableFeatures({ChromeFeatureList.ANDROID_TAB_DECLUTTER})
 @DisabledTest(message = "crbug.com/397759336")
 public class ArchivedTabsDialogCoordinatorTest {
     @Rule
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherLayoutPTTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherLayoutPTTest.java
index 36fc74a..a5ed56a3 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherLayoutPTTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherLayoutPTTest.java
@@ -115,7 +115,9 @@
             PageStation currentStation) {
         RegularTabSwitcherStation tabSwitcherStation = currentStation.openRegularTabSwitcher();
         CarryOn.pickUp(
-                new TabThumbnailsCapturedCarryOn(/* isIncognito= */ false), /* trigger= */ null);
+                new TabThumbnailsCapturedCarryOn(
+                        tabSwitcherStation.tabModelSelectorElement.get(), /* isIncognito= */ false),
+                /* trigger= */ null);
         return tabSwitcherStation;
     }
 
@@ -124,7 +126,9 @@
             PageStation currentStation) {
         IncognitoTabSwitcherStation tabSwitcherStation = currentStation.openIncognitoTabSwitcher();
         CarryOn.pickUp(
-                new TabThumbnailsCapturedCarryOn(/* isIncognito= */ true), /* trigger= */ null);
+                new TabThumbnailsCapturedCarryOn(
+                        tabSwitcherStation.tabModelSelectorElement.get(), /* isIncognito= */ true),
+                /* trigger= */ null);
         return tabSwitcherStation;
     }
 
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceMediator.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceMediator.java
index 0d9c00a..4c2f7c4f 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceMediator.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceMediator.java
@@ -190,7 +190,7 @@
      */
     private class FeedSigninPromo {
         private final SigninPromoCoordinator mSigninPromoCoordinator;
-        private final View mPromoView;
+        @Nullable private View mPromoView;
         private boolean mCanShowPersonalizedSuggestions;
         private boolean mCanShowPromo;
 
@@ -208,14 +208,18 @@
             mCanShowPromo =
                     mSigninPromoCoordinator.canShowPromo() && mCanShowPersonalizedSuggestions;
 
-            mPromoView = mSigninPromoCoordinator.buildPromoView((ViewGroup) mCoordinator.getView());
-            mSigninPromoCoordinator.setView(mPromoView);
+            if (mCanShowPromo) {
+                // The view is created lazily to avoid increasing the browser memory footprint by
+                // keeping the view in memory even when it's never shown to the user.
+                initializePromoView();
+            }
         }
 
         boolean canShowPromo() {
             return mCanShowPromo;
         }
 
+        @Nullable
         View getPromoView() {
             return mPromoView;
         }
@@ -238,8 +242,16 @@
             }
 
             mCanShowPromo = canShowPromo;
+            if (mPromoView == null && mCanShowPromo) {
+                initializePromoView();
+            }
             mCoordinator.updateHeaderViews(mCanShowPromo ? mPromoView : null);
         }
+
+        private void initializePromoView() {
+            mPromoView = mSigninPromoCoordinator.buildPromoView((ViewGroup) mCoordinator.getView());
+            mSigninPromoCoordinator.setView(mPromoView);
+        }
     }
 
     /** Internal implementation of Stream.StreamsMediator. */
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 1125b67..9ee2359 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -2358,7 +2358,7 @@
         if (!TabUiFeatureUtilities.isTabDragToCreateInstanceSupported()) return false;
         @Nullable TabGroupMetadata tabGroupMetadata = IntentHandler.getTabGroupMetadata(intent);
         return tabGroupMetadata != null
-                ? maybeLaunchDraggedTabGroupInWindow(tabGroupMetadata)
+                ? maybeLaunchDraggedTabGroupInWindow(intent, tabGroupMetadata)
                 : maybeLaunchDraggedTabInWindow(intent);
     }
 
@@ -2381,18 +2381,24 @@
         }
         mMultiInstanceManager.moveTabToWindow(this, tab, /* atIndex= */ 0);
         RecordHistogram.recordBooleanHistogram(HISTOGRAM_DRAGGED_TAB_OPENED_NEW_WINDOW, true);
-        DragDropMetricUtils.recordTabDragDropType(
+        DragDropMetricUtils.recordDragDropType(
                 ChromeDragDropUtils.getDragDropTypeFromIntent(intent),
                 AppHeaderUtils.isAppInDesktopWindow(
-                        mRootUiCoordinator.getDesktopWindowStateManager()));
+                        mRootUiCoordinator.getDesktopWindowStateManager()),
+                /* isTabGroup= */ false);
         return true;
     }
 
-    // TODO(crbug.com/384979079): record metrics for tab group drop.
-    private boolean maybeLaunchDraggedTabGroupInWindow(@NonNull TabGroupMetadata tabGroupMetadata) {
+    private boolean maybeLaunchDraggedTabGroupInWindow(
+            Intent intent, @NonNull TabGroupMetadata tabGroupMetadata) {
         if (mMultiInstanceManager == null) return false;
 
         mMultiInstanceManager.moveTabGroupToWindow(this, tabGroupMetadata, /* atIndex= */ 0);
+        DragDropMetricUtils.recordDragDropType(
+                ChromeDragDropUtils.getDragDropTypeFromIntent(intent),
+                AppHeaderUtils.isAppInDesktopWindow(
+                        mRootUiCoordinator.getDesktopWindowStateManager()),
+                /* isTabGroup= */ true);
         return true;
     }
 
@@ -3391,7 +3397,15 @@
             new NtpCustomizationCoordinator(
                             this,
                             mRootUiCoordinator.getBottomSheetController(),
-                            getProfileProviderSupplier())
+                            () -> {
+                                OneshotSupplier<ProfileProvider> profileProviderSupplier =
+                                        getProfileProviderSupplier();
+                                if (profileProviderSupplier.hasValue()) {
+                                    return getProfileProviderSupplier().get().getOriginalProfile();
+                                }
+
+                                return null;
+                            })
                     .showBottomSheet();
             NtpCustomizationMetricsUtils.recordOpenBottomSheetEntry(
                     NtpCustomizationCoordinator.EntryPointType.MAIN_MENU);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/tabmodel/ArchivedTabModelOrchestrator.java b/chrome/android/java/src/org/chromium/chrome/browser/app/tabmodel/ArchivedTabModelOrchestrator.java
index bf6d1d8a..e1cb8522 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/tabmodel/ArchivedTabModelOrchestrator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/tabmodel/ArchivedTabModelOrchestrator.java
@@ -274,8 +274,7 @@
      */
     public void registerTabModelOrchestrator(TabbedModeTabModelOrchestrator orchestrator) {
         mActivityTabModelOrchestrators.add(orchestrator);
-        if (ChromeFeatureList.sAndroidTabDeclutter.isEnabled()
-                && mTabArchiveSettings.getArchiveEnabled()) {
+        if (mTabArchiveSettings.getArchiveEnabled()) {
             // To account for the local tab group sync database synchronizing with the sync service
             // on startup, a delay must be included when initiating a declutter pass that involves
             // archiving tab groups. Unfortunately, there is no particular synchronization step that
@@ -431,7 +430,6 @@
 
     private void doDeclutterPassImpl(TabbedModeTabModelOrchestrator orchestrator) {
         if (!mTabArchiveSettings.getArchiveEnabled()) return;
-        assert ChromeFeatureList.sAndroidTabDeclutter.isEnabled();
         pauseSaveTabList(orchestrator);
 
         int archiveTimeHours = mTabArchiveSettings.getArchiveTimeDeltaHours();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityBrowserControlsVisibilityManager.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityBrowserControlsVisibilityManager.java
index 8313a16..24120459 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityBrowserControlsVisibilityManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityBrowserControlsVisibilityManager.java
@@ -10,7 +10,6 @@
 import org.chromium.blink.mojom.DisplayMode;
 import org.chromium.cc.input.BrowserControlsState;
 import org.chromium.chrome.browser.browserservices.intents.BrowserServicesIntentDataProvider;
-import org.chromium.chrome.browser.browserservices.intents.WebappExtras;
 import org.chromium.chrome.browser.customtabs.CloseButtonVisibilityManager;
 import org.chromium.chrome.browser.customtabs.content.CustomTabActivityTabProvider;
 import org.chromium.chrome.browser.customtabs.content.TabObserverRegistrar;
@@ -92,10 +91,7 @@
     }
 
     private boolean shouldShowWebAppControls() {
-        WebappExtras webappExtras = mIntentDataProvider.getWebappExtras();
-        return mInAppMode
-                && webappExtras != null
-                && webappExtras.displayMode == DisplayMode.MINIMAL_UI;
+        return mInAppMode && mIntentDataProvider.getResolvedDisplayMode() == DisplayMode.MINIMAL_UI;
     }
 
     /** Should be called when the browser enters and exits TWA mode. */
@@ -150,8 +146,10 @@
             return BrowserControlsState.SHOWN;
         }
 
-        // Fallback to browser controls in fullscreen mode.
-        if (shouldShowWebAppControls() && !mIsInDesktopWindow) {
+        // Fallback to browser controls in fullscreen mode when running WebAPK or shortcut web app.
+        if (mIntentDataProvider.isWebappOrWebApkActivity()
+                && shouldShowWebAppControls()
+                && !mIsInDesktopWindow) {
             return BrowserControlsState.BOTH;
         }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java
index 0ed216fe..1855552 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java
@@ -2067,20 +2067,19 @@
         showContextMenu(stripView);
     }
 
-    /**
-     * Returns {@code true} if a context menu triggered from long-pressing a view is showing. Does
-     * not include the context menu from long-pressing the
-     */
+    /** Returns {@code true} if a context menu triggered from long-pressing a view is showing. */
     private boolean isViewContextMenuShowing() {
         return (mTabGroupContextMenuCoordinator != null
                         && mTabGroupContextMenuCoordinator.isMenuShowing())
                 || (mTabContextMenuCoordinator != null
-                        && mTabContextMenuCoordinator.isMenuShowing());
+                        && mTabContextMenuCoordinator.isMenuShowing())
+                || (mCloseButtonMenu != null && mCloseButtonMenu.isShowing());
     }
 
     private void dismissContextMenu() {
         if (mTabGroupContextMenuCoordinator != null) mTabGroupContextMenuCoordinator.dismiss();
         if (mTabContextMenuCoordinator != null) mTabContextMenuCoordinator.dismiss();
+        if (mCloseButtonMenu != null) mCloseButtonMenu.dismiss();
     }
 
     /**
@@ -2595,6 +2594,9 @@
             return;
         }
 
+        // Don't allow the hovercard to show when any context menu is already showing.
+        if (isViewContextMenuShowing()) return;
+
         int hoveredTabIndex = findIndexForTab(mLastHoveredTab.getTabId());
         mTabHoverCardView.show(
                 mModel.getTabAt(hoveredTabIndex),
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/reorder/TabDragSource.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/reorder/TabDragSource.java
index 9cb632d0..7dd910c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/reorder/TabDragSource.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/reorder/TabDragSource.java
@@ -58,7 +58,7 @@
 import org.chromium.ui.dragdrop.DragDropGlobalState;
 import org.chromium.ui.dragdrop.DragDropGlobalState.TrackerToken;
 import org.chromium.ui.dragdrop.DragDropMetricUtils;
-import org.chromium.ui.dragdrop.DragDropMetricUtils.DragDropTabResult;
+import org.chromium.ui.dragdrop.DragDropMetricUtils.DragDropResult;
 import org.chromium.ui.dragdrop.DragDropMetricUtils.DragDropType;
 import org.chromium.ui.util.XrUtils;
 import org.chromium.ui.widget.Toast;
@@ -387,9 +387,10 @@
                 if (didOccurInTabStrip(dragEvent.getY())) {
                     res = onDrop(dragEvent);
                 } else {
-                    DragDropMetricUtils.recordTabDragDropResult(
-                            DragDropTabResult.IGNORED_TOOLBAR,
-                            AppHeaderUtils.isAppInDesktopWindow(mDesktopWindowStateManager));
+                    DragDropMetricUtils.recordDragDropResult(
+                            DragDropResult.IGNORED_TOOLBAR,
+                            AppHeaderUtils.isAppInDesktopWindow(mDesktopWindowStateManager),
+                            isTabGroupDrop());
                     res = false;
                 }
                 break;
@@ -484,7 +485,8 @@
         StripLayoutHelper helper = mStripLayoutHelperSupplier.get();
         helper.stopReorderMode();
         if (isDragSource()) {
-            DragDropMetricUtils.recordTabReorderStripWithDragDrop(mUmaState.mDragEverLeftStrip);
+            DragDropMetricUtils.recordReorderStripWithDragDrop(
+                    mUmaState.mDragEverLeftStrip, isTabGroupDrop());
             return true;
         }
 
@@ -530,14 +532,14 @@
                     tabIndex,
                     /* isCollapsed= */ false);
         }
-        DragDropMetricUtils.recordTabDragDropType(
+        DragDropMetricUtils.recordDragDropType(
                 DragDropType.TAB_STRIP_TO_TAB_STRIP,
-                AppHeaderUtils.isAppInDesktopWindow(mDesktopWindowStateManager));
+                AppHeaderUtils.isAppInDesktopWindow(mDesktopWindowStateManager),
+                /* isTabGroup= */ false);
         mUmaState.mTabLeavingDestStripSystemElapsedTime = SystemClock.elapsedRealtime();
         return true;
     }
 
-    // TODO(crbug.com/384979079): record metrics for tab group drop.
     private boolean handleGroupDrop(DragEvent dropEvent, StripLayoutHelper helper) {
         @Nullable
         TabGroupMetadata tabGroupMetadata =
@@ -567,6 +569,11 @@
             int tabIndex = helper.getTabIndexForTabDrop(dropEvent.getX() * mPxToDp);
             mMultiInstanceManager.moveTabGroupToWindow(getActivity(), tabGroupMetadata, tabIndex);
         }
+        DragDropMetricUtils.recordDragDropType(
+                DragDropType.TAB_STRIP_TO_TAB_STRIP,
+                AppHeaderUtils.isAppInDesktopWindow(mDesktopWindowStateManager),
+                /* isTabGroup= */ true);
+        mUmaState.mTabLeavingDestStripSystemElapsedTime = SystemClock.elapsedRealtime();
         return true;
     }
 
@@ -583,7 +590,7 @@
                                 - mUmaState.mTabEnteringDestStripSystemElapsedTime;
                 assert duration >= 0
                         : "Duration when the drag is within the destination strip is invalid";
-                DragDropMetricUtils.recordTabDurationWithinDestStrip(duration);
+                DragDropMetricUtils.recordDurationWithinDestStrip(duration, isTabGroupDrop());
             }
             return false;
         }
@@ -607,6 +614,7 @@
         int sourceInstanceId =
                 DragDropGlobalState.getState(sDragTrackerToken).getDragSourceInstance();
 
+        boolean isTabGroupDrop = isTabGroupDrop();
         mStripLayoutHelperSupplier.get().stopReorderMode();
         mHandler.removeCallbacks(mOnDragExitRunnable);
         if (mShadowView != null) {
@@ -622,22 +630,23 @@
 
         // Only record for source strip to avoid duplicate.
         if (dropHandled) {
-            DragDropMetricUtils.recordTabDragDropResult(
-                    DragDropTabResult.SUCCESS,
-                    AppHeaderUtils.isAppInDesktopWindow(mDesktopWindowStateManager));
-            DragDropMetricUtils.recordTabDragDropClosedWindow(didCloseWindow);
+            DragDropMetricUtils.recordDragDropResult(
+                    DragDropResult.SUCCESS,
+                    AppHeaderUtils.isAppInDesktopWindow(mDesktopWindowStateManager),
+                    isTabGroupDrop);
+            DragDropMetricUtils.recordDragDropClosedWindow(didCloseWindow, isTabGroupDrop);
         } else if (MultiWindowUtils.getInstanceCount() == MultiWindowUtils.getMaxInstances()) {
             Toast.makeText(
                             mWindowAndroid.getContext().get(),
                             R.string.max_number_of_windows,
                             Toast.LENGTH_LONG)
                     .show();
-            ChromeDragDropUtils.recordTabDragToCreateInstanceFailureCount();
-            DragDropMetricUtils.recordTabDragDropResult(
-                    DragDropTabResult.IGNORED_MAX_INSTANCES,
-                    AppHeaderUtils.isAppInDesktopWindow(mDesktopWindowStateManager));
+            ChromeDragDropUtils.recordTabOrGroupDragToCreateInstanceFailureCount();
+            DragDropMetricUtils.recordDragDropResult(
+                    DragDropResult.IGNORED_MAX_INSTANCES,
+                    AppHeaderUtils.isAppInDesktopWindow(mDesktopWindowStateManager),
+                    isTabGroupDrop);
         }
-
         return true;
     }
 
@@ -713,6 +722,12 @@
         return tab != null;
     }
 
+    private boolean isTabGroupDrop() {
+        return ChromeDragDropUtils.getTabGroupMetadataFromGlobalState(
+                        getDragDropGlobalState(/* dragEvent= */ null))
+                != null;
+    }
+
     public static void setDragTrackerTokenForTesting(TrackerToken token) {
         sDragTrackerToken = token;
         ResettersForTesting.register(() -> sDragTrackerToken = null);
@@ -735,11 +750,14 @@
      * @param tabTitle The title of the tab that cannot be moved. If null, no toast is shown.
      * @return {@code true} if the toast was shown, {@code false} otherwise.
      */
-    // TODO(crbug.com/384979079): Record metrics.
     private boolean disallowDragWithMhtmlTab(Context context, @Nullable String tabTitle) {
         if (tabTitle == null) return false;
         String text = context.getString(R.string.tab_cannot_be_moved, tabTitle);
         Toast.makeText(context, text, Toast.LENGTH_LONG).show();
+        DragDropMetricUtils.recordDragDropResult(
+                DragDropResult.IGNORED_MHTML_TAB,
+                AppHeaderUtils.isAppInDesktopWindow(mDesktopWindowStateManager),
+                /* isTabGroup= */ true);
         return true;
     }
 
@@ -841,6 +859,10 @@
         return tabModelSelector != null && tabModelSelector.getTotalTabCount() > 1;
     }
 
+    void createUmaStateForTesting() {
+        mUmaState = new DragLocalUmaState();
+    }
+
     View getShadowViewForTesting() {
         return mShadowView;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/OMADownloadHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/download/OMADownloadHandler.java
index b6b4011..043a7f9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/OMADownloadHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/OMADownloadHandler.java
@@ -321,11 +321,7 @@
             try {
                 ParcelFileDescriptor fd = null;
                 if (isContentUri) {
-                    int fileDescriptor =
-                            ContentUriUtils.openContentUri(mDownloadInfo.getFilePath(), "r");
-                    if (fileDescriptor > 0) {
-                        fd = ParcelFileDescriptor.fromFd(fileDescriptor);
-                    }
+                    fd = ContentUriUtils.openContentUri(mDownloadInfo.getFilePath(), "r");
                 } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
                     fd = manager.openDownloadedFile(mDownloadId);
                 } else {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/dragdrop/ChromeDragDropUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/dragdrop/ChromeDragDropUtils.java
index 8fcdd64..ed51300e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/dragdrop/ChromeDragDropUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/dragdrop/ChromeDragDropUtils.java
@@ -27,37 +27,43 @@
 
 /** Utility class for Chrome drag and drop implementations. */
 public class ChromeDragDropUtils {
-    private static final int MAX_TAB_TEARING_FAILURE_COUNT_PER_DAY = 10;
+    private static final int MAX_TAB_OR_GROUP_TEARING_FAILURE_COUNT_PER_DAY = 10;
 
     /**
-     * Records linear histogram Android.DragDrop.Tab.MaxInstanceFailureCount and saves related
-     * SharedPreferences values.
+     * Records linear histogram Android.DragDrop.TabOrGroup.MaxInstanceFailureCount and saves
+     * related SharedPreferences values.
      */
-    public static void recordTabDragToCreateInstanceFailureCount() {
+    public static void recordTabOrGroupDragToCreateInstanceFailureCount() {
         var prefs = ChromeSharedPreferences.getInstance();
-        // Check the failure count in a day for every unhandled dragged tab drop when max instances
+        // Check the failure count in a day for every unhandled dragged tab or group drop when max
+        // instances
         // are open.
         long timestamp =
                 prefs.readLong(
-                        ChromePreferenceKeys.TAB_TEARING_MAX_INSTANCES_FAILURE_START_TIME_MS, 0);
+                        ChromePreferenceKeys
+                                .TAB_OR_GROUP_TEARING_MAX_INSTANCES_FAILURE_START_TIME_MS,
+                        0);
         int failureCount =
-                prefs.readInt(ChromePreferenceKeys.TAB_TEARING_MAX_INSTANCES_FAILURE_COUNT, 0);
+                prefs.readInt(
+                        ChromePreferenceKeys.TAB_OR_GROUP_TEARING_MAX_INSTANCES_FAILURE_COUNT, 0);
         long current = System.currentTimeMillis();
 
         boolean isNewDay = timestamp == 0 || current - timestamp > DateUtils.DAY_IN_MILLIS;
         if (isNewDay) {
             prefs.writeLong(
-                    ChromePreferenceKeys.TAB_TEARING_MAX_INSTANCES_FAILURE_START_TIME_MS, current);
+                    ChromePreferenceKeys.TAB_OR_GROUP_TEARING_MAX_INSTANCES_FAILURE_START_TIME_MS,
+                    current);
             // Reset the count to 0 if it is the start of the next 24-hour period.
             failureCount = 0;
         }
 
         RecordHistogram.recordExactLinearHistogram(
-                "Android.DragDrop.Tab.MaxInstanceFailureCount",
+                "Android.DragDrop.TabOrGroup.MaxInstanceFailureCount",
                 failureCount + 1,
-                MAX_TAB_TEARING_FAILURE_COUNT_PER_DAY + 1);
+                MAX_TAB_OR_GROUP_TEARING_FAILURE_COUNT_PER_DAY + 1);
         prefs.writeInt(
-                ChromePreferenceKeys.TAB_TEARING_MAX_INSTANCES_FAILURE_COUNT, failureCount + 1);
+                ChromePreferenceKeys.TAB_OR_GROUP_TEARING_MAX_INSTANCES_FAILURE_COUNT,
+                failureCount + 1);
     }
 
     /**
@@ -71,7 +77,8 @@
         return switch (intent.getIntExtra(
                 IntentHandler.EXTRA_URL_DRAG_SOURCE, UrlIntentSource.UNKNOWN)) {
             case UrlIntentSource.LINK -> DragDropType.LINK_TO_NEW_INSTANCE;
-            case UrlIntentSource.TAB_IN_STRIP -> DragDropType.TAB_STRIP_TO_NEW_INSTANCE;
+            case UrlIntentSource.TAB_IN_STRIP, UrlIntentSource.TAB_GROUP_IN_STRIP -> DragDropType
+                    .TAB_STRIP_TO_NEW_INSTANCE;
             default -> DragDropType.UNKNOWN_TO_NEW_INSTANCE;
         };
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/dragdrop/ChromeTabbedOnDragListener.java b/chrome/android/java/src/org/chromium/chrome/browser/dragdrop/ChromeTabbedOnDragListener.java
index da86b96c..afcb83a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/dragdrop/ChromeTabbedOnDragListener.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/dragdrop/ChromeTabbedOnDragListener.java
@@ -25,7 +25,7 @@
 import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.dragdrop.DragDropGlobalState;
 import org.chromium.ui.dragdrop.DragDropMetricUtils;
-import org.chromium.ui.dragdrop.DragDropMetricUtils.DragDropTabResult;
+import org.chromium.ui.dragdrop.DragDropMetricUtils.DragDropResult;
 import org.chromium.ui.dragdrop.DragDropMetricUtils.DragDropType;
 
 /**
@@ -79,22 +79,23 @@
                 // This is to prevent tab switcher from receiving drops. We might support dropping
                 // into tab switcher in the future, but this should still be retained to prevent
                 // dropping happens on top of tab switcher toolbar.
+                boolean isTabGroupDrop =
+                        clipDescription.hasMimeType(MimeTypeUtils.CHROME_MIMETYPE_TAB_GROUP);
                 if (mLayoutStateProviderSupplier.get() == null
                         || mLayoutStateProviderSupplier
                                 .get()
                                 .isLayoutVisible(LayoutType.TAB_SWITCHER)) {
-                    DragDropMetricUtils.recordTabDragDropResult(
-                            DragDropTabResult.IGNORED_TAB_SWITCHER, isInDesktopWindow);
+                    DragDropMetricUtils.recordDragDropResult(
+                            DragDropResult.IGNORED_TAB_SWITCHER, isInDesktopWindow, isTabGroupDrop);
                     return false;
                 }
                 if (clipDescription == null) return false;
-                if (clipDescription.hasMimeType(MimeTypeUtils.CHROME_MIMETYPE_TAB)) {
+                if (isTabGroupDrop) {
+                    return handleGroupDrop(dragEvent, isInDesktopWindow);
+                } else {
+                    assert clipDescription.hasMimeType(MimeTypeUtils.CHROME_MIMETYPE_TAB);
                     return handleTabDrop(dragEvent, isInDesktopWindow);
                 }
-                if (clipDescription.hasMimeType(MimeTypeUtils.CHROME_MIMETYPE_TAB_GROUP)) {
-                    return handleGroupDrop(dragEvent);
-                }
-                return false;
         }
         return false;
     }
@@ -103,13 +104,17 @@
         DragDropGlobalState globalState = DragDropGlobalState.getState(dragEvent);
         Tab draggedTab = ChromeDragDropUtils.getTabFromGlobalState(globalState);
         if (globalState == null || draggedTab == null) {
-            DragDropMetricUtils.recordTabDragDropResult(
-                    DragDropTabResult.ERROR_TAB_NOT_FOUND, isInDesktopWindow);
+            DragDropMetricUtils.recordDragDropResult(
+                    DragDropResult.ERROR_CONTENT_NOT_FOUND,
+                    isInDesktopWindow,
+                    /* isTabGroup= */ false);
             return false;
         }
         if (globalState.isDragSourceInstance(mMultiInstanceManager.getCurrentInstanceId())) {
-            DragDropMetricUtils.recordTabDragDropResult(
-                    DragDropTabResult.IGNORED_SAME_INSTANCE, isInDesktopWindow);
+            DragDropMetricUtils.recordDragDropResult(
+                    DragDropResult.IGNORED_SAME_INSTANCE,
+                    isInDesktopWindow,
+                    /* isTabGroup= */ false);
             return false;
         }
 
@@ -129,20 +134,27 @@
         // Reparent the dragged tab to the destination window.
         mMultiInstanceManager.moveTabToWindow(
                 mWindowAndroid.getActivity().get(), draggedTab, destIndex);
-        DragDropMetricUtils.recordTabDragDropType(
-                DragDropType.TAB_STRIP_TO_CONTENT, isInDesktopWindow);
+        DragDropMetricUtils.recordDragDropType(
+                DragDropType.TAB_STRIP_TO_CONTENT, isInDesktopWindow, /* isTabGroup= */ false);
         return true;
     }
 
-    // TODO(crbug.com/384979079): record metrics for tab group drop.
-    private boolean handleGroupDrop(DragEvent dragEvent) {
+    private boolean handleGroupDrop(DragEvent dragEvent, boolean isInDesktopWindow) {
         DragDropGlobalState globalState = DragDropGlobalState.getState(dragEvent);
         TabGroupMetadata tabGroupMetadata =
                 ChromeDragDropUtils.getTabGroupMetadataFromGlobalState(globalState);
         if (globalState == null || tabGroupMetadata == null) {
+            DragDropMetricUtils.recordDragDropResult(
+                    DragDropResult.ERROR_CONTENT_NOT_FOUND,
+                    isInDesktopWindow,
+                    /* isTabGroup= */ true);
             return false;
         }
         if (globalState.isDragSourceInstance(mMultiInstanceManager.getCurrentInstanceId())) {
+            DragDropMetricUtils.recordDragDropResult(
+                    DragDropResult.IGNORED_SAME_INSTANCE,
+                    isInDesktopWindow,
+                    /* isTabGroup= */ true);
             return false;
         }
 
@@ -157,6 +169,8 @@
         // Reparent the dragged tab group to destination window.
         mMultiInstanceManager.moveTabGroupToWindow(
                 mWindowAndroid.getActivity().get(), tabGroupMetadata, destIndex);
+        DragDropMetricUtils.recordDragDropType(
+                DragDropType.TAB_STRIP_TO_CONTENT, isInDesktopWindow, /* isTabGroup= */ true);
         return true;
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/MainSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/settings/MainSettings.java
index 867c3b4..8bf468a8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/MainSettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/settings/MainSettings.java
@@ -63,7 +63,6 @@
 import org.chromium.chrome.browser.sync.settings.ManageSyncSettings;
 import org.chromium.chrome.browser.sync.settings.SignInPreference;
 import org.chromium.chrome.browser.sync.settings.SyncSettingsUtils;
-import org.chromium.chrome.browser.tab_group_sync.TabGroupSyncFeatures;
 import org.chromium.chrome.browser.toolbar.ToolbarPositionController;
 import org.chromium.chrome.browser.toolbar.adaptive.AdaptiveToolbarStatePredictor;
 import org.chromium.chrome.browser.toolbar.settings.AddressBarSettingsFragment;
@@ -385,17 +384,7 @@
         updatePlusAddressesPreference();
         updateAddressBarPreference();
         updateAppearancePreference();
-
-        boolean isTabGroupSyncAutoOpenConfigurable =
-                TabGroupSyncFeatures.isTabGroupSyncEnabled(getProfile())
-                        && ChromeFeatureList.isEnabled(
-                                ChromeFeatureList.TAB_GROUP_SYNC_AUTO_OPEN_KILL_SWITCH);
-        if (isTabGroupSyncAutoOpenConfigurable
-                || ChromeFeatureList.isEnabled(ChromeFeatureList.ANDROID_TAB_DECLUTTER)) {
-            addPreferenceIfAbsent(PREF_TABS);
-        } else {
-            removePreferenceIfPresent(PREF_TABS);
-        }
+        addPreferenceIfAbsent(PREF_TABS);
 
         Preference homepagePref = addPreferenceIfAbsent(PREF_HOMEPAGE);
         setOnOffSummary(homepagePref, HomepageManager.getInstance().isHomepageEnabled());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/AccountManagementFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/AccountManagementFragment.java
index 60d55bc..14c5ced2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/AccountManagementFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/AccountManagementFragment.java
@@ -144,11 +144,16 @@
     }
 
     @Override
+    public void onStart() {
+        super.onStart();
+        update();
+    }
+
+    @Override
     public void onResume() {
         super.onResume();
         IdentityServicesProvider.get().getSigninManager(getProfile()).addSignInStateObserver(this);
         mProfileDataCache.addObserver(this);
-        update();
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/GoogleServicesSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/GoogleServicesSettings.java
index df2cbaf..c049b08 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/GoogleServicesSettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/GoogleServicesSettings.java
@@ -179,8 +179,8 @@
     }
 
     @Override
-    public void onResume() {
-        super.onResume();
+    public void onStart() {
+        super.onStart();
         updatePreferences();
     }
 
@@ -303,4 +303,9 @@
             }
         };
     }
+
+    @Override
+    public @AnimationType int getAnimationType() {
+        return AnimationType.PROPERTY;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/ManageSyncSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/ManageSyncSettings.java
index ef59a74..882ced0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/ManageSyncSettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/ManageSyncSettings.java
@@ -555,6 +555,13 @@
         super.onStart();
         mSyncService.addSyncStateChangedListener(this);
         IdentityServicesProvider.get().getIdentityManager(getProfile()).addObserver(this);
+
+        // This is necessary to refresh the batch upload card if the user leaves Chrome open on the
+        // settings screen, changes their screen lock settings, and then returns to Chrome.
+        if (mShouldReplaceSyncSettingsWithAccountSettings) {
+            mBatchUploadCardPreference.hideBatchUploadCardAndUpdate();
+        }
+        updateSyncPreferences();
     }
 
     @Override
@@ -565,17 +572,6 @@
     }
 
     @Override
-    public void onResume() {
-        super.onResume();
-        // This is necessary to refresh the batch upload card if the user leaves Chrome open on the
-        // settings screen, changes their screen lock settings, and then returns to Chrome.
-        if (mShouldReplaceSyncSettingsWithAccountSettings) {
-            mBatchUploadCardPreference.hideBatchUploadCardAndUpdate();
-        }
-        updateSyncPreferences();
-    }
-
-    @Override
     public boolean onPreferenceChange(Preference preference, Object newValue) {
         if (mSyncService.isUsingExplicitPassphrase()
                 && mShouldReplaceSyncSettingsWithAccountSettings
@@ -1203,4 +1199,9 @@
     private void finishCurrentSettings() {
         SettingsNavigationFactory.createSettingsNavigation().finishCurrentSettings(this);
     }
+
+    @Override
+    public @AnimationType int getAnimationType() {
+        return AnimationType.PROPERTY;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/PersonalizeGoogleServicesSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/PersonalizeGoogleServicesSettings.java
index a0a5ab1..6504929 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/PersonalizeGoogleServicesSettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/PersonalizeGoogleServicesSettings.java
@@ -56,11 +56,6 @@
     public void onStart() {
         super.onStart();
         mSyncService.addSyncStateChangedListener(this);
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
         updatePreferences();
     }
 
@@ -106,4 +101,9 @@
                 .openLinkedGoogleServicesSettings(getActivity(), signedInAccountName);
         RecordUserAction.record("Signin_AccountSettings_LinkedGoogleServicesClicked");
     }
+
+    @Override
+    public @AnimationType int getAnimationType() {
+        return AnimationType.PROPERTY;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabArchiveSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabArchiveSettings.java
index 078e5678..1cd7f9a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabArchiveSettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabArchiveSettings.java
@@ -27,6 +27,14 @@
         void onSettingChanged();
     }
 
+    // The default time to consider a tab as inactive.
+    static final int DEFAULT_ARCHIVE_TIME_HOURS = 21 * 24; // 21 days.
+    // The default time interval between same-session declutter passes.
+    private static final int DEFAULT_DECLUTTER_INTERVAL_TIME_HOURS = 7 * 24; // 7 days.
+    // The default allowable times we can show an IPH.
+    private static final int DEFAULT_ALLOWED_IPH_SHOWS = 3;
+    // The default max simultaneous archives to allow in a single pass.
+    static final int DEFAULT_MAX_SIMULTANEOUS_ARCHIVES = 150;
     private static boolean sIphShownThisSession;
 
     /** Sets whether the iph was shown this session. */
@@ -92,10 +100,7 @@
         // are created, and tabs disappearing from tests is very unexpected. For archive tests,
         // this will need to be turned on manually.
         return mPrefsManager.readBoolean(
-                ChromePreferenceKeys.TAB_DECLUTTER_ARCHIVE_ENABLED,
-                BuildConfig.IS_FOR_TEST
-                        ? false
-                        : ChromeFeatureList.sAndroidTabDeclutterArchiveEnabled.getValue());
+                ChromePreferenceKeys.TAB_DECLUTTER_ARCHIVE_ENABLED, !BuildConfig.IS_FOR_TEST);
     }
 
     /** Sets whether archive is enabled in settings. */
@@ -107,17 +112,12 @@
     public int getArchiveTimeDeltaHours() {
         return mPrefsManager.readInt(
                 ChromePreferenceKeys.TAB_DECLUTTER_ARCHIVE_TIME_DELTA_HOURS,
-                ChromeFeatureList.sAndroidTabDeclutterArchiveTimeDeltaHours.getValue());
+                DEFAULT_ARCHIVE_TIME_HOURS);
     }
 
     /** Similar to above, but the return value is in days. */
     public int getArchiveTimeDeltaDays() {
-        return (int)
-                TimeUnit.HOURS.toDays(
-                        mPrefsManager.readInt(
-                                ChromePreferenceKeys.TAB_DECLUTTER_ARCHIVE_TIME_DELTA_HOURS,
-                                ChromeFeatureList.sAndroidTabDeclutterArchiveTimeDeltaHours
-                                        .getValue()));
+        return (int) TimeUnit.HOURS.toDays(getArchiveTimeDeltaHours());
     }
 
     /** Sets the time delta (in hours) used to determine if a tab is eligible for archive. */
@@ -136,7 +136,7 @@
         return getArchiveEnabled()
                 && mPrefsManager.readBoolean(
                         ChromePreferenceKeys.TAB_DECLUTTER_AUTO_DELETE_ENABLED,
-                        ChromeFeatureList.sAndroidTabDeclutterAutoDeleteEnabled.getValue());
+                        ChromeFeatureList.sAndroidTabDeclutterAutoDelete.isEnabled());
     }
 
     /** Sets whether auto deletion for archived tabs is enabled in settings. */
@@ -169,12 +169,7 @@
 
     /** Similar to above, but the return value is in days. */
     public int getAutoDeleteTimeDeltaDays() {
-        return (int)
-                TimeUnit.HOURS.toDays(
-                        mPrefsManager.readInt(
-                                ChromePreferenceKeys.TAB_DECLUTTER_AUTO_DELETE_TIME_DELTA_HOURS,
-                                ChromeFeatureList.sAndroidTabDeclutterAutoDeleteTimeDeltaHours
-                                        .getValue()));
+        return (int) TimeUnit.HOURS.toDays(getAutoDeleteTimeDeltaHours());
     }
 
     /** Sets the time delta used to determine if an archived tab is eligible for auto deletion. */
@@ -185,13 +180,13 @@
 
     /** Returns the interval to perform declutter in hours. */
     public int getDeclutterIntervalTimeDeltaHours() {
-        return ChromeFeatureList.sAndroidTabDeclutterIntervalTimeDeltaHours.getValue();
+        return DEFAULT_DECLUTTER_INTERVAL_TIME_HOURS;
     }
 
     /** Returns whether the dialog iph should be shown. */
     public boolean shouldShowDialogIph() {
         return mPrefsManager.readInt(ChromePreferenceKeys.TAB_DECLUTTER_DIALOG_IPH_DISMISS_COUNT, 0)
-                < ChromeFeatureList.sAndroidTabDeclutterIphMessageDismissThreshold.getValue();
+                < DEFAULT_ALLOWED_IPH_SHOWS;
     }
 
     /** Sets whether the dialog iph should be shown. */
@@ -205,10 +200,11 @@
     public void setShouldShowDialogIphForTesting(boolean shouldShow) {
         mPrefsManager.writeInt(
                 ChromePreferenceKeys.TAB_DECLUTTER_DIALOG_IPH_DISMISS_COUNT,
-                shouldShow
-                        ? 0
-                        : ChromeFeatureList.sAndroidTabDeclutterIphMessageDismissThreshold
-                                .getValue());
+                shouldShow ? 0 : DEFAULT_ALLOWED_IPH_SHOWS);
+    }
+
+    public int getMaxSimultaneousArchives() {
+        return DEFAULT_MAX_SIMULTANEOUS_ARCHIVES;
     }
 
     // Private methods.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabArchiverImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabArchiverImpl.java
index 6c17814a..b9442c6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabArchiverImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabArchiverImpl.java
@@ -154,8 +154,7 @@
         // Maps unique tab group tokens to the eligibility of that group.
         Map<Token, Boolean> tabGroupIdToArchiveEligibilityMap = new HashMap<>();
 
-        int maxSimultaneousArchives =
-                ChromeFeatureList.sAndroidTabDeclutterMaxSimultaneousArchives.getValue();
+        int maxSimultaneousArchives = mTabArchiveSettings.getMaxSimultaneousArchives();
         for (int i = 0; i < model.getCount(); i++) {
             // TODO(crbug.com/369845089): Investigate a more graceful fix to
             // batch these so all relevant tabs still get archived in the same
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedAppMenuPropertiesDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedAppMenuPropertiesDelegate.java
index de654a6..1b6dfab 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedAppMenuPropertiesDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedAppMenuPropertiesDelegate.java
@@ -491,7 +491,8 @@
     /**
      * @return Whether the "New window" menu item should be displayed.
      */
-    protected boolean shouldShowNewWindow() {
+    @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
+    public boolean shouldShowNewWindow() {
         // Hide the menu on automotive devices.
         if (BuildInfo.getInstance().isAutomotive) return false;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tracing/settings/DeveloperSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/tracing/settings/DeveloperSettings.java
index 2cbfbb9..7b9c3f3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tracing/settings/DeveloperSettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tracing/settings/DeveloperSettings.java
@@ -64,4 +64,9 @@
     public ObservableSupplier<String> getPageTitle() {
         return mPageTitle;
     }
+
+    @Override
+    public @AnimationType int getAnimationType() {
+        return AnimationType.PROPERTY;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tracing/settings/TracingCategoriesSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/tracing/settings/TracingCategoriesSettings.java
index 6080ea2..6d735a4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tracing/settings/TracingCategoriesSettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tracing/settings/TracingCategoriesSettings.java
@@ -125,4 +125,9 @@
             pref.callChangeListener(pref.isChecked());
         }
     }
+
+    @Override
+    public @AnimationType int getAnimationType() {
+        return AnimationType.PROPERTY;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tracing/settings/TracingSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/tracing/settings/TracingSettings.java
index 9e852f4..02e07d4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tracing/settings/TracingSettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tracing/settings/TracingSettings.java
@@ -232,9 +232,14 @@
     }
 
     @Override
+    public void onStart() {
+        super.onStart();
+        updatePreferences();
+    }
+
+    @Override
     public void onResume() {
         super.onResume();
-        updatePreferences();
         TracingController.getInstance().addObserver(this);
     }
 
@@ -302,4 +307,9 @@
             mPrefTracingStatus.setTitle(MSG_ACTIVE_SUMMARY);
         }
     }
+
+    @Override
+    public @AnimationType int getAnimationType() {
+        return AnimationType.PROPERTY;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappIntentDataProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappIntentDataProvider.java
index cf1c67b..001df853 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappIntentDataProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappIntentDataProvider.java
@@ -193,22 +193,20 @@
     }
 
     private @DisplayMode.EnumType int resolveDisplayMode() {
-        if (mWebappExtras.displayMode == DisplayMode.FULLSCREEN) {
-            return DisplayMode.FULLSCREEN;
+        if (mWebappExtras.displayMode == DisplayMode.BROWSER) {
+            // `browser` display mode web apps are not installable by default, because by the spec
+            // they should be opened in a new tab or browser window, but in Chrome they can be
+            // forcefully installed via app menu. In this case display mode should resolve to the
+            // first supported display mode in the "fullscreen -> standalone -> minimal-ui ->
+            // browser" fallback chain.
+            if (WebAppHeaderUtils.isMinimalUiFlagEnabled()) {
+                return DisplayMode.MINIMAL_UI;
+            } else {
+                return DisplayMode.STANDALONE;
+            }
         }
 
-        // `browser` display mode web apps are not installable by default, because by the spec they
-        // should be opened in a new tab or browser window, but in Chrome they can be forcefully
-        // installed via app menu. In this case display mode should resolve to the first supported
-        // display mode in the "fullscreen -> standalone -> minimal-ui -> browser" fallback chain.
-        boolean shouldUseMinimalUi =
-                mWebappExtras.displayMode == DisplayMode.MINIMAL_UI
-                        || mWebappExtras.displayMode == DisplayMode.BROWSER;
-        if (WebAppHeaderUtils.isMinimalUiFlagEnabled() && shouldUseMinimalUi) {
-            return DisplayMode.MINIMAL_UI;
-        }
-
-        return DisplayMode.STANDALONE;
+        return mWebappExtras.displayMode;
     }
 
     private static final class ColorProviderImpl implements ColorProvider {
diff --git a/chrome/android/javatests/BUILD.gn b/chrome/android/javatests/BUILD.gn
index 60e5785..1f53e5d8 100644
--- a/chrome/android/javatests/BUILD.gn
+++ b/chrome/android/javatests/BUILD.gn
@@ -512,6 +512,7 @@
   sources = [
     "//chrome/android/java/src/org/chromium/chrome/browser/app/feed/NavigationRecorderTest.java",
     "src/org/chromium/chrome/browser/app/ContextMenuDragTest.java",
+    "src/org/chromium/chrome/browser/app/appmenu/MultiWindowAppMenuTest.java",
     "src/org/chromium/chrome/browser/app/appmenu/OverviewAppMenuTest.java",
     "src/org/chromium/chrome/browser/app/appmenu/TabbedAppMenuPTTest.java",
     "src/org/chromium/chrome/browser/app/appmenu/TabbedAppMenuTest.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/TabsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/TabsTest.java
index 2de53dd..1dd85c47 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/TabsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/TabsTest.java
@@ -868,10 +868,7 @@
     @Test
     @MediumTest
     @Feature({"Android-TabSwitcher"})
-    @DisableFeatures({
-        ChromeFeatureList.ANDROID_TAB_DECLUTTER,
-        ChromeFeatureList.ANDROID_TAB_DECLUTTER_RESCUE_KILLSWITCH
-    })
+    @DisableFeatures({ChromeFeatureList.ANDROID_TAB_DECLUTTER_RESCUE_KILLSWITCH})
     public void testIncognitoTabsNotRestoredAfterSwipe() throws Exception {
         mActivityTestRule.loadUrl(getUrl(TEST_PAGE_FILE_PATH));
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/app/appmenu/MultiWindowAppMenuTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/app/appmenu/MultiWindowAppMenuTest.java
new file mode 100644
index 0000000..15495ed2
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/app/appmenu/MultiWindowAppMenuTest.java
@@ -0,0 +1,69 @@
+// 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.app.appmenu;
+
+import static org.junit.Assert.assertNotEquals;
+
+import androidx.test.filters.LargeTest;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.transit.Station;
+import org.chromium.base.test.transit.TransitAsserts;
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.DoNotBatch;
+import org.chromium.base.test.util.Restriction;
+import org.chromium.chrome.browser.flags.ChromeSwitches;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.chrome.test.transit.ChromeTransitTestRules;
+import org.chromium.chrome.test.transit.FreshCtaTransitTestRule;
+import org.chromium.chrome.test.transit.ntp.IncognitoNewTabPageStation;
+import org.chromium.chrome.test.transit.ntp.RegularNewTabPageStation;
+import org.chromium.chrome.test.transit.page.WebPageStation;
+import org.chromium.ui.base.DeviceFormFactor;
+import org.chromium.ui.test.util.DeviceRestriction;
+
+/** Public Transit tests for operations through the app menu in multi-window. */
+@RunWith(ChromeJUnit4ClassRunner.class)
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+@DoNotBatch(reason = "Batching not yet supported in multi-window")
+// In phones, the New Window option in the app menu is only enabled when already in multi-window or
+// multi-display mode with Chrome not running in an adjacent window.
+@Restriction({DeviceFormFactor.TABLET, DeviceRestriction.RESTRICTION_TYPE_NON_AUTO})
+public class MultiWindowAppMenuTest {
+    @Rule
+    public FreshCtaTransitTestRule mCtaTestRule =
+            ChromeTransitTestRules.freshChromeTabbedActivityRule();
+
+    @Test
+    @LargeTest
+    public void testOpenNewWindow_fromWebPage() {
+        WebPageStation pageInFirstWindow = mCtaTestRule.startOnBlankPage();
+        RegularNewTabPageStation pageInSecondWindow =
+                pageInFirstWindow.openRegularTabAppMenu().openNewWindow();
+
+        assertInDifferentWindows(pageInFirstWindow, pageInSecondWindow);
+        TransitAsserts.assertFinalDestinations(pageInFirstWindow, pageInSecondWindow);
+    }
+
+    @Test
+    @LargeTest
+    public void testOpenNewWindow_fromIncognitoNtp() {
+        IncognitoNewTabPageStation pageInFirstWindow =
+                mCtaTestRule.startOnBlankPage().openNewIncognitoTabFast();
+        RegularNewTabPageStation pageInSecondWindow =
+                pageInFirstWindow.openAppMenu().openNewWindow();
+
+        assertInDifferentWindows(pageInFirstWindow, pageInSecondWindow);
+        TransitAsserts.assertFinalDestinations(pageInFirstWindow, pageInSecondWindow);
+    }
+
+    static void assertInDifferentWindows(Station<?> station1, Station<?> station2) {
+        assertNotEquals(station1.getActivity(), station2.getActivity());
+        assertNotEquals(station1.getActivity().getWindow(), station2.getActivity().getWindow());
+    }
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/app/tabmodel/ArchivedTabModelOrchestratorTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/app/tabmodel/ArchivedTabModelOrchestratorTest.java
index 5b1a52c..9a9b177 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/app/tabmodel/ArchivedTabModelOrchestratorTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/app/tabmodel/ArchivedTabModelOrchestratorTest.java
@@ -72,10 +72,7 @@
 @RunWith(ChromeJUnit4ClassRunner.class)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
 @DoNotBatch(reason = "Test interacts with activity shutdown and thus is incompatible with batching")
-@EnableFeatures({
-    ChromeFeatureList.ANDROID_TAB_DECLUTTER,
-    ChromeFeatureList.ANDROID_TAB_DECLUTTER_RESCUE_KILLSWITCH
-})
+@EnableFeatures({ChromeFeatureList.ANDROID_TAB_DECLUTTER_RESCUE_KILLSWITCH})
 @DisableFeatures({
     ChromeFeatureList.ANDROID_TAB_DECLUTTER_ARCHIVE_ALL_BUT_ACTIVE,
     ChromeFeatureList.ANDROID_TAB_DECLUTTER_ARCHIVE_TAB_GROUPS
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/app/tabmodel/ArchivedTabsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/app/tabmodel/ArchivedTabsTest.java
index a00cda0..afd26338 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/app/tabmodel/ArchivedTabsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/app/tabmodel/ArchivedTabsTest.java
@@ -46,10 +46,7 @@
 @RunWith(ChromeJUnit4ClassRunner.class)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
 @DoNotBatch(reason = "Test interacts with activity shutdown and thus is incompatible with batching")
-@EnableFeatures({
-    ChromeFeatureList.ANDROID_TAB_DECLUTTER,
-    ChromeFeatureList.ANDROID_TAB_DECLUTTER_RESCUE_KILLSWITCH
-})
+@EnableFeatures({ChromeFeatureList.ANDROID_TAB_DECLUTTER_RESCUE_KILLSWITCH})
 @DisableFeatures(ChromeFeatureList.ANDROID_TAB_DECLUTTER_ARCHIVE_ALL_BUT_ACTIVE)
 public class ArchivedTabsTest {
     private static class FakeDeferredStartupHandler extends DeferredStartupHandler {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/paint_preview/StartupPaintPreviewHelperTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/paint_preview/StartupPaintPreviewHelperTest.java
index f160c42..3226323b 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/paint_preview/StartupPaintPreviewHelperTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/paint_preview/StartupPaintPreviewHelperTest.java
@@ -17,10 +17,8 @@
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.CriteriaHelper;
 import org.chromium.base.test.util.DisabledTest;
-import org.chromium.base.test.util.Features.DisableFeatures;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.base.ColdStartTracker;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.metrics.SimpleStartupForegroundSessionDetector;
 import org.chromium.chrome.browser.paint_preview.services.PaintPreviewTabServiceFactory;
@@ -96,7 +94,6 @@
      */
     @Test
     @MediumTest
-    @DisableFeatures({ChromeFeatureList.ANDROID_TAB_DECLUTTER})
     @DisabledTest(message = "Pending revival. See crbug.com/333779543.")
     public void testDisplayOnStartup() throws ExecutionException {
         mActivityTestRule.startMainActivityWithURL(
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/settings/MainSettingsFragmentTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/settings/MainSettingsFragmentTest.java
index b232359..e37782d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/settings/MainSettingsFragmentTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/settings/MainSettingsFragmentTest.java
@@ -820,58 +820,13 @@
 
     @Test
     @SmallTest
-    @EnableFeatures({
-        ChromeFeatureList.TAB_GROUP_SYNC_ANDROID,
-        ChromeFeatureList.TAB_GROUP_SYNC_AUTO_OPEN_KILL_SWITCH
-    })
-    @DisableFeatures(ChromeFeatureList.ANDROID_TAB_DECLUTTER)
-    public void testTabsSettingsOn_GroupSync_KillSwitchInactive() {
+    public void testTabsSettingsOn() {
         startSettings();
         assertSettingsExists(MainSettings.PREF_TABS, TabsSettings.class);
     }
 
     @Test
     @SmallTest
-    @EnableFeatures(ChromeFeatureList.TAB_GROUP_SYNC_ANDROID)
-    @DisableFeatures({
-        ChromeFeatureList.ANDROID_TAB_DECLUTTER,
-        ChromeFeatureList.TAB_GROUP_SYNC_AUTO_OPEN_KILL_SWITCH
-    })
-    public void testTabsSettingsOn_GroupSync_KillSwitchActive() {
-        startSettings();
-        Assert.assertNull(
-                "Tabs settings should not be shown",
-                mMainSettings.findPreference(MainSettings.PREF_TABS));
-    }
-
-    @Test
-    @SmallTest
-    @DisableFeatures({
-        ChromeFeatureList.TAB_GROUP_SYNC_ANDROID,
-        ChromeFeatureList.TAB_GROUP_SYNC_AUTO_OPEN_KILL_SWITCH
-    })
-    @EnableFeatures(ChromeFeatureList.ANDROID_TAB_DECLUTTER)
-    public void testTabsSettingsOn_Declutter() {
-        startSettings();
-        assertSettingsExists(MainSettings.PREF_TABS, TabsSettings.class);
-    }
-
-    @Test
-    @SmallTest
-    @DisableFeatures({
-        ChromeFeatureList.ANDROID_TAB_DECLUTTER,
-        ChromeFeatureList.TAB_GROUP_SYNC_ANDROID
-    })
-    @EnableFeatures(ChromeFeatureList.TAB_GROUP_SYNC_AUTO_OPEN_KILL_SWITCH)
-    public void testTabsSettingsOff() {
-        startSettings();
-        Assert.assertNull(
-                "Tabs settings should not be shown",
-                mMainSettings.findPreference(MainSettings.PREF_TABS));
-    }
-
-    @Test
-    @SmallTest
     @EnableFeatures(ChromeFeatureList.SAFETY_HUB)
     public void testSafetyHubFlagOn() {
         startSettings();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninFirstRunFragmentTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninFirstRunFragmentTest.java
index e535ac2..05cd1da 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninFirstRunFragmentTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninFirstRunFragmentTest.java
@@ -1111,13 +1111,6 @@
             sdk_is_greater_than = Build.VERSION_CODES.S_V2,
             message = "Flaky, crbug.com/358148764")
     public void testFragmentSigninWhenAddedAccountIsNotYetAvailable() {
-        final String continueAsText =
-                mActivityTestRule
-                        .getActivity()
-                        .getString(
-                                R.string.sync_promo_continue_as,
-                                TestAccounts.TEST_ACCOUNT_NO_NAME.getEmail());
-
         // This will freeze AccountManagerFacade with the currently available list of accounts.
         // The added account from add account flow later on will not be available.
         try (var ignored =
@@ -1126,15 +1119,19 @@
             onView(withText(R.string.signin_add_account_to_device)).perform(click());
             mSigninTestRule.setAddAccountFlowResult(TestAccounts.TEST_ACCOUNT_NO_NAME);
             onViewWaiting(AccountManagerTestRule.ADD_ACCOUNT_BUTTON_MATCHER).perform(click());
-            checkFragmentWithSelectedAccount(TestAccounts.TEST_ACCOUNT_NO_NAME);
 
-            clickContinueButton(continueAsText);
-
-            // The click on continue button should be a no-op.
-            verify(mFirstRunPageDelegateMock, never()).advanceToNextPage();
-            checkFragmentWithSelectedAccount(TestAccounts.TEST_ACCOUNT_NO_NAME);
+            // The account is not visible and thus add account button is shown.
+            onView(withText(R.string.signin_add_account_to_device)).check(matches(isDisplayed()));
         }
+
         // Allow account list update and the continue button starts sign-in.
+        checkFragmentWithSelectedAccount(TestAccounts.TEST_ACCOUNT_NO_NAME);
+        String continueAsText =
+                mActivityTestRule
+                        .getActivity()
+                        .getString(
+                                R.string.sync_promo_continue_as,
+                                TestAccounts.TEST_ACCOUNT_NO_NAME.getEmail());
         clickContinueButton(continueAsText);
         verify(mFirstRunPageDelegateMock, timeout(CriteriaHelper.DEFAULT_MAX_TIME_TO_POLL))
                 .advanceToNextPage();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/TabArchiverTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/TabArchiverTest.java
index bb0eb095..56ed93e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/TabArchiverTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/TabArchiverTest.java
@@ -75,10 +75,7 @@
 @RunWith(ChromeJUnit4ClassRunner.class)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
 @Batch(Batch.PER_CLASS)
-@EnableFeatures({
-    ChromeFeatureList.ANDROID_TAB_DECLUTTER,
-    ChromeFeatureList.ANDROID_TAB_DECLUTTER_RESCUE_KILLSWITCH
-})
+@EnableFeatures({ChromeFeatureList.ANDROID_TAB_DECLUTTER_RESCUE_KILLSWITCH})
 @DisableFeatures({ChromeFeatureList.ANDROID_TAB_DECLUTTER_ARCHIVE_TAB_GROUPS})
 public class TabArchiverTest {
     @ClassRule
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/tab_restore/HistoricalTabSaverImplTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/tab_restore/HistoricalTabSaverImplTest.java
index 85e3e3e..a001907 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/tab_restore/HistoricalTabSaverImplTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/tab_restore/HistoricalTabSaverImplTest.java
@@ -21,11 +21,8 @@
 import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.CriteriaHelper;
-import org.chromium.base.test.util.Features.DisableFeatures;
-import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.app.tabmodel.ArchivedTabModelOrchestrator;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.ntp.RecentlyClosedBulkEvent;
 import org.chromium.chrome.browser.ntp.RecentlyClosedEntry;
@@ -63,7 +60,6 @@
     ChromeSwitches.DISABLE_STARTUP_PROMOS
 })
 @Batch(Batch.PER_CLASS)
-@DisableFeatures(ChromeFeatureList.ANDROID_TAB_DECLUTTER)
 public class HistoricalTabSaverImplTest {
     private static final String TEST_PAGE_1 = "/chrome/test/data/android/about.html";
     private static final String TEST_PAGE_2 = "/chrome/test/data/android/simple.html";
@@ -435,7 +431,6 @@
 
     @Test
     @MediumTest
-    @EnableFeatures(ChromeFeatureList.ANDROID_TAB_DECLUTTER)
     public void testArchivedTabsAreExcluded() {
         ArchivedTabModelOrchestrator archivedTabModelOrchestrator =
                 runOnUiThreadBlocking(
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/ArchivedTabCreatorTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/ArchivedTabCreatorTest.java
index 253c7aab..b46d5b8 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/ArchivedTabCreatorTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/ArchivedTabCreatorTest.java
@@ -42,10 +42,7 @@
 @RunWith(ChromeJUnit4ClassRunner.class)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
 @Batch(Batch.PER_CLASS)
-@EnableFeatures({
-    ChromeFeatureList.ANDROID_TAB_DECLUTTER,
-    ChromeFeatureList.ANDROID_TAB_DECLUTTER_RESCUE_KILLSWITCH
-})
+@EnableFeatures({ChromeFeatureList.ANDROID_TAB_DECLUTTER_RESCUE_KILLSWITCH})
 public class ArchivedTabCreatorTest {
     @ClassRule
     public static ChromeTabbedActivityTestRule sActivityTestRule =
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/app/tabmodel/TabPersistentStoreIntegrationTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/app/tabmodel/TabPersistentStoreIntegrationTest.java
index 9c5446c..2216138 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/app/tabmodel/TabPersistentStoreIntegrationTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/app/tabmodel/TabPersistentStoreIntegrationTest.java
@@ -85,7 +85,6 @@
 @LooperMode(Mode.PAUSED)
 @EnableFeatures({ChromeFeatureList.PROCESS_RANK_POLICY_ANDROID})
 @DisableFeatures({
-    ChromeFeatureList.ANDROID_TAB_DECLUTTER,
     ChromeFeatureList.ANDROID_TAB_DECLUTTER_RESCUE_KILLSWITCH,
     ChromeFeatureList.CHANGE_UNFOCUSED_PRIORITY
 })
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityBrowserControlsVisibilityManagerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityBrowserControlsVisibilityManagerTest.java
index 9419ea8..5b22af1 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityBrowserControlsVisibilityManagerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityBrowserControlsVisibilityManagerTest.java
@@ -4,6 +4,8 @@
 
 package org.chromium.chrome.browser.browserservices.trustedwebactivityui.controller;
 
+import static androidx.browser.customtabs.CustomTabsIntent.COLOR_SCHEME_LIGHT;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -16,7 +18,19 @@
 import static org.mockito.Mockito.verifyNoInteractions;
 import static org.mockito.Mockito.when;
 
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
 import android.graphics.Rect;
+import android.os.Build;
+import android.view.ContextThemeWrapper;
+
+import androidx.browser.customtabs.CustomTabsIntent;
+import androidx.browser.customtabs.CustomTabsSession;
+import androidx.browser.customtabs.TrustedWebUtils;
+import androidx.browser.trusted.TrustedWebActivityDisplayMode;
+import androidx.browser.trusted.TrustedWebActivityIntentBuilder;
+import androidx.test.core.app.ApplicationProvider;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -29,14 +43,19 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.blink.mojom.DisplayMode;
 import org.chromium.build.annotations.Nullable;
 import org.chromium.cc.input.BrowserControlsState;
+import org.chromium.chrome.R;
 import org.chromium.chrome.browser.browserservices.intents.BrowserServicesIntentDataProvider;
 import org.chromium.chrome.browser.customtabs.CloseButtonVisibilityManager;
+import org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider;
 import org.chromium.chrome.browser.customtabs.content.CustomTabActivityTabProvider;
 import org.chromium.chrome.browser.customtabs.content.TabObserverRegistrar;
 import org.chromium.chrome.browser.customtabs.features.toolbar.CustomTabToolbarCoordinator;
+import org.chromium.chrome.browser.document.ChromeLauncherActivity;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.test.util.browser.webapps.WebApkIntentDataProviderBuilder;
 import org.chromium.components.browser_ui.desktop_windowing.AppHeaderState;
@@ -64,6 +83,7 @@
     @Mock TrustedWebActivityBrowserControlsVisibilityManager mController;
 
     private @Nullable AppHeaderState mAppHeaderState;
+    private Context mContext;
 
     @Before
     public void setUp() {
@@ -71,6 +91,11 @@
         when(mTabProvider.getTab()).thenReturn(mTab);
         doReturn(Tab.INVALID_TAB_ID).when(mTab).getParentId();
         setTabSecurityLevel(ConnectionSecurityLevel.NONE);
+
+        mContext =
+                new ContextThemeWrapper(
+                        ApplicationProvider.getApplicationContext(),
+                        R.style.Theme_BrowserUI_DayNight);
     }
 
     /** Browser controls should be shown for pages with certificate errors. */
@@ -127,6 +152,8 @@
     }
 
     @Test
+    @Config(sdk = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    @EnableFeatures({ChromeFeatureList.ANDROID_MINIMAL_UI_LARGE_SCREEN})
     public void testMinimalUiExitDesktopWindowingInAppMode_ShowBrowserControls() {
         setupDesktopWindowing(/* isInDesktopWindow= */ true);
         mController = buildController(buildWebApkIntentDataProvider(DisplayMode.MINIMAL_UI));
@@ -134,11 +161,16 @@
         setupDesktopWindowing(/* isInDesktopWindow= */ false);
 
         mController.onDesktopWindowingModeChanged(mAppHeaderState.isInDesktopWindow());
-        assertEquals(BrowserControlsState.BOTH, getLastBrowserControlsState());
+        assertEquals(
+                "Browser controls should be shown",
+                BrowserControlsState.BOTH,
+                getLastBrowserControlsState());
         assertFalse("Close button should be hidden in minimal ui", getLastCloseButtonVisibility());
     }
 
     @Test
+    @Config(sdk = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    @EnableFeatures({ChromeFeatureList.ANDROID_MINIMAL_UI_LARGE_SCREEN})
     public void testMinUiExitDwAndEnterAppMode_KeepBrowserControlsHidden() {
         // navigate out of scope in DW
         mController = buildController(buildWebApkIntentDataProvider(DisplayMode.MINIMAL_UI));
@@ -196,7 +228,12 @@
     /** Browser controls should not be shown for TWAs while in TWA mode. */
     @Test
     public void testTwa() {
-        mController = buildController(mock(BrowserServicesIntentDataProvider.class));
+        var intent = buildTwaIntent();
+        intent.putExtra(
+                TrustedWebActivityIntentBuilder.EXTRA_DISPLAY_MODE,
+                new TrustedWebActivityDisplayMode.DefaultMode().toBundle());
+        mController = buildController(buildCustomTabIntentProvider(intent));
+
         mController.updateIsInAppMode(true);
         assertEquals(BrowserControlsState.HIDDEN, getLastBrowserControlsState());
     }
@@ -207,8 +244,65 @@
         mController = buildController(mock(BrowserServicesIntentDataProvider.class));
         mController.updateIsInAppMode(true);
         mController.updateIsInAppMode(false);
-        assertEquals(BrowserControlsState.BOTH, getLastBrowserControlsState());
-        assertTrue(getLastCloseButtonVisibility());
+        assertEquals(
+                "Browser controls should be visible",
+                BrowserControlsState.BOTH,
+                getLastBrowserControlsState());
+        assertTrue("Close button should be visible", getLastCloseButtonVisibility());
+    }
+
+    @Test
+    public void testTwaMinimalUi_KeepBrowserControlsHidden() {
+        var intent = buildTwaIntent();
+        intent.putExtra(
+                TrustedWebActivityIntentBuilder.EXTRA_DISPLAY_MODE,
+                new TrustedWebActivityDisplayMode.MinimalUiMode().toBundle());
+        mController = buildController(buildCustomTabIntentProvider(intent));
+
+        mController.updateIsInAppMode(true);
+        assertEquals(
+                "Browser controls should be hidden",
+                BrowserControlsState.HIDDEN,
+                getLastBrowserControlsState());
+        assertTrue(
+                "Close button should be visible for future layout", getLastCloseButtonVisibility());
+    }
+
+    @Test
+    @EnableFeatures({ChromeFeatureList.ANDROID_MINIMAL_UI_LARGE_SCREEN})
+    public void testTwaMinimalUiEnterDesktopWindowing_KeepBrowserControlsHidden() {
+        setupDesktopWindowing(/* isInDesktopWindow= */ false);
+        var intent = buildTwaIntent();
+        intent.putExtra(
+                TrustedWebActivityIntentBuilder.EXTRA_DISPLAY_MODE,
+                new TrustedWebActivityDisplayMode.MinimalUiMode().toBundle());
+        mController = buildController(buildCustomTabIntentProvider(intent));
+        mController.updateIsInAppMode(true);
+        setupDesktopWindowing(/* isInDesktopWindow= */ true);
+
+        mController.onDesktopWindowingModeChanged(mAppHeaderState.isInDesktopWindow());
+        assertEquals(
+                "Browser controls should be hidden",
+                BrowserControlsState.HIDDEN,
+                getLastBrowserControlsState());
+        assertTrue(
+                "Close button should be visible for future layout", getLastCloseButtonVisibility());
+    }
+
+    @Test
+    @EnableFeatures({ChromeFeatureList.ANDROID_MINIMAL_UI_LARGE_SCREEN})
+    public void testTwaMinimalUiEnterDesktopWindowingNotInAppMode_DoNotUpdateAnything() {
+        setupDesktopWindowing(/* isInDesktopWindow= */ false);
+        var intent = buildTwaIntent();
+        intent.putExtra(
+                TrustedWebActivityIntentBuilder.EXTRA_DISPLAY_MODE,
+                new TrustedWebActivityDisplayMode.MinimalUiMode().toBundle());
+        mController = buildController(buildCustomTabIntentProvider(intent));
+        mController.updateIsInAppMode(false);
+        setupDesktopWindowing(/* isInDesktopWindow= */ true);
+
+        mController.onDesktopWindowingModeChanged(mAppHeaderState.isInDesktopWindow());
+        verifyNoInteractions(mToolbarCoordinator);
     }
 
     private void setTabSecurityLevel(int securityLevel) {
@@ -222,6 +316,19 @@
                 .build();
     }
 
+    private Intent buildTwaIntent() {
+        CustomTabsSession session =
+                CustomTabsSession.createMockSessionForTesting(
+                        new ComponentName(mContext, ChromeLauncherActivity.class));
+        var intent = new CustomTabsIntent.Builder(session).build().intent;
+        intent.putExtra(TrustedWebUtils.EXTRA_LAUNCH_AS_TRUSTED_WEB_ACTIVITY, true);
+        return intent;
+    }
+
+    private CustomTabIntentDataProvider buildCustomTabIntentProvider(Intent intent) {
+        return new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
+    }
+
     private TrustedWebActivityBrowserControlsVisibilityManager buildController(
             BrowserServicesIntentDataProvider intentDataProvider) {
         return spy(
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java
index 7660983e..e93cbd1 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java
@@ -18,6 +18,7 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.mock;
@@ -206,6 +207,7 @@
     @Mock private ServiceStatus mServiceStatus;
     @Mock private DataSharingUIDelegate mDataSharingUiDelegate;
     @Mock private Bitmap mAvatarBitmap;
+    @Mock private TintedCompositorButton mCloseButton;
     @Mock TabStripIphController mController;
     @Captor private ArgumentCaptor<DataSharingService.Observer> mSharingObserverCaptor;
     @Captor private ArgumentCaptor<Callback<Boolean>> mSharedImageTilesCaptor;
@@ -2152,6 +2154,93 @@
     @Test
     @EnableFeatures({ChromeFeatureList.TAB_STRIP_CONTEXT_MENU})
     @Feature("Tab Context Menu")
+    public void testTabContextMenu_PreventsHovercard() {
+        // Setup.
+        initializeTabHoverTest();
+        mStripLayoutHelper.setTabContextMenuCoordinatorForTesting(mTabContextMenuCoordinator);
+        when(mTabContextMenuCoordinator.isMenuShowing()).thenReturn(true);
+
+        // Now try to hover on the tab.
+        mStripLayoutHelper.updateLastHoveredTab(
+                mStripLayoutHelper.getStripLayoutTabsForTesting()[0]);
+
+        verify(mTabHoverCardView, never())
+                .show(
+                        nullable(Tab.class),
+                        anyBoolean(),
+                        anyFloat(),
+                        anyFloat(),
+                        anyFloat(),
+                        anyFloat());
+    }
+
+    @Test
+    @EnableFeatures({ChromeFeatureList.TAB_STRIP_CONTEXT_MENU})
+    @Feature("Tab Group Context Menu")
+    public void testTabGroupContextMenu_PreventsHovercard() {
+        // Setup.
+        initializeTabHoverTest();
+        mStripLayoutHelper.setTabGroupContextMenuCoordinatorForTesting(
+                mTabGroupContextMenuCoordinator);
+        when(mTabGroupContextMenuCoordinator.isMenuShowing()).thenReturn(true);
+
+        // Now try to hover on the tab.
+        mStripLayoutHelper.updateLastHoveredTab(
+                mStripLayoutHelper.getStripLayoutTabsForTesting()[0]);
+
+        verify(mTabHoverCardView, never())
+                .show(
+                        nullable(Tab.class),
+                        anyBoolean(),
+                        anyFloat(),
+                        anyFloat(),
+                        anyFloat(),
+                        anyFloat());
+    }
+
+    @Test
+    @EnableFeatures({ChromeFeatureList.TAB_STRIP_CONTEXT_MENU})
+    @Feature("Advanced Peripherals Support")
+    public void testCloseTabsContextMenu_PreventsHovercard() {
+        // Set up: see testOnLongPress_OnCloseButton setup.
+        initializeTest(false, false, 0);
+        StripLayoutTab[] tabs = getMockedStripLayoutTabs(150f);
+        mStripLayoutHelper.setStripLayoutTabsForTesting(tabs);
+
+        // Mock tab's view.
+        View tabView = new View(mActivity);
+        tabView.setLayoutParams(new MarginLayoutParams(150, 50));
+        when(mModel.getTabAt(1).getView()).thenReturn(tabView);
+
+        // Long press on second tab's close button.
+        StripLayoutTab tab = tabs[1];
+        when(tab.checkCloseHitTest(anyFloat(), anyFloat())).thenReturn(true);
+        when(tab.getCloseButton()).thenReturn(mCloseButton);
+        when(mCloseButton.getParentView()).thenReturn(tab);
+        when(mCloseButton.getType()).thenReturn(ButtonType.TAB_CLOSE);
+        mStripLayoutHelper.setTabAtPositionForTesting(tab);
+        mStripLayoutHelper.onLongPress(150f, 0f);
+
+        assertTrue(
+                "Expected 'close all tabs' context menu to be showing",
+                mStripLayoutHelper.isCloseButtonMenuShowingForTesting());
+
+        // Now try to hover on the tab.
+        mStripLayoutHelper.updateLastHoveredTab(tab);
+
+        verify(mTabHoverCardView, never())
+                .show(
+                        nullable(Tab.class),
+                        anyBoolean(),
+                        anyFloat(),
+                        anyFloat(),
+                        anyFloat(),
+                        anyFloat());
+    }
+
+    @Test
+    @EnableFeatures({ChromeFeatureList.TAB_STRIP_CONTEXT_MENU})
+    @Feature("Tab Context Menu")
     public void testBottomSheet_constructedWithoutDestroyHide() {
         var tabs = initializeTest_ForTab();
         setupForContextMenu();
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/reorder/TabDragSourceTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/reorder/TabDragSourceTest.java
index 2e9bffed..b0027418 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/reorder/TabDragSourceTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/reorder/TabDragSourceTest.java
@@ -98,7 +98,7 @@
 import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.dragdrop.DragAndDropDelegate;
 import org.chromium.ui.dragdrop.DragDropGlobalState;
-import org.chromium.ui.dragdrop.DragDropMetricUtils.DragDropTabResult;
+import org.chromium.ui.dragdrop.DragDropMetricUtils.DragDropResult;
 import org.chromium.ui.dragdrop.DragDropMetricUtils.DragDropType;
 import org.chromium.ui.dragdrop.DropDataAndroid;
 import org.chromium.ui.util.XrUtils;
@@ -248,6 +248,7 @@
                         mTabStripHeightSupplier,
                         mDesktopWindowStateManager);
         mDestInstance.setTabModelSelector(mTabModelSelector);
+        mSourceInstance.createUmaStateForTesting();
 
         when(mSourceMultiInstanceManager.closeChromeWindowIfEmpty(anyInt())).thenReturn(false);
 
@@ -265,9 +266,9 @@
         ShadowToast.reset();
         ToastManager.resetForTesting();
         mSharedPreferencesManager.removeKey(
-                ChromePreferenceKeys.TAB_TEARING_MAX_INSTANCES_FAILURE_START_TIME_MS);
+                ChromePreferenceKeys.TAB_OR_GROUP_TEARING_MAX_INSTANCES_FAILURE_START_TIME_MS);
         mSharedPreferencesManager.removeKey(
-                ChromePreferenceKeys.TAB_TEARING_MAX_INSTANCES_FAILURE_COUNT);
+                ChromePreferenceKeys.TAB_OR_GROUP_TEARING_MAX_INSTANCES_FAILURE_COUNT);
         XrUtils.resetXrDeviceForTesting();
     }
 
@@ -574,18 +575,7 @@
     /** Test for tab drag {@link #ONDRAG_TEST_CASES} - Scenario A */
     @Test
     public void test_onDrag_dropInStrip_source() {
-        HistogramWatcher histogramExpectation =
-                HistogramWatcher.newBuilder()
-                        .expectIntRecord(
-                                "Android.DragDrop.Tab.FromStrip.Result", DragDropTabResult.SUCCESS)
-                        .expectNoRecords("Android.DragDrop.Tab.FromStrip.Result.DesktopWindow")
-                        .expectNoRecords("Android.DragDrop.Tab.Type")
-                        .expectNoRecords("Android.DragDrop.Tab.Type.DesktopWindow")
-                        .expectBooleanRecord("Android.DragDrop.Tab.ReorderStripWithDragDrop", false)
-                        .expectNoRecords("Android.DragDrop.Tab.Duration.WithinDestStrip")
-                        .build();
         doTestOnDragDropInStripSource(/* isGroupDrag= */ false);
-        histogramExpectation.assertExpected();
     }
 
     /** Test for tab group drag {@link #ONDRAG_TEST_CASES} - Scenario A */
@@ -597,19 +587,7 @@
     /** Test for tab drag {@link #ONDRAG_TEST_CASES} - Scenario B */
     @Test
     public void test_onDrag_dropInToolbarContainer_source() {
-        HistogramWatcher histogramExpectation =
-                HistogramWatcher.newBuilder()
-                        .expectIntRecord(
-                                "Android.DragDrop.Tab.FromStrip.Result",
-                                DragDropTabResult.IGNORED_TOOLBAR)
-                        .expectNoRecords("Android.DragDrop.Tab.FromStrip.Result.DesktopWindow")
-                        .expectNoRecords("Android.DragDrop.Tab.Type")
-                        .expectNoRecords("Android.DragDrop.Tab.Type.DesktopWindow")
-                        .expectNoRecords("Android.DragDrop.Tab.ReorderStripWithDragDrop")
-                        .expectNoRecords("Android.DragDrop.Tab.Duration.WithinDestStrip")
-                        .build();
         doTestOnDragDropInToolbarContainerSource(/* isGroupDrag= */ false);
-        histogramExpectation.assertExpected();
     }
 
     /** Test for tab group drag {@link #ONDRAG_TEST_CASES} - Scenario B */
@@ -619,8 +597,8 @@
     }
 
     /**
-     * Test for tab drag {@link #ONDRAG_TEST_CASES} - Scenario C.1 - XR-specific flow that we
-     * currently does not support moving tab groups.
+     * Test for tab drag {@link #ONDRAG_TEST_CASES} - Scenario C.1 - XR-specific flow currently does
+     * not support moving tab groups.
      */
     @Test
     public void test_onDrag_dropOutsideToolbarContainer() {
@@ -657,8 +635,8 @@
     }
 
     /**
-     * Test for {@link #ONDRAG_TEST_CASES} - Scenario C.2 - XR-specific flow that we currently does
-     * not support moving tab groups.
+     * Test for {@link #ONDRAG_TEST_CASES} - Scenario C.2 - XR-specific flow currently does not
+     * support moving tab groups.
      */
     @Test
     public void test_onDrag_dropOutsideToolbarContainer_dragAsWindow() {
@@ -709,21 +687,45 @@
     }
 
     @Test
+    public void test_onDrag_unhandledDropOutside_maxChromeInstances_tabGroup_desktopWindow() {
+        doTestUnhandledDropOutsideWithMaxInstances(
+                /* isInDesktopWindow= */ true, /* isGroupDrag= */ true);
+    }
+
+    @Test
     public void test_onDrag_multipleUnhandledDropsOutside_maxChromeInstances() {
         MultiWindowUtils.setInstanceCountForTesting(5);
         MultiWindowUtils.setMaxInstancesForTesting(5);
 
         // Simulate failures on day 1.
-        doTriggerUnhandledDrop(4);
+        doTriggerUnhandledDrop(4, /* isGroupDrag= */ false);
 
         // Force update the count start time saved in SharedPreferences for day 1 to restart count
         // for next day.
         mSharedPreferencesManager.writeLong(
-                ChromePreferenceKeys.TAB_TEARING_MAX_INSTANCES_FAILURE_START_TIME_MS,
+                ChromePreferenceKeys.TAB_OR_GROUP_TEARING_MAX_INSTANCES_FAILURE_START_TIME_MS,
                 System.currentTimeMillis() - DateUtils.DAY_IN_MILLIS - 1);
 
         // Simulate a failure on day 2.
-        doTriggerUnhandledDrop(1);
+        doTriggerUnhandledDrop(1, /* isGroupDrag= */ false);
+    }
+
+    @Test
+    public void test_onDrag_multipleUnhandledDropsOutside_maxChromeInstances_tabGroup() {
+        MultiWindowUtils.setInstanceCountForTesting(5);
+        MultiWindowUtils.setMaxInstancesForTesting(5);
+
+        // Simulate failures on day 1.
+        doTriggerUnhandledDrop(4, /* isGroupDrag= */ true);
+
+        // Force update the count start time saved in SharedPreferences for day 1 to restart count
+        // for next day.
+        mSharedPreferencesManager.writeLong(
+                ChromePreferenceKeys.TAB_OR_GROUP_TEARING_MAX_INSTANCES_FAILURE_START_TIME_MS,
+                System.currentTimeMillis() - DateUtils.DAY_IN_MILLIS - 1);
+
+        // Simulate a failure on day 2.
+        doTriggerUnhandledDrop(1, /* isGroupDrag= */ true);
     }
 
     /** Test for Tab Drag {@link #ONDRAG_TEST_CASES} - Scenario D.1 */
@@ -740,7 +742,7 @@
     @Test
     public void test_onDrag_dropInStrip_destination_tabGroup() {
         doTestDropInStripDestination(
-                /* isInDesktopWindow= */ true,
+                /* isInDesktopWindow= */ false,
                 /* isGroupDrag= */ true,
                 /* isGroupShared= */ false,
                 /* mhtmlTabTitle= */ null);
@@ -760,6 +762,12 @@
     /** Test for Tab Group Drag {@link #ONDRAG_TEST_CASES} - Scenario D.1 */
     @Test
     public void test_onDrag_dropInStrip_hasMhtmlTab_destination_tabGroup() {
+        HistogramWatcher.Builder builder =
+                HistogramWatcher.newBuilder()
+                        .expectIntRecord(
+                                "Android.DragDrop.TabGroup.FromStrip.Result",
+                                DragDropResult.IGNORED_MHTML_TAB);
+        HistogramWatcher histogramExpectation = builder.build();
         String url = "file:///example.mhtml";
         Uri uri = Uri.parse(url);
         GURL gurl = new GURL(uri.toString());
@@ -777,6 +785,8 @@
                 /* isGroupDrag= */ true,
                 /* isGroupShared= */ false,
                 /* mhtmlTabTitle= */ mhtmlTabTitle);
+        // Verify histogram recorded for ignored mhtml tab group .
+        histogramExpectation.assertExpected();
     }
 
     /** Test for Desktop Window {@link #ONDRAG_TEST_CASES} - Scenario D.1 */
@@ -789,22 +799,20 @@
                 /* mhtmlTabTitle= */ null);
     }
 
+    /** Test for Tab Group Drag in Desktop Window {@link #ONDRAG_TEST_CASES} - Scenario D.1 */
+    @Test
+    public void test_onDrag_dropInStrip_destination_tabGroup_desktopWindow() {
+        doTestDropInStripDestination(
+                /* isInDesktopWindow= */ true,
+                /* isGroupDrag= */ true,
+                /* isGroupShared= */ false,
+                /* mhtmlTabTitle= */ null);
+    }
+
     /** Test for Tab Drag {@link #ONDRAG_TEST_CASES} - Scenario D.2 */
     @Test
     public void test_onDrag_dropInStrip_differentModel_destination() {
-        HistogramWatcher histogramExpectation =
-                HistogramWatcher.newBuilder()
-                        .expectIntRecord(
-                                "Android.DragDrop.Tab.FromStrip.Result", DragDropTabResult.SUCCESS)
-                        .expectNoRecords("Android.DragDrop.Tab.FromStrip.Result.DesktopWindow")
-                        .expectIntRecord(
-                                "Android.DragDrop.Tab.Type", DragDropType.TAB_STRIP_TO_TAB_STRIP)
-                        .expectNoRecords("Android.DragDrop.Tab.Type.DesktopWindow")
-                        .expectNoRecords("Android.DragDrop.Tab.ReorderStripWithDragDrop")
-                        .expectAnyRecord("Android.DragDrop.Tab.Duration.WithinDestStrip")
-                        .build();
         doTestDropInDestinationDifferentModel(/* isGroupDrag= */ false);
-        histogramExpectation.assertExpected();
     }
 
     /** Test for Tab Group Drag {@link #ONDRAG_TEST_CASES} - Scenario D.2 */
@@ -814,8 +822,8 @@
     }
 
     /**
-     * Test for {@link #ONDRAG_TEST_CASES} - Scenario D.3 - XR-specific flow that we currently does
-     * not support moving tab groups.
+     * Test for {@link #ONDRAG_TEST_CASES} - Scenario D.3 - XR-specific flow currently does not
+     * support moving tab groups.
      */
     @Test
     public void test_onDrag_dropInStrip_withDragAsWindowFF_destination() {
@@ -832,19 +840,7 @@
     /** Test for Tab Drag {@link #ONDRAG_TEST_CASES} - Scenario E */
     @Test
     public void test_onDrag_dropInToolbarContainer_destination() {
-        HistogramWatcher histogramExpectation =
-                HistogramWatcher.newBuilder()
-                        .expectIntRecord(
-                                "Android.DragDrop.Tab.FromStrip.Result",
-                                DragDropTabResult.IGNORED_TOOLBAR)
-                        .expectNoRecords("Android.DragDrop.Tab.FromStrip.Result.DesktopWindow")
-                        .expectNoRecords("Android.DragDrop.Tab.Type")
-                        .expectNoRecords("Android.DragDrop.Tab.Type.DesktopWindow")
-                        .expectNoRecords("Android.DragDrop.Tab.ReorderStripWithDragDrop")
-                        .expectAnyRecord("Android.DragDrop.Tab.Duration.WithinDestStrip")
-                        .build();
         doTestDropInDestinationToolbarContainer(/* isGroupDrag= */ false);
-        histogramExpectation.assertExpected();
     }
 
     /** Test for Tab Group Drag {@link #ONDRAG_TEST_CASES} - Scenario E */
@@ -856,18 +852,7 @@
     /** Test for Tab Drag {@link #ONDRAG_TEST_CASES} - Scenario F */
     @Test
     public void test_onDrag_exitIntoToolbarAndRenterStripAndDrop_source() {
-        HistogramWatcher histogramExpectation =
-                HistogramWatcher.newBuilder()
-                        .expectIntRecord(
-                                "Android.DragDrop.Tab.FromStrip.Result", DragDropTabResult.SUCCESS)
-                        .expectNoRecords("Android.DragDrop.Tab.FromStrip.Result.DesktopWindow")
-                        .expectNoRecords("Android.DragDrop.Tab.Type")
-                        .expectNoRecords("Android.DragDrop.Tab.Type.DesktopWindow")
-                        .expectBooleanRecord("Android.DragDrop.Tab.ReorderStripWithDragDrop", true)
-                        .expectNoRecords("Android.DragDrop.Tab.Duration.WithinDestStrip")
-                        .build();
         doTestExitIntoSourceToolbarAndRenterStripAndDrop(/* isGroupDrag= */ false);
-        histogramExpectation.assertExpected();
     }
 
     /** Test for Tab Group Drag {@link #ONDRAG_TEST_CASES} - Scenario F */
@@ -1046,6 +1031,27 @@
     }
 
     private void doTestOnDragDropInStripSource(boolean isGroupDrag) {
+        String resultHistogram =
+                String.format(
+                        "Android.DragDrop.%s.FromStrip.Result", isGroupDrag ? "TabGroup" : "Tab");
+        String reorderHistogram =
+                String.format(
+                        "Android.DragDrop.%s.ReorderStripWithDragDrop",
+                        isGroupDrag ? "TabGroup" : "Tab");
+        HistogramWatcher histogramExpectation =
+                HistogramWatcher.newBuilder()
+                        .expectIntRecord(resultHistogram, DragDropResult.SUCCESS)
+                        .expectBooleanRecord(reorderHistogram, false)
+                        .expectNoRecords("Android.DragDrop.TabGroup.FromStrip.Result.DesktopWindow")
+                        .expectNoRecords("Android.DragDrop.TabGroup.Type")
+                        .expectNoRecords("Android.DragDrop.TabGroup.Type.DesktopWindow")
+                        .expectNoRecords("Android.DragDrop.TabGroup.Duration.WithinDestStrip")
+                        .expectNoRecords("Android.DragDrop.Tab.FromStrip.Result.DesktopWindow")
+                        .expectNoRecords("Android.DragDrop.Tab.Type")
+                        .expectNoRecords("Android.DragDrop.Tab.Type.DesktopWindow")
+                        .expectNoRecords("Android.DragDrop.Tab.Duration.WithinDestStrip")
+                        .build();
+
         new DragEventInvoker(isGroupDrag, /* isGroupShared= */ false)
                 .drop(mSourceInstance)
                 .end(true);
@@ -1060,9 +1066,28 @@
         verifyViewNotMovedToWindow(isGroupDrag);
         // Verify destination strip not invoked.
         verifyNoInteractions(mDestStripLayoutHelper);
+        // Verify histograms.
+        histogramExpectation.assertExpected();
     }
 
     private void doTestOnDragDropInToolbarContainerSource(boolean isGroupDrag) {
+        String resultHistogram =
+                String.format(
+                        "Android.DragDrop.%s.FromStrip.Result", isGroupDrag ? "TabGroup" : "Tab");
+        HistogramWatcher histogramExpectation =
+                HistogramWatcher.newBuilder()
+                        .expectIntRecord(resultHistogram, DragDropResult.IGNORED_TOOLBAR)
+                        .expectNoRecords("Android.DragDrop.Tab.FromStrip.Result.DesktopWindow")
+                        .expectNoRecords("Android.DragDrop.Tab.Type")
+                        .expectNoRecords("Android.DragDrop.Tab.Type.DesktopWindow")
+                        .expectNoRecords("Android.DragDrop.Tab.ReorderStripWithDragDrop")
+                        .expectNoRecords("Android.DragDrop.Tab.Duration.WithinDestStrip")
+                        .expectNoRecords("Android.DragDrop.TabGroup.FromStrip.Result.DesktopWindow")
+                        .expectNoRecords("Android.DragDrop.TabGroup.Type")
+                        .expectNoRecords("Android.DragDrop.TabGroup.Type.DesktopWindow")
+                        .expectNoRecords("Android.DragDrop.TabGroup.ReorderStripWithDragDrop")
+                        .expectNoRecords("Android.DragDrop.TabGroup.Duration.WithinDestStrip")
+                        .build();
         new DragEventInvoker(isGroupDrag, /* isGroupShared= */ false)
                 // Drag our of strip but within toolbar container.
                 .dragLocationY(mSourceInstance, 3 * DRAG_MOVE_DISTANCE)
@@ -1083,6 +1108,8 @@
         verify(mSourceStripLayoutHelper, times(1)).stopReorderMode();
         // Verify destination strip not invoked.
         verifyNoInteractions(mDestStripLayoutHelper);
+        // Verify histograms.
+        histogramExpectation.assertExpected();
     }
 
     private void verifyDropOutsideToolbarContainerAsWindow() {
@@ -1108,11 +1135,15 @@
 
     private void doTestUnhandledDropOutsideWithMaxInstances(
             boolean isInDesktopWindow, boolean isGroupDrag) {
+        String resultHistogram =
+                String.format(
+                        "Android.DragDrop.%s.FromStrip.Result", isGroupDrag ? "TabGroup" : "Tab");
+
         HistogramWatcher.Builder builder =
                 HistogramWatcher.newBuilder()
                         .expectIntRecord(
                                 "Android.DragDrop.Tab.FromStrip.Result",
-                                DragDropTabResult.IGNORED_MAX_INSTANCES)
+                                DragDropResult.IGNORED_MAX_INSTANCES)
                         .expectNoRecords("Android.DragDrop.Tab.Type")
                         .expectNoRecords("Android.DragDrop.Tab.Type.DesktopWindow")
                         .expectNoRecords("Android.DragDrop.Tab.ReorderStripWithDragDrop")
@@ -1121,10 +1152,9 @@
         if (isInDesktopWindow) {
             AppHeaderUtils.setAppInDesktopWindowForTesting(true);
             builder.expectIntRecord(
-                    "Android.DragDrop.Tab.FromStrip.Result.DesktopWindow",
-                    DragDropTabResult.IGNORED_MAX_INSTANCES);
+                    resultHistogram + ".DesktopWindow", DragDropResult.IGNORED_MAX_INSTANCES);
         } else {
-            builder.expectNoRecords("Android.DragDrop.Tab.FromStrip.Result.DesktopWindow");
+            builder.expectNoRecords(resultHistogram + ".DesktopWindow");
         }
         HistogramWatcher histogramExpectation = builder.build();
 
@@ -1141,37 +1171,38 @@
         }
     }
 
-    private void doTriggerUnhandledDrop(int failureCount) {
+    private void doTriggerUnhandledDrop(int failureCount, boolean isGroupDrag) {
+        String resultHistogram =
+                String.format(
+                        "Android.DragDrop.%s.FromStrip.Result", isGroupDrag ? "TabGroup" : "Tab");
+        String failureHistogram = "Android.DragDrop.TabOrGroup.MaxInstanceFailureCount";
         var histogramBuilder =
                 HistogramWatcher.newBuilder()
                         .expectIntRecordTimes(
-                                "Android.DragDrop.Tab.FromStrip.Result",
-                                DragDropTabResult.IGNORED_MAX_INSTANCES,
-                                failureCount)
-                        .expectNoRecords("Android.DragDrop.Tab.FromStrip.Result.DesktopWindow");
+                                resultHistogram, DragDropResult.IGNORED_MAX_INSTANCES, failureCount)
+                        .expectNoRecords(resultHistogram + ".DesktopWindow");
 
         // Set histogram expectation.
         for (int i = 0; i < failureCount; i++) {
-            histogramBuilder =
-                    histogramBuilder.expectIntRecord(
-                            "Android.DragDrop.Tab.MaxInstanceFailureCount", i + 1);
+            histogramBuilder = histogramBuilder.expectIntRecord(failureHistogram, i + 1);
         }
         var histogramExpectation = histogramBuilder.build();
 
         // Simulate unhandled tab drops |failureCount| number of times.
         for (int i = 0; i < failureCount; i++) {
-            new DragEventInvoker(/* isGroupDrag= */ false, /* isGroupShared= */ false)
+            new DragEventInvoker(isGroupDrag, /* isGroupShared= */ false)
                     .dragExit(mSourceInstance)
                     .end(false);
         }
 
         // Verify that the count is correctly updated in SharedPreferences and the histogram is
         // emitted as expected.
+        String maxInstanceFailureKey =
+                ChromePreferenceKeys.TAB_OR_GROUP_TEARING_MAX_INSTANCES_FAILURE_COUNT;
         assertEquals(
                 "Tab drag max-instance failure count saved in shared prefs is incorrect.",
                 failureCount,
-                mSharedPreferencesManager.readInt(
-                        ChromePreferenceKeys.TAB_TEARING_MAX_INSTANCES_FAILURE_COUNT));
+                mSharedPreferencesManager.readInt(maxInstanceFailureKey));
         histogramExpectation.assertExpected();
     }
 
@@ -1180,30 +1211,36 @@
             boolean isGroupDrag,
             boolean isGroupShared,
             String mhtmlTabTitle) {
+        String resultHistogram =
+                String.format(
+                        "Android.DragDrop.%s.FromStrip.Result", isGroupDrag ? "TabGroup" : "Tab");
+        String typeHistogram =
+                String.format("Android.DragDrop.%s.Type", isGroupDrag ? "TabGroup" : "Tab");
+        String durationHistogram =
+                String.format(
+                        "Android.DragDrop.%s.Duration.WithinDestStrip",
+                        isGroupDrag ? "TabGroup" : "Tab");
         HistogramWatcher.Builder builder =
                 HistogramWatcher.newBuilder()
-                        .expectIntRecord(
-                                "Android.DragDrop.Tab.FromStrip.Result", DragDropTabResult.SUCCESS)
-                        .expectIntRecord(
-                                "Android.DragDrop.Tab.Type", DragDropType.TAB_STRIP_TO_TAB_STRIP)
+                        .expectIntRecord(resultHistogram, DragDropResult.SUCCESS)
+                        .expectIntRecord(typeHistogram, DragDropType.TAB_STRIP_TO_TAB_STRIP)
                         .expectNoRecords("Android.DragDrop.Tab.ReorderStripWithDragDrop")
-                        .expectAnyRecord("Android.DragDrop.Tab.Duration.WithinDestStrip");
+                        .expectNoRecords("Android.DragDrop.TabGroup.ReorderStripWithDragDrop")
+                        .expectAnyRecord(durationHistogram);
 
         if (isInDesktopWindow) {
             AppHeaderUtils.setAppInDesktopWindowForTesting(true);
-            builder.expectIntRecord(
-                            "Android.DragDrop.Tab.FromStrip.Result.DesktopWindow",
-                            DragDropTabResult.SUCCESS)
+            builder.expectIntRecord(resultHistogram + ".DesktopWindow", DragDropResult.SUCCESS)
                     .expectIntRecord(
-                            "Android.DragDrop.Tab.Type.DesktopWindow",
-                            DragDropType.TAB_STRIP_TO_TAB_STRIP);
+                            typeHistogram + ".DesktopWindow", DragDropType.TAB_STRIP_TO_TAB_STRIP);
         }
         HistogramWatcher histogramExpectation = builder.build();
 
         when(mDestStripLayoutHelper.getTabIndexForTabDrop(anyFloat())).thenReturn(TAB_INDEX);
 
         // Invoke drop.
-        invokeDropInDestinationStrip(/* dragEndRes= */ true, isGroupDrag, isGroupShared);
+        invokeDropInDestinationStrip(
+                /* dragEndRes= */ mhtmlTabTitle == null, isGroupDrag, isGroupShared);
 
         // Verify - drop failed and toast is shown for group that has mhtml tab.
         if (mhtmlTabTitle != null) {
@@ -1225,12 +1262,32 @@
         verify(mDestStripLayoutHelper).stopReorderMode();
 
         assertNull(ShadowToast.getLatestToast());
-        if (!isGroupDrag) {
-            histogramExpectation.assertExpected();
-        }
+        histogramExpectation.assertExpected();
     }
 
     private void doTestDropInDestinationDifferentModel(boolean isGroupDrag) {
+        String resultHistogram =
+                String.format(
+                        "Android.DragDrop.%s.FromStrip.Result", isGroupDrag ? "TabGroup" : "Tab");
+        String typeHistogram =
+                String.format("Android.DragDrop.%s.Type", isGroupDrag ? "TabGroup" : "Tab");
+        String durationHistogram =
+                String.format(
+                        "Android.DragDrop.%s.Duration.WithinDestStrip",
+                        isGroupDrag ? "TabGroup" : "Tab");
+        HistogramWatcher histogramExpectation =
+                HistogramWatcher.newBuilder()
+                        .expectIntRecord(resultHistogram, DragDropResult.SUCCESS)
+                        .expectIntRecord(typeHistogram, DragDropType.TAB_STRIP_TO_TAB_STRIP)
+                        .expectAnyRecord(durationHistogram)
+                        .expectNoRecords("Android.DragDrop.Tab.FromStrip.Result.DesktopWindow")
+                        .expectNoRecords("Android.DragDrop.Tab.Type.DesktopWindow")
+                        .expectNoRecords("Android.DragDrop.Tab.ReorderStripWithDragDrop")
+                        .expectNoRecords("Android.DragDrop.TabGroup.FromStrip.Result.DesktopWindow")
+                        .expectNoRecords("Android.DragDrop.TabGroup.Type.DesktopWindow")
+                        .expectNoRecords("Android.DragDrop.TabGroup.ReorderStripWithDragDrop")
+                        .build();
+
         // Destination tab model is incognito.
         when(mTabModel.isIncognitoBranded()).thenReturn(true);
         TabModel standardModelDestination = mock(TabModel.class);
@@ -1248,9 +1305,33 @@
                 ContextUtils.getApplicationContext()
                         .getString(R.string.tab_dropped_different_model));
         assertNotNull(ShadowToast.getLatestToast());
+
+        // Verify histograms.
+        histogramExpectation.assertExpected();
     }
 
     private void doTestDropInDestinationToolbarContainer(boolean isGroupDrag) {
+        String resultHistogram =
+                String.format(
+                        "Android.DragDrop.%s.FromStrip.Result", isGroupDrag ? "TabGroup" : "Tab");
+        String durationHistogram =
+                String.format(
+                        "Android.DragDrop.%s.Duration.WithinDestStrip",
+                        isGroupDrag ? "TabGroup" : "Tab");
+
+        HistogramWatcher histogramExpectation =
+                HistogramWatcher.newBuilder()
+                        .expectIntRecord(resultHistogram, DragDropResult.IGNORED_TOOLBAR)
+                        .expectNoRecords("Android.DragDrop.Tab.FromStrip.Result.DesktopWindow")
+                        .expectNoRecords("Android.DragDrop.Tab.Type")
+                        .expectNoRecords("Android.DragDrop.Tab.Type.DesktopWindow")
+                        .expectNoRecords("Android.DragDrop.Tab.ReorderStripWithDragDrop")
+                        .expectAnyRecord(durationHistogram)
+                        .expectNoRecords("Android.DragDrop.TabGroup.FromStrip.Result.DesktopWindow")
+                        .expectNoRecords("Android.DragDrop.TabGroup.Type")
+                        .expectNoRecords("Android.DragDrop.TabGroup.Type.DesktopWindow")
+                        .expectNoRecords("Android.DragDrop.TabGroup.ReorderStripWithDragDrop")
+                        .build();
         new DragEventInvoker(isGroupDrag, /* isGroupShared= */ false)
                 .dragExit(mSourceInstance)
                 .verifyShadowVisibility(true)
@@ -1278,9 +1359,33 @@
 
         // Verify tab cleared.
         verify(mSourceStripLayoutHelper, times(1)).stopReorderMode();
+
+        // Verify histograms.
+        histogramExpectation.assertExpected();
     }
 
     private void doTestExitIntoSourceToolbarAndRenterStripAndDrop(boolean isGroupDrag) {
+        String resultHistogram =
+                String.format(
+                        "Android.DragDrop.%s.FromStrip.Result", isGroupDrag ? "TabGroup" : "Tab");
+        String reorderHistogram =
+                String.format(
+                        "Android.DragDrop.%s.ReorderStripWithDragDrop",
+                        isGroupDrag ? "TabGroup" : "Tab");
+        HistogramWatcher histogramExpectation =
+                HistogramWatcher.newBuilder()
+                        .expectIntRecord(resultHistogram, DragDropResult.SUCCESS)
+                        .expectBooleanRecord(reorderHistogram, true)
+                        .expectNoRecords("Android.DragDrop.TabGroup.FromStrip.Result.DesktopWindow")
+                        .expectNoRecords("Android.DragDrop.TabGroup.Type")
+                        .expectNoRecords("Android.DragDrop.TabGroup.Type.DesktopWindow")
+                        .expectNoRecords("Android.DragDrop.TabGroup.Duration.WithinDestStrip")
+                        .expectNoRecords("Android.DragDrop.Tab.FromStrip.Result.DesktopWindow")
+                        .expectNoRecords("Android.DragDrop.Tab.Type")
+                        .expectNoRecords("Android.DragDrop.Tab.Type.DesktopWindow")
+                        .expectNoRecords("Android.DragDrop.Tab.Duration.WithinDestStrip")
+                        .build();
+
         new DragEventInvoker(isGroupDrag, /* isGroupShared= */ false)
                 .dragLocationY(mSourceInstance, 3 * DRAG_MOVE_DISTANCE) // move to toolbar
                 .verifyShadowVisibility(true)
@@ -1301,6 +1406,9 @@
 
         // Verify destination strip not invoked.
         verifyNoInteractions(mDestStripLayoutHelper);
+
+        // Verify histograms.
+        histogramExpectation.assertExpected();
     }
 
     private void doTestOnDragInvalidClipData(boolean isGroupDrag) {
@@ -1516,8 +1624,8 @@
         }
 
         public DragEventInvoker end(boolean res) {
-            mSourceInstance.onDrag(mTabsToolbarView, mockDragEndEvent(res, mIsGroupDrag));
             mDestInstance.onDrag(mTabsToolbarView, mockDragEndEvent(res, mIsGroupDrag));
+            mSourceInstance.onDrag(mTabsToolbarView, mockDragEndEvent(res, mIsGroupDrag));
             assertFalse(
                     "Global state should be cleared on all drag end",
                     DragDropGlobalState.hasValue());
@@ -1559,8 +1667,9 @@
             TabGroupMetadata tabGroupMetadata,
             boolean isGroupDrag) {
         DragEvent event = mock(DragEvent.class);
+        ChromeDropDataAndroid dropData;
         if (isGroupDrag) {
-            ChromeDropDataAndroid dropData =
+            dropData =
                     new ChromeTabGroupDropDataAndroid.Builder()
                             .withTabGroupMetadata(tabGroupMetadata)
                             .build();
@@ -1573,8 +1682,7 @@
             when(event.getClipDescription())
                     .thenReturn(new ClipDescription("", SUPPORTED_GROUP_MIME_TYPES));
         } else {
-            ChromeDropDataAndroid dropData =
-                    new ChromeTabDropDataAndroid.Builder().withTab(tab).build();
+            dropData = new ChromeTabDropDataAndroid.Builder().withTab(tab).build();
             when(event.getClipData())
                     .thenReturn(
                             new ClipData(
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/dragdrop/ChromeTabbedOnDragListenerUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/dragdrop/ChromeTabbedOnDragListenerUnitTest.java
index d384612..85b3785 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/dragdrop/ChromeTabbedOnDragListenerUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/dragdrop/ChromeTabbedOnDragListenerUnitTest.java
@@ -33,13 +33,14 @@
 import org.chromium.chrome.browser.layouts.LayoutType;
 import org.chromium.chrome.browser.multiwindow.MultiInstanceManager;
 import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tabmodel.TabGroupMetadata;
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.ui.desktop_windowing.AppHeaderUtils;
 import org.chromium.components.browser_ui.desktop_windowing.DesktopWindowStateManager;
 import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.dragdrop.DragDropGlobalState;
-import org.chromium.ui.dragdrop.DragDropMetricUtils.DragDropTabResult;
+import org.chromium.ui.dragdrop.DragDropMetricUtils.DragDropResult;
 import org.chromium.ui.dragdrop.DragDropMetricUtils.DragDropType;
 
 import java.lang.ref.WeakReference;
@@ -55,6 +56,7 @@
     @Mock private LayoutStateProvider mLayoutStateProvider;
     @Mock private DragDropGlobalState mDragDropGlobalState;
     @Mock private Tab mTab;
+    @Mock private TabGroupMetadata mTabGroupMetadata;
     @Mock private DesktopWindowStateManager mDesktopWindowStateManager;
     private OneshotSupplierImpl<LayoutStateProvider> mLayoutStateProviderSupplierImpl;
     private ClipDescription mTabClipDescription =
@@ -85,8 +87,6 @@
         when(mCurrentTab.getId()).thenReturn(1);
         when(mTabModelSelector.getModel(false)).thenReturn(Mockito.mock(TabModel.class));
         when(mMultiInstanceManager.getCurrentInstanceId()).thenReturn(SOURCE_INSTANCE_ID);
-        when(mDragDropGlobalState.getData())
-                .thenReturn(new ChromeTabDropDataAndroid.Builder().withTab(mTab).build());
         when(mDragDropGlobalState.isDragSourceInstance(SOURCE_INSTANCE_ID)).thenReturn(true);
         DragDropGlobalState.setInstanceForTesting(mDragDropGlobalState);
         Activity activity = Mockito.mock(Activity.class);
@@ -106,7 +106,7 @@
     }
 
     private void doTestOnDragActionDragStarted(boolean isGroupDrag) {
-        // Drag started should return false, since drag source is not chrome tab.
+        // Drag started should return false, since drag source is not chrome tab or group.
         assertFalse(
                 "Drag started should return false.",
                 mChromeTabbedOnDragListener.onDrag(
@@ -147,17 +147,7 @@
 
     @Test
     public void testOnDrag_ActionDrop_TabSwitcher() {
-        HistogramWatcher histogramExpectation =
-                HistogramWatcher.newBuilder()
-                        .expectIntRecord(
-                                "Android.DragDrop.Tab.FromStrip.Result",
-                                DragDropTabResult.IGNORED_TAB_SWITCHER)
-                        .expectNoRecords("Android.DragDrop.Tab.FromStrip.Result.DesktopWindow")
-                        .expectNoRecords("Android.DragDrop.Tab.Type")
-                        .expectNoRecords("Android.DragDrop.Tab.Type.DesktopWindow")
-                        .build();
         doTestOnDragActionDropInTabSwitcher(/* isGroupDrag= */ false);
-        histogramExpectation.assertExpected();
     }
 
     @Test
@@ -166,6 +156,18 @@
     }
 
     private void doTestOnDragActionDropInTabSwitcher(boolean isGroupDrag) {
+        String resultHistogram =
+                String.format(
+                        "Android.DragDrop.%s.FromStrip.Result", isGroupDrag ? "TabGroup" : "Tab");
+        HistogramWatcher histogramExpectation =
+                HistogramWatcher.newBuilder()
+                        .expectIntRecord(resultHistogram, DragDropResult.IGNORED_TAB_SWITCHER)
+                        .expectNoRecords(resultHistogram + ".DesktopWindow")
+                        .expectNoRecords("Android.DragDrop.Tab.Type")
+                        .expectNoRecords("Android.DragDrop.Tab.Type.DesktopWindow")
+                        .expectNoRecords("Android.DragDrop.TabGroup.Type")
+                        .expectNoRecords("Android.DragDrop.TabGroup.Type.DesktopWindow")
+                        .build();
         // Call drag start to set states.
         assertTrue(
                 "Drag started should return true.",
@@ -181,24 +183,13 @@
                 mChromeTabbedOnDragListener.onDrag(
                         mCompositorViewHolder,
                         mockDragEvent(DragEvent.ACTION_DROP, /* result= */ false, isGroupDrag)));
+        // Verify histograms.
+        histogramExpectation.assertExpected();
     }
 
     @Test
     public void testOnDrag_ActionDrop_SameInstance() {
-        AppHeaderUtils.setAppInDesktopWindowForTesting(true);
-        HistogramWatcher histogramExpectation =
-                HistogramWatcher.newBuilder()
-                        .expectIntRecord(
-                                "Android.DragDrop.Tab.FromStrip.Result",
-                                DragDropTabResult.IGNORED_SAME_INSTANCE)
-                        .expectIntRecord(
-                                "Android.DragDrop.Tab.FromStrip.Result.DesktopWindow",
-                                DragDropTabResult.IGNORED_SAME_INSTANCE)
-                        .expectNoRecords("Android.DragDrop.Tab.Type")
-                        .expectNoRecords("Android.DragDrop.Tab.Type.DesktopWindow")
-                        .build();
         doTestOnDragActionDropInSameInstance(/* isGroupDrag= */ false);
-        histogramExpectation.assertExpected();
     }
 
     @Test
@@ -207,6 +198,22 @@
     }
 
     private void doTestOnDragActionDropInSameInstance(boolean isGroupDrag) {
+        String resultHistogram =
+                String.format(
+                        "Android.DragDrop.%s.FromStrip.Result", isGroupDrag ? "TabGroup" : "Tab");
+        AppHeaderUtils.setAppInDesktopWindowForTesting(true);
+        HistogramWatcher histogramExpectation =
+                HistogramWatcher.newBuilder()
+                        .expectIntRecord(resultHistogram, DragDropResult.IGNORED_SAME_INSTANCE)
+                        .expectIntRecord(
+                                resultHistogram + ".DesktopWindow",
+                                DragDropResult.IGNORED_SAME_INSTANCE)
+                        .expectNoRecords("Android.DragDrop.Tab.Type")
+                        .expectNoRecords("Android.DragDrop.Tab.Type.DesktopWindow")
+                        .expectNoRecords("Android.DragDrop.TabGroup.Type")
+                        .expectNoRecords("Android.DragDrop.TabGroup.Type.DesktopWindow")
+                        .build();
+        setGlobalStateData(isGroupDrag);
         // Call drag start to set states.
         assertTrue(
                 "Drag started should return true.",
@@ -222,10 +229,15 @@
                 mChromeTabbedOnDragListener.onDrag(
                         mCompositorViewHolder,
                         mockDragEvent(DragEvent.ACTION_DROP, /* result= */ false, isGroupDrag)));
+        // Verify histograms.
+        histogramExpectation.assertExpected();
     }
 
     @Test
     public void testOnDrag_ActionDrop_Success() {
+        // Setup drag drop global state.
+        setGlobalStateData(/* isGroupDrag= */ false);
+
         // Verify action drop is success.
         verifyActionDropSuccess(/* isInDesktopWindow= */ false, /* isGroupDrag= */ false);
 
@@ -238,13 +250,10 @@
     }
 
     @Test
-    public void testOnDrag_ActionDrop_Success_tabGroup() {
-        // Verify action drop is success.
-        verifyActionDropSuccess(/* isInDesktopWindow= */ false, /* isGroupDrag= */ true);
-    }
-
-    @Test
     public void testOnDrag_ActionDrop_Success_DesktopWindow() {
+        // Setup drag drop global state.
+        setGlobalStateData(/* isGroupDrag= */ false);
+
         // Verify action drop is success.
         verifyActionDropSuccess(/* isInDesktopWindow= */ true, /* isGroupDrag= */ false);
 
@@ -276,17 +285,45 @@
                 mUserActionTest.getActionCount("MobileToolbarReorderTab.TabRemovedFromGroup"));
     }
 
+    @Test
+    public void testOnDrag_ActionDrop_Success_TabGroup_DesktopWindow() {
+        // Setup drag drop global state.
+        setGlobalStateData(/* isGroupDrag= */ true);
+
+        // Verify action drop is success.
+        verifyActionDropSuccess(/* isInDesktopWindow= */ true, /* isGroupDrag= */ true);
+    }
+
+    @Test
+    public void testOnDrag_ActionDrop_Success_TabGroup() {
+        // Setup drag drop global state.
+        setGlobalStateData(/* isGroupDrag= */ true);
+
+        // Verify action drop is success.
+        verifyActionDropSuccess(/* isInDesktopWindow= */ false, /* isGroupDrag= */ true);
+    }
+
     private void verifyActionDropSuccess(boolean isInDesktopWindow, boolean isGroupDrag) {
-        HistogramWatcher.Builder histogramExpectationBuilder =
+        String histogram =
+                String.format("Android.DragDrop.%s.Type", isGroupDrag ? "TabGroup" : "Tab");
+        AppHeaderUtils.setAppInDesktopWindowForTesting(isInDesktopWindow);
+
+        HistogramWatcher.Builder builder =
                 HistogramWatcher.newBuilder()
-                        .expectIntRecord(
-                                "Android.DragDrop.Tab.Type", DragDropType.TAB_STRIP_TO_CONTENT);
+                        .expectIntRecord(histogram, DragDropType.TAB_STRIP_TO_CONTENT)
+                        .expectNoRecords("Android.DragDrop.Tab.FromStrip.Result")
+                        .expectNoRecords("Android.DragDrop.Tab.FromStrip.Result.DesktopWindow")
+                        .expectNoRecords("Android.DragDrop.TabGroup.FromStrip.Result")
+                        .expectNoRecords(
+                                "Android.DragDrop.TabGroup.FromStrip.Result.DesktopWindow");
         if (isInDesktopWindow) {
-            AppHeaderUtils.setAppInDesktopWindowForTesting(true);
-            histogramExpectationBuilder.expectIntRecord(
-                    "Android.DragDrop.Tab.Type.DesktopWindow", DragDropType.TAB_STRIP_TO_CONTENT);
+            builder.expectIntRecord(
+                    histogram + ".DesktopWindow", DragDropType.TAB_STRIP_TO_CONTENT);
+        } else {
+            builder.expectNoRecords(histogram + ".DesktopWindow");
         }
-        HistogramWatcher histogramWatcher = histogramExpectationBuilder.build();
+        HistogramWatcher histogramWatcher = builder.build();
+
         // Call drag start to set states.
         assertTrue(
                 "Drag started should return true.",
@@ -303,10 +340,7 @@
                 "Action drop should return true",
                 mChromeTabbedOnDragListener.onDrag(
                         mCompositorViewHolder,
-                        mockDragEvent(
-                                DragEvent.ACTION_DROP,
-                                /* result= */ false,
-                                /* isGroupDrag= */ false)));
+                        mockDragEvent(DragEvent.ACTION_DROP, /* result= */ false, isGroupDrag)));
         histogramWatcher.assertExpected();
     }
 
@@ -322,4 +356,17 @@
         doReturn(action).when(event).getAction();
         return event;
     }
+
+    private void setGlobalStateData(boolean isGroupDrag) {
+        if (isGroupDrag) {
+            when(mDragDropGlobalState.getData())
+                    .thenReturn(
+                            new ChromeTabGroupDropDataAndroid.Builder()
+                                    .withTabGroupMetadata(mTabGroupMetadata)
+                                    .build());
+        } else {
+            when(mDragDropGlobalState.getData())
+                    .thenReturn(new ChromeTabDropDataAndroid.Builder().withTab(mTab).build());
+        }
+    }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManagerApi31UnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManagerApi31UnitTest.java
index bb64c3b3..30087e1b 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManagerApi31UnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManagerApi31UnitTest.java
@@ -1324,7 +1324,6 @@
 
     @Test
     @Config(sdk = 31)
-    @DisableFeatures(ChromeFeatureList.ANDROID_TAB_DECLUTTER)
     public void testCleanupIfLastInstance() {
         TabGroupSyncServiceFactory.setForTesting(mTabGroupSyncService);
         when(mTabGroupSyncService.getAllGroupIds()).thenReturn(new String[] {});
@@ -1357,7 +1356,6 @@
 
     @Test
     @Config(sdk = 31)
-    @DisableFeatures(ChromeFeatureList.ANDROID_TAB_DECLUTTER)
     public void testCleanupSyncedTabGroupsIfOnlyInstance() {
         mMultiInstanceManager.mTestBuildInstancesList = true;
         when(mTabGroupSyncService.getAllGroupIds()).thenReturn(new String[] {});
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/tab/TabArchiveSettingsTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/tab/TabArchiveSettingsTest.java
index d60aece88..9efc8d1 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/tab/TabArchiveSettingsTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/tab/TabArchiveSettingsTest.java
@@ -5,7 +5,6 @@
 package org.chromium.chrome.browser.tab;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import org.junit.Before;
@@ -13,7 +12,6 @@
 import org.junit.runner.RunWith;
 import org.robolectric.annotation.Config;
 
-import org.chromium.base.FeatureOverrides;
 import org.chromium.base.shared_preferences.SharedPreferencesManager;
 import org.chromium.base.task.TaskTraits;
 import org.chromium.base.task.test.ShadowPostTask;
@@ -28,14 +26,7 @@
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(shadows = {ShadowPostTask.class})
 public class TabArchiveSettingsTest {
-
-    static final String ARCHIVE_ENABLED_PARAM = "android_tab_declutter_archive_enabled";
-    static final String ARCHIVE_TIME_DELTA_PARAM = "android_tab_declutter_archive_time_delta_hours";
-    static final int ARCHIVE_TIME_DELTA_HOURS_DEFAULT = 21 * 24;
-    static final String AUTO_DELETE_ENABLED_PARAM = "android_tab_declutter_auto_delete_enabled";
-    static final String AUTO_DELETE_TIME_DELTA_PARAM =
-            "android_tab_declutter_auto_delete_time_delta_hours";
-    static final int AUTO_DELETE_TIME_DELTA_HOURS_DEFAULT = 60 * 24;
+    private static final int AUTO_DELETE_TIME_DELTA_HOURS_DEFAULT = 60 * 24; // 60 days.
 
     private TabArchiveSettings mSettings;
     private SharedPreferencesManager mPrefsManager;
@@ -58,29 +49,20 @@
     }
 
     @Test
-    public void testSettings() {
+    public void testDefaultSettings() {
         // Archive is disabled for tests, reset it to the default param value.
-        mSettings.setArchiveEnabled(
-                ChromeFeatureList.sAndroidTabDeclutterArchiveEnabled.getValue());
-        assertEquals(
-                ChromeFeatureList.sAndroidTabDeclutterArchiveEnabled.getValue(),
-                mSettings.getArchiveEnabled());
-        assertEquals(ARCHIVE_TIME_DELTA_HOURS_DEFAULT, mSettings.getArchiveTimeDeltaHours());
-        assertEquals(false, mSettings.isAutoDeleteEnabled());
-        assertEquals(AUTO_DELETE_TIME_DELTA_HOURS_DEFAULT, mSettings.getAutoDeleteTimeDeltaHours());
-
-        mSettings.setArchiveEnabled(false);
-        assertFalse(mSettings.getArchiveEnabled());
-
-        mSettings.setArchiveTimeDeltaHours(1);
-        assertEquals(1, mSettings.getArchiveTimeDeltaHours());
-
         mSettings.setArchiveEnabled(true);
-        mSettings.setAutoDeleteEnabled(true);
-        assertTrue(mSettings.isAutoDeleteEnabled());
-
-        mSettings.setAutoDeleteTimeDeltaHours(1);
-        assertEquals(1, mSettings.getArchiveTimeDeltaHours());
+        assertTrue(mSettings.getArchiveEnabled());
+        assertEquals(
+                TabArchiveSettings.DEFAULT_ARCHIVE_TIME_HOURS,
+                mSettings.getArchiveTimeDeltaHours());
+        assertEquals(
+                ChromeFeatureList.sAndroidTabDeclutterAutoDelete.isEnabled(),
+                mSettings.isAutoDeleteEnabled());
+        assertEquals(AUTO_DELETE_TIME_DELTA_HOURS_DEFAULT, mSettings.getAutoDeleteTimeDeltaHours());
+        assertEquals(
+                TabArchiveSettings.DEFAULT_MAX_SIMULTANEOUS_ARCHIVES,
+                mSettings.getMaxSimultaneousArchives());
     }
 
     @Test
@@ -91,38 +73,6 @@
     }
 
     @Test
-    public void testSettingsDefaultOverriddenByFinch() {
-        // Archive is disabled for tests, reset it to the default param value.
-        mSettings.setArchiveEnabled(
-                ChromeFeatureList.sAndroidTabDeclutterArchiveEnabled.getValue());
-        assertTrue(mSettings.getArchiveEnabled());
-        assertFalse(mSettings.isAutoDeleteEnabled());
-
-        FeatureOverrides.newBuilder()
-                .param(ChromeFeatureList.ANDROID_TAB_DECLUTTER, ARCHIVE_ENABLED_PARAM, false)
-                .param(ChromeFeatureList.ANDROID_TAB_DECLUTTER, ARCHIVE_TIME_DELTA_PARAM, 10)
-                .param(ChromeFeatureList.ANDROID_TAB_DECLUTTER, AUTO_DELETE_ENABLED_PARAM, true)
-                .param(ChromeFeatureList.ANDROID_TAB_DECLUTTER, AUTO_DELETE_TIME_DELTA_PARAM, 20)
-                .apply();
-
-        // Archive is disabled for tests, reset it to the default param value.
-        mSettings.setArchiveEnabled(
-                ChromeFeatureList.sAndroidTabDeclutterArchiveEnabled.getValue());
-        assertFalse(mSettings.getArchiveEnabled());
-        assertFalse(mSettings.isAutoDeleteEnabled());
-        mSettings.setArchiveEnabled(true);
-        assertTrue(mSettings.isAutoDeleteEnabled());
-        assertEquals(10, mSettings.getArchiveTimeDeltaHours());
-        assertEquals(20, mSettings.getAutoDeleteTimeDeltaHours());
-
-        mSettings.setArchiveTimeDeltaHours(1);
-        assertEquals(1, mSettings.getArchiveTimeDeltaHours());
-
-        mSettings.setAutoDeleteTimeDeltaHours(1);
-        assertEquals(1, mSettings.getArchiveTimeDeltaHours());
-    }
-
-    @Test
     public void testNotifyObservers() throws Exception {
         CallbackHelper callbackHelper = new CallbackHelper();
         Observer obs =
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/tab/TabArchiverUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/tab/TabArchiverUnitTest.java
index 652ea94..0fea33b4 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/tab/TabArchiverUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/tab/TabArchiverUnitTest.java
@@ -4,12 +4,12 @@
 
 package org.chromium.chrome.browser.tab;
 
-import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -25,9 +25,7 @@
 import org.chromium.base.task.test.ShadowPostTask;
 import org.chromium.base.task.test.ShadowPostTask.TestImpl;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.base.test.util.HistogramWatcher;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tabmodel.TabCreator;
 import org.chromium.chrome.browser.tabmodel.TabGroupModelFilter;
@@ -39,7 +37,6 @@
 /** Tests for {@link TabArchiveSettings}. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(shadows = {ShadowPostTask.class})
-@EnableFeatures(ChromeFeatureList.ANDROID_TAB_DECLUTTER)
 public class TabArchiverUnitTest {
     public @Rule MockitoRule mMockitoRule = MockitoJUnit.rule().strictness(Strictness.LENIENT);
 
@@ -116,11 +113,8 @@
     }
 
     @Test
-    @EnableFeatures(
-            ChromeFeatureList.ANDROID_TAB_DECLUTTER
-                    + ":android_tab_declutter_max_simultaneous_archives/20")
     public void testMaxSimultaneousArchives() {
-        assertEquals(20, ChromeFeatureList.sAndroidTabDeclutterMaxSimultaneousArchives.getValue());
+        when(mTabArchiveSettings.getMaxSimultaneousArchives()).thenReturn(20);
 
         HistogramWatcher watcher =
                 HistogramWatcher.newSingleRecordWatcher("Tabs.ArchivedTabs.MaxLimitReachedAt", 20);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebappIntentDataProviderTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebappIntentDataProviderTest.java
index 4ade676e..96ae0a7e 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebappIntentDataProviderTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebappIntentDataProviderTest.java
@@ -98,7 +98,7 @@
 
         assertEquals(
                 "Should resolve to standalone",
-                DisplayMode.STANDALONE,
+                DisplayMode.MINIMAL_UI,
                 intentDataProvider.getResolvedDisplayMode());
     }
 
@@ -117,19 +117,6 @@
 
     @Test
     @Config(sdk = Build.VERSION_CODES.VANILLA_ICE_CREAM)
-    @DisableFeatures({ChromeFeatureList.ANDROID_MINIMAL_UI_LARGE_SCREEN})
-    public void testMinUiModeDisabled_ResolveToStandalone() {
-        var intentDataProvider =
-                buildWebAppIntentDataProvider(mIntent, buildWebAppExtras(DisplayMode.MINIMAL_UI));
-
-        assertEquals(
-                "Should resolve to standalone",
-                DisplayMode.STANDALONE,
-                intentDataProvider.getResolvedDisplayMode());
-    }
-
-    @Test
-    @Config(sdk = Build.VERSION_CODES.VANILLA_ICE_CREAM)
     @EnableFeatures({ChromeFeatureList.ANDROID_MINIMAL_UI_LARGE_SCREEN})
     public void testBrowserModeWithMinUiEnabled_ResolveToMinUi() {
         var intentDataProvider =
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index f4eabcc..5734eb32 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-138.0.7155.0_rc-r1-merged.afdo.bz2
+chromeos-chrome-amd64-138.0.7164.0_rc-r1-merged.afdo.bz2
diff --git a/chrome/app/version_assembly/BUILD.gn b/chrome/app/version_assembly/BUILD.gn
index f7dd033..fe7d4875 100644
--- a/chrome/app/version_assembly/BUILD.gn
+++ b/chrome/app/version_assembly/BUILD.gn
@@ -32,7 +32,7 @@
     sources += [ segment_heap_manifest ]
   }
 
-  deps = [ ":chrome_exe_version_manifest" ]
+  public_deps = [ ":chrome_exe_version_manifest" ]
 }
 
 # Generates the manifest for the version assembly, which is the versioned
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 47d351af..7be63e23 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -2903,6 +2903,8 @@
       "android/webapps/webapp_registry.h",
       "auxiliary_search/auxiliary_search_provider.cc",
       "auxiliary_search/auxiliary_search_provider.h",
+      "auxiliary_search/auxiliary_search_top_site_provider_bridge.cc",
+      "auxiliary_search/auxiliary_search_top_site_provider_bridge.h",
       "auxiliary_search/fetch_and_rank_helper.cc",
       "auxiliary_search/fetch_and_rank_helper.h",
       "banners/android/chrome_app_banner_manager_android.cc",
@@ -4268,6 +4270,8 @@
       "webauthn/gpm_enclave_transaction.h",
       "webauthn/gpm_user_verification_policy.cc",
       "webauthn/gpm_user_verification_policy.h",
+      "webauthn/immediate_request_rate_limiter_factory.cc",
+      "webauthn/immediate_request_rate_limiter_factory.h",
       "webauthn/local_authentication_token.h",
       "webauthn/local_credential_management.cc",
       "webauthn/local_credential_management.h",
@@ -6279,8 +6283,6 @@
       "win/chrome_elf_init.h",
       "win/chrome_select_file_dialog_factory.cc",
       "win/chrome_select_file_dialog_factory.h",
-      "win/cloud_synced_folder_checker.cc",
-      "win/cloud_synced_folder_checker.h",
       "win/conflicts/enumerate_input_method_editors.cc",
       "win/conflicts/enumerate_input_method_editors.h",
       "win/conflicts/enumerate_shell_extensions.cc",
@@ -6334,6 +6336,7 @@
       "//chrome/browser/os_crypt",
       "//chrome/browser/shortcuts",
       "//chrome/browser/web_applications/chrome_pwa_launcher:util",
+      "//chrome/browser/win:cloud_synced_folder_checker",
       "//chrome/browser/win:mica_titlebar",
       "//chrome/browser/win/conflicts:module_info",
       "//chrome/chrome_elf:constants",
@@ -9296,7 +9299,6 @@
     deps = [
       ":browser",
       "//base",
-      "//components/exo/wayland:test_controller_stub",
       "//components/exo/wayland:ui_controls_protocol_stub",
       "//third_party/protobuf:protobuf_lite",
     ]
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 989d8e44..1506274 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -7511,7 +7511,7 @@
      flag_descriptions::kNavigationCaptureRefactorAndroidName,
      flag_descriptions::kNavigationCaptureRefactorAndroidDescription,
      kOsAndroid,
-     FEATURE_VALUE_TYPE(chrome::android::kNavigationCaptureRefactorAndroid)},
+     FEATURE_VALUE_TYPE(external_intents::kNavigationCaptureRefactorAndroid)},
 
     {"enable-magic-stack-android", flag_descriptions::kMagicStackAndroidName,
      flag_descriptions::kMagicStackAndroidDescription, kOsAndroid,
@@ -10766,10 +10766,6 @@
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
 #if BUILDFLAG(IS_ANDROID)
-    {"android-tab-declutter", flag_descriptions::kAndroidTabDeclutterName,
-     flag_descriptions::kAndroidTabDeclutterDescription, kOsAndroid,
-     FEATURE_VALUE_TYPE(chrome::android::kAndroidTabDeclutter)},
-
     {"android-tab-declutter-archive-all-but-active-tab",
      flag_descriptions::kAndroidTabDeclutterArchiveAllButActiveTabName,
      flag_descriptions::kAndroidTabDeclutterArchiveAllButActiveTabDescription,
@@ -10790,6 +10786,11 @@
      kOsAndroid,
      FEATURE_VALUE_TYPE(chrome::android::kAndroidTabDeclutterArchiveTabGroups)},
 
+    {"android-tab-declutter-auto-delete",
+     flag_descriptions::kAndroidTabDeclutterAutoDeleteName,
+     flag_descriptions::kAndroidTabDeclutterAutoDeleteDescription, kOsAndroid,
+     FEATURE_VALUE_TYPE(chrome::android::kAndroidTabDeclutterAutoDelete)},
+
     {"android-tab-declutter-performance-improvements",
      flag_descriptions::kAndroidTabDeclutterPerformanceImprovementsName,
      flag_descriptions::kAndroidTabDeclutterPerformanceImprovementsDescription,
@@ -12412,6 +12413,12 @@
      FEATURE_VALUE_TYPE(ash::features::kFwupdDeveloperMode)},
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
+#if BUILDFLAG(IS_ANDROID)
+    {"android-sms-otp-filling", flag_descriptions::kAndroidSmsOtpFillingName,
+     flag_descriptions::kAndroidSmsOtpFillingDescription, kOsAndroid,
+     FEATURE_VALUE_TYPE(password_manager::features::kAndroidSmsOtpFilling)},
+#endif  // BUILDFLAG(IS_ANDROID)
+
     // Add new entries above this line.
 
     // NOTE: Adding a new flag requires adding a corresponding entry to enum
diff --git a/chrome/browser/actor/BUILD.gn b/chrome/browser/actor/BUILD.gn
index 6661c62..db6fdf42 100644
--- a/chrome/browser/actor/BUILD.gn
+++ b/chrome/browser/actor/BUILD.gn
@@ -92,6 +92,7 @@
   testonly = true
   defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
   sources = [
+    "actor_coordinator_browsertest.cc",
     "site_policy_browsertest.cc",
     "tools/tools_browsertest.cc",
   ]
@@ -99,6 +100,7 @@
     ":actor",
     ":test_support",
     "//base/test:test_support",
+    "//chrome/browser/glic:glic",
     "//chrome/browser/optimization_guide:test_support",
     "//chrome/browser/safe_browsing",
     "//chrome/browser/ui:ui",
diff --git a/chrome/browser/actor/actor_coordinator.cc b/chrome/browser/actor/actor_coordinator.cc
index 14215f41..1affef8 100644
--- a/chrome/browser/actor/actor_coordinator.cc
+++ b/chrome/browser/actor/actor_coordinator.cc
@@ -169,6 +169,11 @@
   return !!task_state_;
 }
 
+bool ActorCoordinator::HasTaskForTab(const content::WebContents* tab) const {
+  return HasTask() && task_state_->HasTab() &&
+         task_state_->tab->GetContents() == tab;
+}
+
 void ActorCoordinator::StartTaskForTesting(tabs::TabInterface* tab) {
   CHECK(tab);
   CHECK(!task_state_);
diff --git a/chrome/browser/actor/actor_coordinator.h b/chrome/browser/actor/actor_coordinator.h
index a2c40eb8..95d7480c 100644
--- a/chrome/browser/actor/actor_coordinator.h
+++ b/chrome/browser/actor/actor_coordinator.h
@@ -77,6 +77,9 @@
   // Returns true if a task is currently active.
   bool HasTask() const;
 
+  // Returns true if a task is currently active in `tab`.
+  bool HasTaskForTab(const content::WebContents* tab) const;
+
   // Starts new task with an existing tab, for testing only. Intended for unit
   // tests that do not use a browser and actual navigation.
   void StartTaskForTesting(tabs::TabInterface* tab);
diff --git a/chrome/browser/actor/actor_coordinator_browsertest.cc b/chrome/browser/actor/actor_coordinator_browsertest.cc
new file mode 100644
index 0000000..8f6d32c
--- /dev/null
+++ b/chrome/browser/actor/actor_coordinator_browsertest.cc
@@ -0,0 +1,115 @@
+// 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/actor/actor_coordinator.h"
+
+#include <optional>
+#include <string_view>
+
+#include "base/test/scoped_feature_list.h"
+#include "base/test/test_future.h"
+#include "chrome/browser/actor/actor_test_util.h"
+#include "chrome/browser/glic/glic_keyed_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/common/chrome_features.h"
+#include "chrome/test/base/chrome_test_utils.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "components/optimization_guide/proto/features/actions_data.pb.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/test_frame_navigation_observer.h"
+#include "net/dns/mock_host_resolver.h"
+
+using ::base::test::TestFuture;
+using ::optimization_guide::proto::BrowserAction;
+
+namespace actor {
+
+namespace {
+
+class ActorCoordinatorBrowserTest : public InProcessBrowserTest {
+ public:
+  ActorCoordinatorBrowserTest() {
+    scoped_feature_list_.InitWithFeatures(
+        /*enabled_features=*/{features::kGlic, features::kTabstripComboButton,
+                              features::kGlicActor},
+        /*disabled_features=*/{features::kGlicWarming});
+  }
+  ActorCoordinatorBrowserTest(const ActorCoordinatorBrowserTest&) = delete;
+  ActorCoordinatorBrowserTest& operator=(const ActorCoordinatorBrowserTest&) =
+      delete;
+
+  ~ActorCoordinatorBrowserTest() override = default;
+
+  void SetUpOnMainThread() override {
+    InProcessBrowserTest::SetUpOnMainThread();
+    host_resolver()->AddRule("*", "127.0.0.1");
+    ASSERT_TRUE(embedded_test_server()->Start());
+
+    // TODO(crbug.com/409564704): Mock the delay so that tests can run at
+    // reasonable speed. Remove once there is a more permanent approach.
+    OverrideActionObservationDelay(base::Milliseconds(10));
+
+    actor_coordinator().StartTaskForTesting(browser()->GetActiveTabInterface());
+  }
+
+ protected:
+  content::WebContents* web_contents() {
+    return chrome_test_utils::GetActiveWebContents(this);
+  }
+
+  content::RenderFrameHost* main_frame() {
+    return web_contents()->GetPrimaryMainFrame();
+  }
+
+  ActorCoordinator& actor_coordinator() {
+    Profile* profile = chrome_test_utils::GetProfile(this);
+    auto* glic_service = glic::GlicKeyedService::Get(profile);
+    return glic_service->GetActorCoordinatorForTesting();
+  }
+
+  void ClickTarget(std::string_view query_selector) {
+    std::optional<int> dom_node_id =
+        content::GetDOMNodeId(*main_frame(), query_selector);
+    ASSERT_TRUE(dom_node_id);
+    BrowserAction action = MakeClick(dom_node_id.value());
+    TestFuture<bool> result;
+    actor_coordinator().Act(action, result.GetCallback());
+    EXPECT_TRUE(result.Get());
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+// The coordinator does not yet handle multi-tab cases. For now,
+// while acting on a tab, we override attempts by the page to create new
+// tabs, and instead navigate the existing tab.
+IN_PROC_BROWSER_TEST_F(ActorCoordinatorBrowserTest, ForceSameTabNavigation) {
+  const GURL url =
+      embedded_test_server()->GetURL("/actor/target_blank_links.html");
+  ASSERT_TRUE(content::NavigateToURL(web_contents(), url));
+
+  // Check specifically that it's the existing frame that navigates.
+  content::TestFrameNavigationObserver frame_nav_observer(main_frame());
+  ClickTarget("#anchorTarget");
+  frame_nav_observer.Wait();
+}
+
+IN_PROC_BROWSER_TEST_F(ActorCoordinatorBrowserTest,
+                       ForceSameTabNavigationByScript) {
+  const GURL url =
+      embedded_test_server()->GetURL("/actor/target_blank_links.html");
+  ASSERT_TRUE(content::NavigateToURL(web_contents(), url));
+
+  // Check specifically that it's the existing frame that navigates.
+  content::TestFrameNavigationObserver frame_nav_observer(main_frame());
+  ClickTarget("#scriptOpen");
+  frame_nav_observer.Wait();
+}
+
+}  // namespace
+
+}  // namespace actor
diff --git a/chrome/browser/ai/ai_data_keyed_service.cc b/chrome/browser/ai/ai_data_keyed_service.cc
index b3be43b..98b9b87 100644
--- a/chrome/browser/ai/ai_data_keyed_service.cc
+++ b/chrome/browser/ai/ai_data_keyed_service.cc
@@ -935,6 +935,15 @@
 #endif  // BUILDFLAG(ENABLE_GLIC)
 }
 
+bool AiDataKeyedService::IsActorCoordinatorActingOnTab(
+    const content::WebContents* tab) const {
+#if BUILDFLAG(ENABLE_GLIC)
+  return actor_coordinator_ && actor_coordinator_->HasTaskForTab(tab);
+#else
+  return false;
+#endif
+}
+
 #if BUILDFLAG(ENABLE_GLIC)
 void AiDataKeyedService::OnTaskCreated(
     base::OnceCallback<void(optimization_guide::proto::BrowserStartTaskResult)>
diff --git a/chrome/browser/ai/ai_data_keyed_service.h b/chrome/browser/ai/ai_data_keyed_service.h
index 5ff3b9c..23151a1 100644
--- a/chrome/browser/ai/ai_data_keyed_service.h
+++ b/chrome/browser/ai/ai_data_keyed_service.h
@@ -28,7 +28,8 @@
 class BrowserContext;
 }  // namespace content
 
-// Browser service to collect AI data.
+// Browser service to collect AI data, including data resulting from triggering
+// actor tasks.
 class AiDataKeyedService : public KeyedService {
  public:
   // Data related to AiData collection.
@@ -82,6 +83,11 @@
       base::OnceCallback<void(optimization_guide::proto::BrowserActionResult)>
           callback);
 
+  // Returns true if the associated ActorCoordinator is active on the given
+  // `tab`. This can be used by callers to customize certain behaviour that
+  // might interfere with the ActorCoordinator.
+  bool IsActorCoordinatorActingOnTab(const content::WebContents* tab) const;
+
   static const base::Feature& GetAllowlistedAiDataExtensionsFeatureForTesting();
   static const base::Feature&
   GetAllowlistedActionsExtensionsFeatureForTesting();
diff --git a/chrome/browser/ai/ai_data_keyed_service_browsertest.cc b/chrome/browser/ai/ai_data_keyed_service_browsertest.cc
index 9fb79ac..36381ff8 100644
--- a/chrome/browser/ai/ai_data_keyed_service_browsertest.cc
+++ b/chrome/browser/ai/ai_data_keyed_service_browsertest.cc
@@ -48,6 +48,7 @@
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/content_browser_test_utils.h"
 #include "content/public/test/fenced_frame_test_util.h"
+#include "content/public/test/test_frame_navigation_observer.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/request_handler_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -55,6 +56,8 @@
 
 namespace {
 
+using ::base::test::TestFuture;
+using ::optimization_guide::proto::ClickAction;
 using ::testing::ReturnRef;
 using AiData = AiDataKeyedService::AiData;
 using AiDataSpecifier = AiDataKeyedService::AiDataSpecifier;
@@ -612,6 +615,55 @@
       base::BindLambdaForTesting(start_task_callback_2));
   run_loop->Run();
 }
+
+// See ActorCoordinatorBrowserTest.ForceSameTabNavigation
+IN_PROC_BROWSER_TEST_F(AiDataKeyedServiceActorBrowserTest,
+                       ForceSameTabNavigation) {
+  TestFuture<optimization_guide::proto::BrowserStartTaskResult>
+      start_task_result;
+  int id = 1;
+  ai_data_service().StartTask(/*task=*/{}, start_task_result.GetCallback());
+  auto& task = start_task_result.Get();
+  EXPECT_EQ(task.task_id(), id);
+  EXPECT_EQ(task.tab_id(), id);
+
+  const GURL url = https_server()->GetURL("/actor/target_blank_links.html");
+  TestFuture<optimization_guide::proto::BrowserActionResult> navigate_result;
+  optimization_guide::proto::BrowserAction action_request;
+  action_request.set_task_id(id);
+  action_request.set_tab_id(id);
+  action_request.add_action_information()->mutable_navigate()->set_url(
+      url.spec());
+  ai_data_service().ExecuteAction(std::move(action_request),
+                                  navigate_result.GetCallback());
+  auto& navigate_response = navigate_result.Get();
+  EXPECT_EQ(navigate_response.task_id(), id);
+  EXPECT_EQ(navigate_response.tab_id(), id);
+
+  std::optional<int> anchor_dom_node_id = content::GetDOMNodeId(
+      *web_contents()->GetPrimaryMainFrame(), "#anchorTarget");
+  ASSERT_TRUE(anchor_dom_node_id);
+
+  TestFuture<optimization_guide::proto::BrowserActionResult> click_result;
+  optimization_guide::proto::BrowserAction click_request;
+  click_request.set_task_id(id);
+  click_request.set_tab_id(id);
+  ClickAction* click = click_request.add_action_information()->mutable_click();
+  click->mutable_target()->set_content_node_id(anchor_dom_node_id.value());
+  click->set_click_type(ClickAction::LEFT);
+  click->set_click_count(ClickAction::SINGLE);
+
+  // Check specifically that it's the existing frame that navigates.
+  content::TestFrameNavigationObserver frame_nav_observer(
+      web_contents()->GetPrimaryMainFrame());
+  ai_data_service().ExecuteAction(std::move(click_request),
+                                  click_result.GetCallback());
+  auto& click_response = click_result.Get();
+  EXPECT_EQ(click_response.task_id(), id);
+  EXPECT_EQ(click_response.tab_id(), id);
+  frame_nav_observer.Wait();
+}
+
 #endif  // BUILDFLAG(ENABLE_GLIC)
 
 }  // namespace
diff --git a/chrome/browser/android/BUILD.gn b/chrome/browser/android/BUILD.gn
index 02f3e08..8d5f069 100644
--- a/chrome/browser/android/BUILD.gn
+++ b/chrome/browser/android/BUILD.gn
@@ -17,6 +17,7 @@
   deps = [
     ":tabs_public",
     "//chrome/browser/sync",
+    "//components/external_intents/android",
     "//components/favicon/content",
     "//components/metrics:content",
     "//content/public/browser",
diff --git a/chrome/browser/android/tab_web_contents_delegate_android.cc b/chrome/browser/android/tab_web_contents_delegate_android.cc
index 33818ac..9c42813 100644
--- a/chrome/browser/android/tab_web_contents_delegate_android.cc
+++ b/chrome/browser/android/tab_web_contents_delegate_android.cc
@@ -55,6 +55,7 @@
 #include "components/blocked_content/popup_tracker.h"
 #include "components/browser_ui/sms/android/sms_infobar.h"
 #include "components/browser_ui/util/android/url_constants.h"
+#include "components/external_intents/android/external_intents_features.h"
 #include "components/find_in_page/find_notification_details.h"
 #include "components/find_in_page/find_tab_helper.h"
 #include "components/infobars/content/content_infobar_manager.h"
@@ -297,7 +298,7 @@
   }
 
   if (base::FeatureList::IsEnabled(
-          chrome::android::kNavigationCaptureRefactorAndroid)) {
+          external_intents::kNavigationCaptureRefactorAndroid)) {
     if (IsCustomTab() &&
         disposition == WindowOpenDisposition::NEW_BACKGROUND_TAB) {
       if (OpenInAppOrChromeFromCct(params.url)) {
diff --git a/chrome/browser/android/vr/arcore_device/fake_arcore.cc b/chrome/browser/android/vr/arcore_device/fake_arcore.cc
index c7b4d9b..2d06d2d 100644
--- a/chrome/browser/android/vr/arcore_device/fake_arcore.cc
+++ b/chrome/browser/android/vr/arcore_device/fake_arcore.cc
@@ -322,15 +322,20 @@
   result->light_probe->spherical_harmonics->coefficients.resize(9);
 
   // Initialize reflection_probe to black
+  const uint32_t cube_map_side_size = 16;
+  const uint64_t num_components = mojom::XRCubeMap::kNumComponentsPerPixel;
+  const size_t cube_map_size =
+      cube_map_side_size * cube_map_side_size * num_components;
+
   result->reflection_probe = mojom::XRReflectionProbe::New();
   result->reflection_probe->cube_map = mojom::XRCubeMap::New();
-  result->reflection_probe->cube_map->width_and_height = 16;
-  result->reflection_probe->cube_map->positive_x.resize(16 * 16);
-  result->reflection_probe->cube_map->negative_x.resize(16 * 16);
-  result->reflection_probe->cube_map->positive_y.resize(16 * 16);
-  result->reflection_probe->cube_map->negative_y.resize(16 * 16);
-  result->reflection_probe->cube_map->positive_z.resize(16 * 16);
-  result->reflection_probe->cube_map->negative_z.resize(16 * 16);
+  result->reflection_probe->cube_map->width_and_height = cube_map_side_size;
+  result->reflection_probe->cube_map->positive_x.resize(cube_map_size);
+  result->reflection_probe->cube_map->negative_x.resize(cube_map_size);
+  result->reflection_probe->cube_map->positive_y.resize(cube_map_size);
+  result->reflection_probe->cube_map->negative_y.resize(cube_map_size);
+  result->reflection_probe->cube_map->positive_z.resize(cube_map_size);
+  result->reflection_probe->cube_map->negative_z.resize(cube_map_size);
 
   return result;
 }
diff --git a/chrome/browser/apps/app_service/publishers/chrome_app_deprecation.cc b/chrome/browser/apps/app_service/publishers/chrome_app_deprecation.cc
index 41be9a8..d715587 100644
--- a/chrome/browser/apps/app_service/publishers/chrome_app_deprecation.cc
+++ b/chrome/browser/apps/app_service/publishers/chrome_app_deprecation.cc
@@ -6,6 +6,7 @@
 
 #include "ash/public/cpp/system_notification_builder.h"
 #include "base/containers/fixed_flat_set.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/no_destructor.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/extensions/extension_service.h"
@@ -29,7 +30,6 @@
              base::FEATURE_ENABLED_BY_DEFAULT);
 
 namespace {
-// TODO(crbug.com/413912653): Split the allowlists per context.
 constexpr auto kCommonAllowlist = base::MakeFixedFlatSet<std::string_view>(
     {"aakfkoilmhehmmadlkedfbcelkbamdkj", "aepgaekjheajlcifmpjcnpbjcencoefn",
      "afoipjmffplafpbfjopglheidddioiai", "afpnehpifljbjjplppeplamalioanmio",
@@ -107,7 +107,6 @@
 
 constexpr auto kUserInstalledAllowlist = base::flat_set<std::string_view>();
 
-// TODO(crbug.com/383754553): Add the finalised list only in M138 builds.
 constexpr auto kKioskSessionAllowlist =
     base::MakeFixedFlatSet<std::string_view>(
         {"adbijfidmjidmkkpiglnfkflcoblkfmn", "adpfhflbokfdhnfakijgjkpkjegncbpl",
@@ -167,6 +166,38 @@
 // because the allowlist are always valid while Chrome is running.
 static base::NoDestructor<std::unordered_set<std::string>> testAllowlistedApps;
 
+// This enum lists the possible outcomes of the deprecation checks performed
+// during the launch of a ChromeApp.
+//
+// These values are persisted to logs and the values match the entries of
+// `enum ChromeAppDeprecationLaunchOutcome` in
+// `tools/metrics/histograms/metadata/apps/enums.xml`.
+// Entries should not be renumbered and numeric values should never be reused.
+// LINT.IfChange(ChromeAppDeprecationLaunchOutcome)
+enum class DeprecationCheckOutcome {
+  kUserInstalledAllowedByFlag = 0,
+  kUserInstalledAllowedByAllowlist = 1,
+  kUserInstalledBlocked = 2,
+  kKioskModeAllowedByFlag = 3,
+  kKioskModeAllowedByAllowlist = 4,
+  kKioskModeAllowedByAdminPolicy = 5,
+  kKioskModeBlocked = 6,
+  kManagedAllowedByFlag = 7,
+  kManagedAllowedByAllowlist = 8,
+  kManagedAllowedByAdminPolicy = 9,
+  kManagedBlocked = 10,
+  kAllowedNotChromeApp = 11,
+  kAllowedDefault = 12,
+  kBlockedDefault = 13,
+  kMaxValue = kBlockedDefault
+};
+// LINT.ThenChange(//tools/metrics/histograms/metadata/apps/enums.xml:ChromeAppDeprecationLaunchOutcome)
+
+void ReportMetric(DeprecationCheckOutcome outcome) {
+  base::UmaHistogramEnumeration("Apps.AppLaunch.ChromeAppsDeprecationCheck",
+                                outcome);
+}
+
 static bool fakeKioskSessionForTesting = false;
 
 enum class AllowlistContext { UserInstalled, KioskSession };
@@ -223,14 +254,17 @@
                                          Profile* profile) {
   // TODO(crbug.com/379261516): Block the execution in M139.
   if (IsAllowlisted(app.id(), AllowlistContext::UserInstalled)) {
+    ReportMetric(DeprecationCheckOutcome::kUserInstalledAllowedByAllowlist);
     return DeprecationStatus::kLaunchAllowed;
   }
 
   if (base::FeatureList::IsEnabled(kAllowUserInstalledChromeApps)) {
     ShowNotification(app, profile);
+    ReportMetric(DeprecationCheckOutcome::kUserInstalledAllowedByFlag);
     return DeprecationStatus::kLaunchAllowed;
   }
 
+  ReportMetric(DeprecationCheckOutcome::kUserInstalledBlocked);
   return DeprecationStatus::kLaunchBlocked;
 }
 
@@ -238,17 +272,21 @@
                                         Profile* profile) {
   // TODO(crbug.com/379262711): Block the execution in M151.
   if (IsAllowlisted(app.id(), AllowlistContext::KioskSession)) {
+    ReportMetric(DeprecationCheckOutcome::kKioskModeAllowedByAllowlist);
     return DeprecationStatus::kLaunchAllowed;
   }
 
   if (profile->GetPrefs()->GetBoolean(prefs::kKioskChromeAppsForceAllowed)) {
+    ReportMetric(DeprecationCheckOutcome::kKioskModeAllowedByAdminPolicy);
     return DeprecationStatus::kLaunchAllowed;
   }
 
   if (base::FeatureList::IsEnabled(kAllowChromeAppsInKioskSessions)) {
+    ReportMetric(DeprecationCheckOutcome::kKioskModeAllowedByFlag);
     return DeprecationStatus::kLaunchAllowed;
   }
 
+  ReportMetric(DeprecationCheckOutcome::kKioskModeBlocked);
   return DeprecationStatus::kLaunchBlocked;
 }
 }  // namespace
@@ -259,6 +297,7 @@
           app_id.data());
 
   if (!app || !app->is_app()) {
+    ReportMetric(DeprecationCheckOutcome::kAllowedNotChromeApp);
     return DeprecationStatus::kLaunchAllowed;
   }
 
@@ -270,6 +309,7 @@
     return HandleUserInstalledApp(*app, profile);
   }
 
+  ReportMetric(DeprecationCheckOutcome::kAllowedDefault);
   return DeprecationStatus::kLaunchAllowed;
 }
 
@@ -281,8 +321,8 @@
   testAllowlistedApps->clear();
 }
 
-void SetKioskSessionForTesting() {
-  fakeKioskSessionForTesting = true;
+void SetKioskSessionForTesting(bool value) {
+  fakeKioskSessionForTesting = value;
 }
 
 }  // namespace apps::chrome_app_deprecation
diff --git a/chrome/browser/apps/app_service/publishers/chrome_app_deprecation.h b/chrome/browser/apps/app_service/publishers/chrome_app_deprecation.h
index 9d26e046..77b33b8 100644
--- a/chrome/browser/apps/app_service/publishers/chrome_app_deprecation.h
+++ b/chrome/browser/apps/app_service/publishers/chrome_app_deprecation.h
@@ -23,7 +23,7 @@
 
 void AddAppToAllowlistForTesting(std::string_view app_id);
 void ResetAllowlistForTesting();
-void SetKioskSessionForTesting();
+void SetKioskSessionForTesting(bool value = true);
 
 BASE_DECLARE_FEATURE(kAllowUserInstalledChromeApps);
 BASE_DECLARE_FEATURE(kAllowChromeAppsInKioskSessions);
diff --git a/chrome/browser/apps/app_service/publishers/chrome_app_deprecation_unittest.cc b/chrome/browser/apps/app_service/publishers/chrome_app_deprecation_unittest.cc
index 1f3aaf5..b8abea1 100644
--- a/chrome/browser/apps/app_service/publishers/chrome_app_deprecation_unittest.cc
+++ b/chrome/browser/apps/app_service/publishers/chrome_app_deprecation_unittest.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/apps/app_service/publishers/chrome_app_deprecation.h"
 
 #include "base/feature_list.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "chrome/browser/extensions/chrome_test_extension_loader.h"
 #include "chrome/browser/extensions/crx_installer.h"
 #include "chrome/browser/extensions/extension_service_test_base.h"
@@ -25,6 +26,9 @@
 
 namespace apps::chrome_app_deprecation {
 
+constexpr std::string_view kHistogram =
+    "Apps.AppLaunch.ChromeAppsDeprecationCheck";
+
 class ChromeAppDeprecationTest : public extensions::ExtensionServiceTestBase {
  protected:
   void SetUp() override {
@@ -65,6 +69,8 @@
 
   base::test::ScopedFeatureList scoped_feature_list_;
   scoped_refptr<const Extension> app_;
+
+  base::HistogramTester histogram_tester_;
 };
 
 TEST_F(ChromeAppDeprecationTest, DefaultFeatureFlag) {
@@ -73,6 +79,23 @@
 
   EXPECT_EQ(HandleDeprecation(app_->id(), profile()),
             DeprecationStatus::kLaunchAllowed);
+
+  EXPECT_THAT(
+      histogram_tester_.GetAllSamples(kHistogram),
+      BucketsAre(base::Bucket(
+          /*DeprecationCheckOutcome::kUserInstalledAllowedByFlag*/ 0, 1)));
+}
+
+TEST_F(ChromeAppDeprecationTest, DefaultFeatureFlagNotChromeApp) {
+  scoped_feature_list_.InitWithEmptyFeatureAndFieldTrialLists();
+  ASSERT_TRUE(base::FeatureList::IsEnabled(kAllowUserInstalledChromeApps));
+
+  EXPECT_EQ(HandleDeprecation("Not a Chrome App id", profile()),
+            DeprecationStatus::kLaunchAllowed);
+
+  EXPECT_THAT(histogram_tester_.GetAllSamples(kHistogram),
+              BucketsAre(base::Bucket(
+                  /*DeprecationCheckOutcome::kAllowedNotChromeApp*/ 11, 1)));
 }
 
 TEST_F(ChromeAppDeprecationTest, DisabledFeatureFlag) {
@@ -81,6 +104,22 @@
 
   EXPECT_EQ(HandleDeprecation(app_->id(), profile()),
             DeprecationStatus::kLaunchBlocked);
+
+  EXPECT_THAT(histogram_tester_.GetAllSamples(kHistogram),
+              BucketsAre(base::Bucket(
+                  /*DeprecationCheckOutcome::kUserInstalledBlocked*/ 2, 1)));
+}
+
+TEST_F(ChromeAppDeprecationTest, DisabledFeatureFlagNotChromeApp) {
+  scoped_feature_list_.InitAndDisableFeature(kAllowUserInstalledChromeApps);
+  ASSERT_FALSE(base::FeatureList::IsEnabled(kAllowUserInstalledChromeApps));
+
+  EXPECT_EQ(HandleDeprecation("Not a Chrome App id", profile()),
+            DeprecationStatus::kLaunchAllowed);
+
+  EXPECT_THAT(histogram_tester_.GetAllSamples(kHistogram),
+              BucketsAre(base::Bucket(
+                  /*DeprecationCheckOutcome::kAllowedNotChromeApp*/ 11, 1)));
 }
 
 TEST_F(ChromeAppDeprecationTest, EnabledFeatureFlag) {
@@ -89,14 +128,38 @@
 
   EXPECT_EQ(HandleDeprecation(app_->id(), profile()),
             DeprecationStatus::kLaunchAllowed);
+
+  EXPECT_THAT(
+      histogram_tester_.GetAllSamples(kHistogram),
+      BucketsAre(base::Bucket(
+          /*DeprecationCheckOutcome::kUserInstalledAllowedByFlag*/ 0, 1)));
+}
+
+TEST_F(ChromeAppDeprecationTest, EnabledFeatureFlagNotChromeApp) {
+  scoped_feature_list_.InitAndEnableFeature(kAllowUserInstalledChromeApps);
+  ASSERT_TRUE(base::FeatureList::IsEnabled(kAllowUserInstalledChromeApps));
+
+  EXPECT_EQ(HandleDeprecation("Not a Chrome App id", profile()),
+            DeprecationStatus::kLaunchAllowed);
+
+  EXPECT_THAT(histogram_tester_.GetAllSamples(kHistogram),
+              BucketsAre(base::Bucket(
+                  /*DeprecationCheckOutcome::kAllowedNotChromeApp*/ 11, 1)));
 }
 
 class ChromeAppDeprecationKioskTest : public ChromeAppDeprecationTest {
+ protected:
   void SetUp() override {
     ChromeAppDeprecationTest::SetUp();
 
     SetKioskSessionForTesting();
   }
+
+  void TearDown() override {
+    SetKioskSessionForTesting(false);
+
+    ChromeAppDeprecationTest::TearDown();
+  }
 };
 
 TEST_F(ChromeAppDeprecationKioskTest, DefaultFeatureFlag) {
@@ -105,6 +168,10 @@
 
   EXPECT_EQ(HandleDeprecation(app_->id(), profile()),
             DeprecationStatus::kLaunchAllowed);
+
+  EXPECT_THAT(histogram_tester_.GetAllSamples(kHistogram),
+              BucketsAre(base::Bucket(
+                  /*DeprecationCheckOutcome::kKioskModeAllowedByFlag*/ 3, 1)));
 }
 
 TEST_F(ChromeAppDeprecationKioskTest, DisabledFeatureFlag) {
@@ -113,6 +180,10 @@
 
   EXPECT_EQ(HandleDeprecation(app_->id(), profile()),
             DeprecationStatus::kLaunchBlocked);
+
+  EXPECT_THAT(histogram_tester_.GetAllSamples(kHistogram),
+              BucketsAre(base::Bucket(
+                  /*DeprecationCheckOutcome::kKioskModeBlocked*/ 6, 1)));
 }
 
 TEST_F(ChromeAppDeprecationKioskTest, EnabledFeatureFlag) {
@@ -121,6 +192,10 @@
 
   EXPECT_EQ(HandleDeprecation(app_->id(), profile()),
             DeprecationStatus::kLaunchAllowed);
+
+  EXPECT_THAT(histogram_tester_.GetAllSamples(kHistogram),
+              BucketsAre(base::Bucket(
+                  /*DeprecationCheckOutcome::kKioskModeAllowedByFlag*/ 3, 1)));
 }
 
 TEST_F(ChromeAppDeprecationKioskTest, DisabledFeatureFlagDefaultPolicy) {
@@ -131,6 +206,10 @@
 
   EXPECT_EQ(HandleDeprecation(app_->id(), profile()),
             DeprecationStatus::kLaunchBlocked);
+
+  EXPECT_THAT(histogram_tester_.GetAllSamples(kHistogram),
+              BucketsAre(base::Bucket(
+                  /*DeprecationCheckOutcome::kKioskModeBlocked*/ 6, 1)));
 }
 
 TEST_F(ChromeAppDeprecationKioskTest, DisabledFeatureFlagOverridenByPolicy) {
@@ -143,9 +222,15 @@
 
   EXPECT_EQ(HandleDeprecation(app_->id(), profile()),
             DeprecationStatus::kLaunchAllowed);
+
+  EXPECT_THAT(
+      histogram_tester_.GetAllSamples(kHistogram),
+      BucketsAre(base::Bucket(
+          /*DeprecationCheckOutcome::kKioskModeAllowedByAdminPolicy*/ 5, 1)));
 }
 
-class ChromeAppDeprecationAllowlistTest : public ChromeAppDeprecationTest {
+class ChromeAppDeprecationUserInstalledAllowlistTest
+    : public ChromeAppDeprecationTest {
  protected:
   void SetUp() override {
     ChromeAppDeprecationTest::SetUp();
@@ -160,27 +245,98 @@
   }
 };
 
-TEST_F(ChromeAppDeprecationAllowlistTest, DefaultFeatureFlag) {
+TEST_F(ChromeAppDeprecationUserInstalledAllowlistTest, DefaultFeatureFlag) {
   scoped_feature_list_.InitWithEmptyFeatureAndFieldTrialLists();
   ASSERT_TRUE(base::FeatureList::IsEnabled(kAllowUserInstalledChromeApps));
 
   EXPECT_EQ(HandleDeprecation(app_->id(), profile()),
             DeprecationStatus::kLaunchAllowed);
+
+  EXPECT_THAT(
+      histogram_tester_.GetAllSamples(kHistogram),
+      BucketsAre(base::Bucket(
+          /*DeprecationCheckOutcome::kUserInstalledAllowedByAllowlist*/ 1, 1)));
 }
 
-TEST_F(ChromeAppDeprecationAllowlistTest, DisabledFeatureFlag) {
+TEST_F(ChromeAppDeprecationUserInstalledAllowlistTest, DisabledFeatureFlag) {
   scoped_feature_list_.InitAndDisableFeature(kAllowUserInstalledChromeApps);
   ASSERT_FALSE(base::FeatureList::IsEnabled(kAllowUserInstalledChromeApps));
 
   EXPECT_EQ(HandleDeprecation(app_->id(), profile()),
             DeprecationStatus::kLaunchAllowed);
+
+  EXPECT_THAT(
+      histogram_tester_.GetAllSamples(kHistogram),
+      BucketsAre(base::Bucket(
+          /*DeprecationCheckOutcome::kUserInstalledAllowedByAllowlist*/ 1, 1)));
 }
 
-TEST_F(ChromeAppDeprecationAllowlistTest, EnabledFeatureFlag) {
+TEST_F(ChromeAppDeprecationUserInstalledAllowlistTest, EnabledFeatureFlag) {
   scoped_feature_list_.InitAndEnableFeature(kAllowUserInstalledChromeApps);
   ASSERT_TRUE(base::FeatureList::IsEnabled(kAllowUserInstalledChromeApps));
 
   EXPECT_EQ(HandleDeprecation(app_->id(), profile()),
             DeprecationStatus::kLaunchAllowed);
+
+  EXPECT_THAT(
+      histogram_tester_.GetAllSamples(kHistogram),
+      BucketsAre(base::Bucket(
+          /*DeprecationCheckOutcome::kUserInstalledAllowedByAllowlist*/ 1, 1)));
 }
+
+class ChromeAppDeprecationKioskAllowlistTest
+    : public ChromeAppDeprecationKioskTest {
+ protected:
+  void SetUp() override {
+    ChromeAppDeprecationKioskTest::SetUp();
+
+    AddAppToAllowlistForTesting(app_->id());
+  }
+
+  void TearDown() override {
+    ResetAllowlistForTesting();
+
+    ChromeAppDeprecationKioskTest::TearDown();
+  }
+};
+
+TEST_F(ChromeAppDeprecationKioskAllowlistTest, DefaultFeatureFlag) {
+  scoped_feature_list_.InitWithEmptyFeatureAndFieldTrialLists();
+  ASSERT_TRUE(base::FeatureList::IsEnabled(kAllowUserInstalledChromeApps));
+
+  EXPECT_EQ(HandleDeprecation(app_->id(), profile()),
+            DeprecationStatus::kLaunchAllowed);
+
+  EXPECT_THAT(
+      histogram_tester_.GetAllSamples(kHistogram),
+      BucketsAre(base::Bucket(
+          /*DeprecationCheckOutcome::kKioskModeAllowedByAllowlist*/ 4, 1)));
+}
+
+TEST_F(ChromeAppDeprecationKioskAllowlistTest, DisabledFeatureFlag) {
+  scoped_feature_list_.InitAndDisableFeature(kAllowUserInstalledChromeApps);
+  ASSERT_FALSE(base::FeatureList::IsEnabled(kAllowUserInstalledChromeApps));
+
+  EXPECT_EQ(HandleDeprecation(app_->id(), profile()),
+            DeprecationStatus::kLaunchAllowed);
+
+  EXPECT_THAT(
+      histogram_tester_.GetAllSamples(kHistogram),
+      BucketsAre(base::Bucket(
+          /*DeprecationCheckOutcome::kKioskModeAllowedByAllowlist*/ 4, 1)));
+}
+
+TEST_F(ChromeAppDeprecationKioskAllowlistTest, EnabledFeatureFlag) {
+  scoped_feature_list_.InitAndEnableFeature(kAllowUserInstalledChromeApps);
+  ASSERT_TRUE(base::FeatureList::IsEnabled(kAllowUserInstalledChromeApps));
+
+  EXPECT_EQ(HandleDeprecation(app_->id(), profile()),
+            DeprecationStatus::kLaunchAllowed);
+
+  EXPECT_THAT(
+      histogram_tester_.GetAllSamples(kHistogram),
+      BucketsAre(base::Bucket(
+          /*DeprecationCheckOutcome::kKioskModeAllowedByAllowlist*/ 4, 1)));
+}
+
 }  // namespace apps::chrome_app_deprecation
diff --git a/chrome/browser/ash/app_mode/BUILD.gn b/chrome/browser/ash/app_mode/BUILD.gn
index 6ee39a13..bd19c913 100644
--- a/chrome/browser/ash/app_mode/BUILD.gn
+++ b/chrome/browser/ash/app_mode/BUILD.gn
@@ -81,6 +81,7 @@
     "//chromeos/ash/components/kiosk/vision",
     "//chromeos/ash/components/login/auth",
     "//chromeos/ash/components/network",
+    "//chromeos/ash/components/policy/device_local_account",
     "//chromeos/ash/components/settings",
     "//chromeos/crosapi/mojom",
     "//chromeos/crosapi/mojom:mojom_shared_cpp_sources",
@@ -191,6 +192,7 @@
     "//chrome/common:non_code_constants",
     "//chrome/test:test_support_ui",
     "//chrome/test/data/chromeos/app_mode/webstore/itemsnippet",
+    "//chromeos/ash/components/policy/device_local_account",
   ]
   data_deps = [ "//chrome/test/data/chromeos/app_mode/webstore/itemsnippet:generated_protobufs" ]
 }
@@ -228,6 +230,7 @@
     "//chrome/test:test_support_ui",
     "//chromeos/ash/components/login/login_state",
     "//chromeos/ash/components/network:test_support",
+    "//chromeos/ash/components/policy/device_local_account",
     "//chromeos/ash/components/sync_wifi:test_support",
     "//components/prefs:test_support",
     "//components/webapps/browser",
@@ -291,6 +294,7 @@
     "//chromeos/ash/components/dbus/session_manager",
     "//chromeos/ash/components/dbus/shill",
     "//chromeos/ash/components/dbus/update_engine:update_engine",
+    "//chromeos/ash/components/policy/device_local_account",
     "//components/crx_file",
     "//components/ownership",
     "//content/test:test_support",
diff --git a/chrome/browser/ash/app_mode/consumer_kiosk_test_helper.cc b/chrome/browser/ash/app_mode/consumer_kiosk_test_helper.cc
index d5bb9b9..de80e18 100644
--- a/chrome/browser/ash/app_mode/consumer_kiosk_test_helper.cc
+++ b/chrome/browser/ash/app_mode/consumer_kiosk_test_helper.cc
@@ -16,8 +16,8 @@
 #include "chrome/browser/ash/app_mode/kiosk_chrome_app_manager.h"
 #include "chrome/browser/ash/ownership/owner_settings_service_ash.h"
 #include "chrome/browser/ash/policy/core/device_local_account.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
-#include "components/policy/core/common/device_local_account_type.h"
 
 namespace ash {
 
diff --git a/chrome/browser/ash/app_mode/isolated_web_app/BUILD.gn b/chrome/browser/ash/app_mode/isolated_web_app/BUILD.gn
index 2baf38c30..3c8b5775 100644
--- a/chrome/browser/ash/app_mode/isolated_web_app/BUILD.gn
+++ b/chrome/browser/ash/app_mode/isolated_web_app/BUILD.gn
@@ -20,6 +20,7 @@
     "//chrome/browser/ash/policy/core",
     "//chrome/browser/chromeos/app_mode",
     "//chrome/browser/web_applications",
+    "//chromeos/ash/components/policy/device_local_account",
     "//chromeos/ash/components/settings",
     "//components/prefs",
     "//components/web_package",
@@ -38,6 +39,7 @@
   deps = [
     "//ash/constants",
     "//chrome/browser/ash/policy/core",
+    "//chromeos/ash/components/policy/device_local_account",
     "//chromeos/ash/components/settings",
     "//components/account_id",
     "//components/prefs",
@@ -58,6 +60,7 @@
     "//chrome/browser/ash/settings",
     "//chrome/browser/web_applications",
     "//chrome/test:test_support",
+    "//chromeos/ash/components/policy/device_local_account",
     "//chromeos/ash/components/settings",
     "//components/policy/core/common",
     "//testing/gtest",
diff --git a/chrome/browser/ash/app_mode/isolated_web_app/kiosk_iwa_data_unittest.cc b/chrome/browser/ash/app_mode/isolated_web_app/kiosk_iwa_data_unittest.cc
index 7fb32cb..4479d6e 100644
--- a/chrome/browser/ash/app_mode/isolated_web_app/kiosk_iwa_data_unittest.cc
+++ b/chrome/browser/ash/app_mode/isolated_web_app/kiosk_iwa_data_unittest.cc
@@ -13,7 +13,7 @@
 #include "chrome/browser/web_applications/web_app_helpers.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/url_constants.h"
-#include "components/policy/core/common/device_local_account_type.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "components/prefs/testing_pref_service.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
diff --git a/chrome/browser/ash/app_mode/isolated_web_app/kiosk_iwa_manager.cc b/chrome/browser/ash/app_mode/isolated_web_app/kiosk_iwa_manager.cc
index a838f609..df3f4fe 100644
--- a/chrome/browser/ash/app_mode/isolated_web_app/kiosk_iwa_manager.cc
+++ b/chrome/browser/ash/app_mode/isolated_web_app/kiosk_iwa_manager.cc
@@ -26,9 +26,9 @@
 #include "chrome/browser/chromeos/app_mode/kiosk_web_app_update_observer.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/web_applications/web_app_install_info.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
 #include "components/account_id/account_id.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 #include "url/origin.h"
diff --git a/chrome/browser/ash/app_mode/isolated_web_app/kiosk_iwa_manager_unittest.cc b/chrome/browser/ash/app_mode/isolated_web_app/kiosk_iwa_manager_unittest.cc
index 6deb0b6d..ea9925d 100644
--- a/chrome/browser/ash/app_mode/isolated_web_app/kiosk_iwa_manager_unittest.cc
+++ b/chrome/browser/ash/app_mode/isolated_web_app/kiosk_iwa_manager_unittest.cc
@@ -21,10 +21,10 @@
 #include "chrome/test/base/scoped_testing_local_state.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chromeos/ash/components/dbus/userdataauth/userdataauth_client.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "chromeos/ash/components/settings/cros_settings.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
 #include "components/account_id/account_id.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "components/user_manager/scoped_user_manager.h"
 #include "content/public/test/browser_task_environment.h"
 #include "testing/gmock/include/gmock/gmock.h"
diff --git a/chrome/browser/ash/app_mode/isolated_web_app/kiosk_iwa_policy_util.cc b/chrome/browser/ash/app_mode/isolated_web_app/kiosk_iwa_policy_util.cc
index 25cd252..c975efb 100644
--- a/chrome/browser/ash/app_mode/isolated_web_app/kiosk_iwa_policy_util.cc
+++ b/chrome/browser/ash/app_mode/isolated_web_app/kiosk_iwa_policy_util.cc
@@ -14,9 +14,9 @@
 #include "base/check_op.h"
 #include "base/notreached.h"
 #include "chrome/browser/ash/policy/core/device_local_account.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "chromeos/ash/components/settings/cros_settings.h"
 #include "components/account_id/account_id.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "components/user_manager/user.h"
 #include "components/user_manager/user_manager.h"
 #include "components/web_package/signed_web_bundles/signed_web_bundle_id.h"
diff --git a/chrome/browser/ash/app_mode/kiosk_chrome_app_manager.cc b/chrome/browser/ash/app_mode/kiosk_chrome_app_manager.cc
index 0aed76e..732217e 100644
--- a/chrome/browser/ash/app_mode/kiosk_chrome_app_manager.cc
+++ b/chrome/browser/ash/app_mode/kiosk_chrome_app_manager.cc
@@ -54,10 +54,10 @@
 #include "chrome/browser/extensions/external_provider_impl.h"
 #include "chrome/browser/net/system_network_context_manager.h"
 #include "chrome/common/chrome_paths.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "chromeos/ash/components/settings/cros_settings.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
 #include "components/account_id/account_id.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
diff --git a/chrome/browser/ash/app_mode/kiosk_chrome_app_manager_browsertest.cc b/chrome/browser/ash/app_mode/kiosk_chrome_app_manager_browsertest.cc
index 994cdc5..eceed3c 100644
--- a/chrome/browser/ash/app_mode/kiosk_chrome_app_manager_browsertest.cc
+++ b/chrome/browser/ash/app_mode/kiosk_chrome_app_manager_browsertest.cc
@@ -50,10 +50,10 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
 #include "components/crx_file/crx_verifier.h"
 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "components/prefs/scoped_user_pref_update.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/test_utils.h"
diff --git a/chrome/browser/ash/app_mode/kiosk_crash_restore_browsertest.cc b/chrome/browser/ash/app_mode/kiosk_crash_restore_browsertest.cc
index 25c7590..9522097b 100644
--- a/chrome/browser/ash/app_mode/kiosk_crash_restore_browsertest.cc
+++ b/chrome/browser/ash/app_mode/kiosk_crash_restore_browsertest.cc
@@ -29,9 +29,9 @@
 #include "chromeos/ash/components/dbus/session_manager/fake_session_manager_client.h"
 #include "chromeos/ash/components/dbus/session_manager/session_manager_client.h"
 #include "chromeos/ash/components/dbus/userdataauth/userdataauth_client.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "chromeos/ash/components/settings/device_settings_cache.h"
 #include "components/ownership/mock_owner_key_util.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "components/policy/proto/device_management_backend.pb.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/test/browser_test.h"
diff --git a/chrome/browser/ash/app_mode/startup_app_launcher_unittest.cc b/chrome/browser/ash/app_mode/startup_app_launcher_unittest.cc
index 7fce8d3..a9df4241 100644
--- a/chrome/browser/ash/app_mode/startup_app_launcher_unittest.cc
+++ b/chrome/browser/ash/app_mode/startup_app_launcher_unittest.cc
@@ -50,9 +50,9 @@
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile_manager.h"
 #include "chromeos/ash/components/login/login_state/login_state.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
 #include "components/account_id/account_id.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "components/sync/model/string_ordinal.h"
 #include "components/user_manager/scoped_user_manager.h"
 #include "content/public/browser/browser_context.h"
diff --git a/chrome/browser/ash/app_mode/test/BUILD.gn b/chrome/browser/ash/app_mode/test/BUILD.gn
index 750339d6..63927ef97 100644
--- a/chrome/browser/ash/app_mode/test/BUILD.gn
+++ b/chrome/browser/ash/app_mode/test/BUILD.gn
@@ -51,6 +51,7 @@
     "//chrome/test:test_support",
     "//chrome/test:test_support_ui",
     "//chromeos/ash/components/disks:test_support",
+    "//chromeos/ash/components/policy/device_local_account",
     "//chromeos/ash/components/system",
     "//components/services/app_service/public/cpp:app_types",
     "//components/web_package",
@@ -91,6 +92,7 @@
     "//chrome/browser/chromeos/app_mode",
     "//chrome/test:test_support_ui",
     "//chromeos/ash/components/network:test_support",
+    "//chromeos/ash/components/policy/device_local_account",
     "//components/web_package",
   ]
 }
diff --git a/chrome/browser/ash/app_mode/test/kiosk_misconfigured_user_browsertest.cc b/chrome/browser/ash/app_mode/test/kiosk_misconfigured_user_browsertest.cc
index 25d34c0..ce16f6c 100644
--- a/chrome/browser/ash/app_mode/test/kiosk_misconfigured_user_browsertest.cc
+++ b/chrome/browser/ash/app_mode/test/kiosk_misconfigured_user_browsertest.cc
@@ -11,8 +11,8 @@
 #include "chrome/browser/ash/app_mode/test/kiosk_mixin.h"
 #include "chrome/browser/ash/app_mode/test/kiosk_test_utils.h"
 #include "chrome/test/base/mixin_based_in_process_browser_test.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "components/account_id/account_id.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "components/prefs/pref_service.h"
 #include "components/user_manager/user_directory_integrity_manager.h"
 #include "content/public/test/browser_test.h"
diff --git a/chrome/browser/ash/app_mode/test/kiosk_test_utils.cc b/chrome/browser/ash/app_mode/test/kiosk_test_utils.cc
index 7aba0d7..c70cf926 100644
--- a/chrome/browser/ash/app_mode/test/kiosk_test_utils.cc
+++ b/chrome/browser/ash/app_mode/test/kiosk_test_utils.cc
@@ -41,9 +41,9 @@
 #include "chrome/browser/ui/webui/ash/login/app_launch_splash_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/error_screen_handler.h"
 #include "chromeos/ash/components/dbus/session_manager/fake_session_manager_client.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
 #include "components/policy/core/common/cloud/test/policy_builder.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "extensions/browser/app_window/app_window.h"
 #include "extensions/browser/app_window/app_window_registry.h"
 #include "extensions/browser/extension_registry.h"
diff --git a/chrome/browser/ash/app_mode/test/kiosk_test_utils.h b/chrome/browser/ash/app_mode/test/kiosk_test_utils.h
index c2a7311d..ee6093e 100644
--- a/chrome/browser/ash/app_mode/test/kiosk_test_utils.h
+++ b/chrome/browser/ash/app_mode/test/kiosk_test_utils.h
@@ -14,9 +14,9 @@
 #include "chrome/browser/ash/app_mode/kiosk_app.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "components/account_id/account_id.h"
 #include "components/policy/core/common/cloud/test/policy_builder.h"
-#include "components/policy/core/common/device_local_account_type.h"
 
 namespace ash::kiosk::test {
 
diff --git a/chrome/browser/ash/boca/DEPS b/chrome/browser/ash/boca/DEPS
index a317330..bbbf67e 100644
--- a/chrome/browser/ash/boca/DEPS
+++ b/chrome/browser/ash/boca/DEPS
@@ -44,12 +44,15 @@
   "+components/sessions",
   # Files needed by babelorca/
   "+chrome/browser/ash/accessibility/live_caption/system_live_caption_service.h",
-  "+chrome/browser/browser_process.h",
   # Files needed by spotlight/
   "+chrome/browser/ash/policy/remote_commands/crd/public",
 ]
 
 specific_include_rules = {
+  "boca_manager_factory\\.cc": [
+    "+chrome/browser/browser_process.h",
+  ],
+
   "content_settings_handler_unittest.cc": [
       "+chrome/browser/content_settings/host_content_settings_map_factory.h",
   ],
diff --git a/chrome/browser/ash/child_accounts/time_limit_consistency_test/BUILD.gn b/chrome/browser/ash/child_accounts/time_limit_consistency_test/BUILD.gn
index bab2556..948c9e2c 100644
--- a/chrome/browser/ash/child_accounts/time_limit_consistency_test/BUILD.gn
+++ b/chrome/browser/ash/child_accounts/time_limit_consistency_test/BUILD.gn
@@ -33,7 +33,6 @@
     "//chrome/browser/ash/child_accounts",
     "//chrome/browser/ash/child_accounts:test_support",
     "//chromeos/ash/components/settings",
-    "//components/exo/wayland:test_controller_stub",
     "//components/exo/wayland:ui_controls_protocol_stub",
     "//testing/gmock",
     "//testing/gtest",
diff --git a/chrome/browser/ash/extensions/autotest_private/BUILD.gn b/chrome/browser/ash/extensions/autotest_private/BUILD.gn
index 80780f970..eae388d 100644
--- a/chrome/browser/ash/extensions/autotest_private/BUILD.gn
+++ b/chrome/browser/ash/extensions/autotest_private/BUILD.gn
@@ -63,7 +63,6 @@
     "//chromeos/ash/components/settings",
     "//chromeos/ash/experiences/arc",
     "//chromeos/ash/experiences/arc/mojom",
-    "//chromeos/ash/services/assistant:lib",
     "//chromeos/ash/services/assistant/public/cpp",
     "//chromeos/components/quick_answers/public/cpp",
     "//chromeos/components/quick_answers/public/cpp:prefs",
diff --git a/chrome/browser/ash/extensions/autotest_private/autotest_private_api.cc b/chrome/browser/ash/extensions/autotest_private/autotest_private_api.cc
index da4640a..37c807d 100644
--- a/chrome/browser/ash/extensions/autotest_private/autotest_private_api.cc
+++ b/chrome/browser/ash/extensions/autotest_private/autotest_private_api.cc
@@ -177,7 +177,6 @@
 #include "chromeos/ash/experiences/arc/session/arc_bridge_service.h"
 #include "chromeos/ash/experiences/arc/session/arc_service_manager.h"
 #include "chromeos/ash/experiences/arc/system_ui/arc_system_ui_bridge.h"
-#include "chromeos/ash/services/assistant/assistant_manager_service_impl.h"
 #include "chromeos/ash/services/assistant/public/cpp/assistant_prefs.h"
 #include "chromeos/ash/services/assistant/public/cpp/assistant_service.h"
 #include "chromeos/components/quick_answers/public/cpp/quick_answers_prefs.h"
@@ -3255,15 +3254,6 @@
     return RespondNow(Error("Assistant is already enabled."));
   }
 
-  // We can set this callback only when assistant status is NOT_READY. We should
-  // call this before we try to enable Assistant to avoid causing some timing
-  // issue.
-  ash::assistant::AssistantManagerServiceImpl::
-      SetInitializedInternalCallbackForTesting(base::BindOnce(
-          &AutotestPrivateEnableAssistantAndWaitForReadyFunction::
-              OnInitializedInternal,
-          this));
-
   Profile* profile = Profile::FromBrowserContext(browser_context());
   const std::string& err_msg = SetAllowedPref(
       profile, ash::assistant::prefs::kAssistantEnabled, base::Value(true));
diff --git a/chrome/browser/ash/extensions/file_manager/BUILD.gn b/chrome/browser/ash/extensions/file_manager/BUILD.gn
index 2365423d1..0c82e20 100644
--- a/chrome/browser/ash/extensions/file_manager/BUILD.gn
+++ b/chrome/browser/ash/extensions/file_manager/BUILD.gn
@@ -253,7 +253,6 @@
     ":file_manager",
     "//base",
     "//chrome/browser",
-    "//components/exo/wayland:test_controller",
     "//components/exo/wayland:ui_controls_protocol",
   ]
 }
@@ -265,7 +264,6 @@
     "//base",
     "//chrome/browser",
     "//chrome/browser/ash/fileapi",
-    "//components/exo/wayland:test_controller",
     "//components/exo/wayland:ui_controls_protocol",
   ]
 }
diff --git a/chrome/browser/ash/file_manager/BUILD.gn b/chrome/browser/ash/file_manager/BUILD.gn
index 5472676..4a0c5f7 100644
--- a/chrome/browser/ash/file_manager/BUILD.gn
+++ b/chrome/browser/ash/file_manager/BUILD.gn
@@ -445,6 +445,7 @@
     "//chrome/browser/ash/guest_os",
     "//chrome/browser/ash/guest_os/public",
     "//chrome/browser/ash/login/test:test_support",
+    "//chrome/browser/ash/policy/core:test_support",
     "//chrome/browser/ash/policy/dlp",
     "//chrome/browser/ash/policy/dlp/dialogs",
     "//chrome/browser/ash/policy/dlp/test:test_support",
@@ -485,6 +486,7 @@
     "//chromeos/ash/components/drivefs/mojom",
     "//chromeos/ash/components/file_manager:constants",
     "//chromeos/ash/components/settings",
+    "//chromeos/ash/components/settings:test_support",
     "//chromeos/ash/components/smbfs",
     "//chromeos/ash/components/smbfs/mojom",
     "//chromeos/ash/experiences/arc:arc_base_utils",
diff --git a/chrome/browser/ash/file_manager/app_service_file_tasks.cc b/chrome/browser/ash/file_manager/app_service_file_tasks.cc
index 83a4a076..24c4d2c4 100644
--- a/chrome/browser/ash/file_manager/app_service_file_tasks.cc
+++ b/chrome/browser/ash/file_manager/app_service_file_tasks.cc
@@ -265,8 +265,7 @@
     if (app_type == apps::AppType::kWeb ||
         app_type == apps::AppType::kSystemWeb) {
       // Check the origin trial and feature flag for file handling in web apps.
-      // TODO(1240018): Remove when this feature is fully launched. This check
-      // will not work for lacros web apps.
+      // TODO(crbug.com/255838199): Remove when this feature is fully launched.
       web_app::WebAppProvider* provider =
           web_app::WebAppProvider::GetDeprecated(profile_with_app_service);
       web_app::OsIntegrationManager& os_integration_manager =
diff --git a/chrome/browser/ash/file_manager/file_manager_browsertest.cc b/chrome/browser/ash/file_manager/file_manager_browsertest.cc
index 6825f0c..f9b294d 100644
--- a/chrome/browser/ash/file_manager/file_manager_browsertest.cc
+++ b/chrome/browser/ash/file_manager/file_manager_browsertest.cc
@@ -30,8 +30,7 @@
 #include "chrome/browser/ash/file_manager/io_task.h"
 #include "chrome/browser/ash/login/test/device_state_mixin.h"
 #include "chrome/browser/ash/login/test/logged_in_user_mixin.h"
-#include "chrome/browser/ash/settings/scoped_testing_cros_settings.h"
-#include "chrome/browser/ash/settings/stub_cros_settings_provider.h"
+#include "chrome/browser/ash/policy/core/device_policy_cros_test_helper.h"
 #include "chrome/browser/extensions/component_loader.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
@@ -42,6 +41,7 @@
 #include "chrome/test/base/fake_gaia_mixin.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
+#include "chromeos/ash/components/settings/device_settings_cache_test_support.h"
 #include "chromeos/constants/chromeos_features.h"
 #include "components/account_id/account_id.h"
 #include "components/download/public/common/download_item.h"
@@ -58,10 +58,11 @@
 
 using file_manager::test::TestCase;
 
+namespace em = enterprise_management;
+
 namespace file_manager {
 namespace {
 constexpr char kOwnerEmail[] = "owner@example.com";
-
 }  // namespace
 
 // FilesApp browser test.
@@ -121,6 +122,10 @@
         LogInTypeFor(GetOptions().test_account_type),
         /*include_initial_user=*/true,
         AccountIdFor(GetOptions().test_account_type));
+  }
+
+  void SetUpLocalStatePrefService(PrefService* local_state) override {
+    FilesAppBrowserTest::SetUpLocalStatePrefService(local_state);
 
     // Set up owner email of a device. We set up owner email only if a device is
     // kConsumerOwned. If a device is enrolled, an account cannot be an owner of
@@ -141,8 +146,14 @@
           break;
       }
 
-      scoped_testing_cros_settings_.device_settings()->Set(
-          ash::kDeviceOwner, base::Value(owner_email));
+      ash::device_settings_cache::Update(
+          local_state,
+          [&](em::PolicyData& policy) { policy.set_username(owner_email); });
+
+      policy_helper_.device_policy()->policy_data().set_username(owner_email);
+      policy_helper_.device_policy()->policy_data().set_management_mode(
+          em::PolicyData::LOCAL_OWNER);
+      policy_helper_.RefreshDevicePolicy();
     }
   }
 
@@ -172,7 +183,7 @@
   std::unique_ptr<ash::LoggedInUserMixin> logged_in_user_mixin_;
   std::unique_ptr<ash::DeviceStateMixin> device_state_mixin_;
 
-  ash::ScopedTestingCrosSettings scoped_testing_cros_settings_;
+  policy::DevicePolicyCrosTestHelper policy_helper_;
 };
 
 IN_PROC_BROWSER_TEST_P(LoggedInUserFilesAppBrowserTest, Test) {
diff --git a/chrome/browser/ash/file_manager/file_tasks.cc b/chrome/browser/ash/file_manager/file_tasks.cc
index bad56155..b851df2 100644
--- a/chrome/browser/ash/file_manager/file_tasks.cc
+++ b/chrome/browser/ash/file_manager/file_tasks.cc
@@ -860,8 +860,6 @@
       task.task_type == TASK_TYPE_BRUSCHETTA_APP ||
       task.task_type == TASK_TYPE_CROSTINI_APP ||
       task.task_type == TASK_TYPE_PLUGIN_VM_APP) {
-    // TODO(petermarshall): Implement GetProfileForExtensionTask in Lacros if
-    // necessary, for Chrome Apps.
     extensions::app_file_handler_util::MimeTypeCollector* mime_collector =
         new extensions::app_file_handler_util::MimeTypeCollector(profile);
     mime_collector->CollectForURLs(
diff --git a/chrome/browser/ash/file_manager/office_file_tasks.cc b/chrome/browser/ash/file_manager/office_file_tasks.cc
index 70066da..d705bfd 100644
--- a/chrome/browser/ash/file_manager/office_file_tasks.cc
+++ b/chrome/browser/ash/file_manager/office_file_tasks.cc
@@ -459,8 +459,7 @@
   if (!proxy) {
     return false;
   }
-  // The AppRegistryCache will contain the QuickOffice extension whether on Ash
-  // or Lacros.
+  // The AppRegistryCache will contain the QuickOffice extension on Ash.
   bool installed = false;
   proxy->AppRegistryCache().ForOneApp(
       extension_misc::kQuickOfficeComponentExtensionId,
diff --git a/chrome/browser/ash/fileapi/recent_model.cc b/chrome/browser/ash/fileapi/recent_model.cc
index 2b4ac7f..fa16d03 100644
--- a/chrome/browser/ash/fileapi/recent_model.cc
+++ b/chrome/browser/ash/fileapi/recent_model.cc
@@ -90,11 +90,10 @@
     if (!volume || volume->type() != file_manager::VOLUME_TYPE_PROVIDED ||
         volume->file_system_type() == file_manager::util::kFuseBox) {
       // Provided volume types are served via two file system types: fusebox
-      // (usable from ash or lacros, but requires ChromeOS' /usr/bin/fusebox
-      // daemon process to be running) and non-fusebox (ash only, no separate
-      // process required). The Files app runs in ash and could use either.
-      // Using both would return duplicate results. We therefore filter out
-      // the fusebox file system type.
+      // (requires ChromeOS' /usr/bin/fusebox daemon process to be running) and
+      // non-fusebox. The Files app runs in ash and could use either. Using both
+      // would return duplicate results. We therefore filter out the fusebox
+      // file system type.
       continue;
     }
     sources.emplace_back(std::make_unique<RecentDiskSource>(
diff --git a/chrome/browser/ash/fusebox/README.md b/chrome/browser/ash/fusebox/README.md
index cf28293..37e2807 100644
--- a/chrome/browser/ash/fusebox/README.md
+++ b/chrome/browser/ash/fusebox/README.md
@@ -7,16 +7,17 @@
 FUSE](https://www.kernel.org/doc/html/latest/filesystems/fuse.html) protocol.
 
 It enables sharing virtual-file-like things *across processes* (e.g. between
-ash-chrome and lacros-chrome) or *with Virtual Machines* (e.g. the Android or
-Crostini VMs) just by sharing a string file name or an integer file descriptor.
+ash-chrome and lacros-chrome, although lacros-chrome is now deprecated) or
+*with Virtual Machines* (e.g. the Android or Crostini VMs) just by sharing a
+string file name or an integer file descriptor.
 
 Fusebox doesn't *replace* the `storage` C++ API. It provides *an alternative
 mechanism* for accessing those virtual files. Workflows that stay entirely
 within ash-chrome can continue to use the C++ API. But when the GMail web-app
-(running in a sandboxed lacros-chrome process) wants to upload files from a
-phone attached to a Chromebook via USB cable, and the MTP (Media Transfer
-Protocol) volume (virtual directory) is served by ash-chrome code, that access
-is facilitated by Fusebox.
+(running in a sandboxed and now deprecated lacros-chrome process) wants to
+upload files from a phone attached to a Chromebook via USB cable, and the MTP
+(Media Transfer Protocol) volume (virtual directory) is served by ash-chrome
+code, that access is facilitated by Fusebox.
 
 
 ## Structure
diff --git a/chrome/browser/ash/login/BUILD.gn b/chrome/browser/ash/login/BUILD.gn
index 54e2aab..ff84d71 100644
--- a/chrome/browser/ash/login/BUILD.gn
+++ b/chrome/browser/ash/login/BUILD.gn
@@ -474,6 +474,7 @@
     "//chromeos/ash/components/install_attributes:test_support",
     "//chromeos/ash/components/language_preferences",
     "//chromeos/ash/components/network:test_support",
+    "//chromeos/ash/components/policy/device_local_account",
     "//chromeos/ash/components/settings",
     "//chromeos/ash/components/system",
     "//chromeos/ash/components/timezone",
@@ -593,6 +594,7 @@
     "//chromeos/ash/components/login/auth",
     "//chromeos/ash/components/login/auth/public:authpublic",
     "//chromeos/ash/components/network:test_support",
+    "//chromeos/ash/components/policy/device_local_account",
     "//chromeos/ash/components/settings",
     "//chromeos/ash/components/system",
     "//chromeos/ash/services/network_config/public/cpp:test_support",
diff --git a/chrome/browser/ash/login/app_mode/kiosk_launch_controller_unittest.cc b/chrome/browser/ash/login/app_mode/kiosk_launch_controller_unittest.cc
index f493775..3c78cf9c 100644
--- a/chrome/browser/ash/login/app_mode/kiosk_launch_controller_unittest.cc
+++ b/chrome/browser/ash/login/app_mode/kiosk_launch_controller_unittest.cc
@@ -60,10 +60,10 @@
 #include "chrome/test/base/testing_profile_manager.h"
 #include "chromeos/ash/components/install_attributes/stub_install_attributes.h"
 #include "chromeos/ash/components/network/network_handler.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "components/account_id/account_id.h"
 #include "components/crash/core/common/crash_key.h"
 #include "components/policy/core/browser/browser_policy_connector_base.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "components/policy/core/common/policy_map.h"
 #include "components/policy/core/common/policy_types.h"
 #include "components/policy/policy_constants.h"
diff --git a/chrome/browser/ash/login/app_mode/test/BUILD.gn b/chrome/browser/ash/login/app_mode/test/BUILD.gn
index 2f5122a96..e2202da 100644
--- a/chrome/browser/ash/login/app_mode/test/BUILD.gn
+++ b/chrome/browser/ash/login/app_mode/test/BUILD.gn
@@ -58,6 +58,7 @@
     "//chrome/browser/chromeos/app_mode",
     "//chrome/browser/ui",
     "//chrome/browser/ui/ash/login",
+    "//chromeos/ash/components/policy/device_local_account",
     "//components/policy:generated",
     "//components/policy:policy_code_generate",
     "//components/policy/core/common",
diff --git a/chrome/browser/ash/login/app_mode/test/auto_launched_kiosk_browsertest.cc b/chrome/browser/ash/login/app_mode/test/auto_launched_kiosk_browsertest.cc
index 89000d3..2a07ff18 100644
--- a/chrome/browser/ash/login/app_mode/test/auto_launched_kiosk_browsertest.cc
+++ b/chrome/browser/ash/login/app_mode/test/auto_launched_kiosk_browsertest.cc
@@ -38,8 +38,8 @@
 #include "chrome/test/base/mixin_based_in_process_browser_test.h"
 #include "chromeos/ash/components/dbus/session_manager/fake_session_manager_client.h"
 #include "chromeos/ash/components/dbus/session_manager/session_manager_client.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "components/crx_file/crx_verifier.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/test_utils.h"
diff --git a/chrome/browser/ash/login/app_mode/test/web_kiosk_base_test.cc b/chrome/browser/ash/login/app_mode/test/web_kiosk_base_test.cc
index ec7ff02..b05188b8 100644
--- a/chrome/browser/ash/login/app_mode/test/web_kiosk_base_test.cc
+++ b/chrome/browser/ash/login/app_mode/test/web_kiosk_base_test.cc
@@ -19,8 +19,8 @@
 #include "chrome/browser/ash/ownership/fake_owner_settings_service.h"  // IWYU pragma: keep
 #include "chrome/browser/ash/policy/core/device_local_account.h"
 #include "chrome/browser/ui/browser_list.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "components/account_id/account_id.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
diff --git a/chrome/browser/ash/login/demo_mode/BUILD.gn b/chrome/browser/ash/login/demo_mode/BUILD.gn
index 68abaa0..f1a8b11 100644
--- a/chrome/browser/ash/login/demo_mode/BUILD.gn
+++ b/chrome/browser/ash/login/demo_mode/BUILD.gn
@@ -252,6 +252,7 @@
     "//chromeos/ash/components/demo_mode",
     "//chromeos/ash/components/install_attributes:test_support",
     "//chromeos/ash/components/login/auth",
+    "//chromeos/ash/components/policy/device_local_account",
     "//chromeos/ash/components/system",
     "//chromeos/constants",
     "//components/component_updater/ash:test_support",
diff --git a/chrome/browser/ash/login/demo_mode/demo_login_controller_unittest.cc b/chrome/browser/ash/login/demo_mode/demo_login_controller_unittest.cc
index b18fc950..c6f2338 100644
--- a/chrome/browser/ash/login/demo_mode/demo_login_controller_unittest.cc
+++ b/chrome/browser/ash/login/demo_mode/demo_login_controller_unittest.cc
@@ -25,6 +25,7 @@
 #include "chromeos/ash/components/dbus/session_manager/fake_session_manager_client.h"
 #include "chromeos/ash/components/demo_mode/utils/demo_session_utils.h"
 #include "chromeos/ash/components/install_attributes/stub_install_attributes.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
 #include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "chromeos/dbus/power/fake_power_manager_client.h"
@@ -34,7 +35,6 @@
 #include "components/policy/core/common/cloud/mock_cloud_policy_manager.h"
 #include "components/policy/core/common/cloud/mock_cloud_policy_service.h"
 #include "components/policy/core/common/cloud/mock_cloud_policy_store.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "components/session_manager/core/session_manager.h"
 #include "components/user_manager/scoped_user_manager.h"
 #include "content/public/test/browser_task_environment.h"
diff --git a/chrome/browser/ash/login/existing_user_controller_auto_login_unittest.cc b/chrome/browser/ash/login/existing_user_controller_auto_login_unittest.cc
index 3f8b383..4fe5cd3 100644
--- a/chrome/browser/ash/login/existing_user_controller_auto_login_unittest.cc
+++ b/chrome/browser/ash/login/existing_user_controller_auto_login_unittest.cc
@@ -16,10 +16,10 @@
 #include "chrome/test/base/testing_browser_process.h"
 #include "chromeos/ash/components/dbus/session_manager/fake_session_manager_client.h"
 #include "chromeos/ash/components/login/auth/auth_events_recorder.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "chromeos/ash/components/settings/cros_settings.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
 #include "components/ownership/mock_owner_key_util.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "components/session_manager/core/session_manager.h"
 #include "components/user_manager/scoped_user_manager.h"
 #include "content/public/test/browser_task_environment.h"
diff --git a/chrome/browser/ash/login/existing_user_controller_browsertest.cc b/chrome/browser/ash/login/existing_user_controller_browsertest.cc
index 64618f4..f8d48df 100644
--- a/chrome/browser/ash/login/existing_user_controller_browsertest.cc
+++ b/chrome/browser/ash/login/existing_user_controller_browsertest.cc
@@ -68,6 +68,7 @@
 #include "chromeos/ash/components/login/auth/public/user_context.h"
 #include "chromeos/ash/components/login/auth/stub_authenticator_builder.h"
 #include "chromeos/ash/components/network/network_state_test_helper.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "chromeos/ash/components/settings/cros_settings.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
 #include "chromeos/ash/components/settings/cros_settings_provider.h"
@@ -81,7 +82,6 @@
 #include "components/policy/core/common/cloud/cloud_policy_store.h"
 #include "components/policy/core/common/cloud/mock_cloud_policy_store.h"
 #include "components/policy/core/common/cloud/test/policy_builder.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "components/policy/core/common/mock_configuration_policy_provider.h"
 #include "components/policy/policy_constants.h"
 #include "components/policy/proto/chrome_device_policy.pb.h"
diff --git a/chrome/browser/ash/login/reporting/BUILD.gn b/chrome/browser/ash/login/reporting/BUILD.gn
index 8ea10b6..fbbd24c 100644
--- a/chrome/browser/ash/login/reporting/BUILD.gn
+++ b/chrome/browser/ash/login/reporting/BUILD.gn
@@ -30,6 +30,7 @@
     "//chrome/browser/ash/profiles",
     "//chrome/browser/profiles:profile_util",
     "//chromeos/ash/components/login/auth/public:authpublic",
+    "//chromeos/ash/components/policy/device_local_account",
     "//chromeos/ash/components/settings",
     "//components/policy/core/common",
     "//components/prefs",
@@ -128,6 +129,7 @@
     "//chrome/test:test_support",
     "//chromeos/ash/components/login/auth/public:authpublic",
     "//chromeos/ash/components/login/session",
+    "//chromeos/ash/components/policy/device_local_account",
     "//chromeos/dbus/power",
     "//components/policy/core/common",
     "//components/reporting/client:test_support",
diff --git a/chrome/browser/ash/login/reporting/login_logout_reporter.cc b/chrome/browser/ash/login/reporting/login_logout_reporter.cc
index 50ad4f0..ce01dd4d 100644
--- a/chrome/browser/ash/login/reporting/login_logout_reporter.cc
+++ b/chrome/browser/ash/login/reporting/login_logout_reporter.cc
@@ -18,8 +18,8 @@
 #include "chrome/browser/policy/messaging_layer/proto/synced/login_logout_event.pb.h"
 #include "chrome/browser/profiles/reporting_util.h"
 #include "chromeos/ash/components/login/auth/public/auth_failure.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "components/account_id/account_id.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 #include "components/prefs/scoped_user_pref_update.h"
diff --git a/chrome/browser/ash/login/reporting/login_logout_reporter_browsertest.cc b/chrome/browser/ash/login/reporting/login_logout_reporter_browsertest.cc
index 889be61..d28631e 100644
--- a/chrome/browser/ash/login/reporting/login_logout_reporter_browsertest.cc
+++ b/chrome/browser/ash/login/reporting/login_logout_reporter_browsertest.cc
@@ -44,13 +44,13 @@
 #include "chromeos/ash/components/login/auth/public/user_context.h"
 #include "chromeos/ash/components/login/auth/stub_authenticator_builder.h"
 #include "chromeos/ash/components/login/login_state/login_state.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
 #include "chromeos/dbus/missive/missive_client.h"
 #include "chromeos/dbus/missive/missive_client_test_observer.h"
 #include "components/account_id/account_id.h"
 #include "components/policy/core/common/cloud/cloud_policy_store.h"
 #include "components/policy/core/common/cloud/mock_cloud_policy_store.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "components/policy/proto/chrome_device_policy.pb.h"
 #include "components/policy/proto/device_management_backend.pb.h"
 #include "components/reporting/proto/synced/record.pb.h"
diff --git a/chrome/browser/ash/login/reporting/login_logout_reporter_unittest.cc b/chrome/browser/ash/login/reporting/login_logout_reporter_unittest.cc
index ed3efac2..ac32d06e 100644
--- a/chrome/browser/ash/login/reporting/login_logout_reporter_unittest.cc
+++ b/chrome/browser/ash/login/reporting/login_logout_reporter_unittest.cc
@@ -17,8 +17,8 @@
 #include "chrome/test/base/testing_profile.h"
 #include "chromeos/ash/components/login/auth/public/auth_failure.h"
 #include "chromeos/ash/components/login/session/session_termination_manager.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "chromeos/dbus/power/fake_power_manager_client.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "components/reporting/client/mock_report_queue.h"
 #include "components/user_manager/scoped_user_manager.h"
 #include "components/user_manager/user_names.h"
diff --git a/chrome/browser/ash/login/screens/BUILD.gn b/chrome/browser/ash/login/screens/BUILD.gn
index 64d02ed2..05942b9 100644
--- a/chrome/browser/ash/login/screens/BUILD.gn
+++ b/chrome/browser/ash/login/screens/BUILD.gn
@@ -405,7 +405,6 @@
     "add_child_screen_browsertest.cc",
     "ai_intro_screen_browsertest.cc",
     "app_downloading_screen_browsertest.cc",
-    "assistant_optin_flow_screen_browsertest.cc",
     "categories_selection_screen_browsertest.cc",
     "choobe_screen_browsertest.cc",
     "consolidated_consent_screen_browsertest.cc",
@@ -513,6 +512,7 @@
     "//chromeos/ash/components/network",
     "//chromeos/ash/components/network:test_support",
     "//chromeos/ash/components/osauth/public",
+    "//chromeos/ash/components/policy/device_local_account",
     "//chromeos/ash/components/quick_start",
     "//chromeos/ash/components/settings",
     "//chromeos/ash/components/system",
@@ -521,7 +521,6 @@
     "//chromeos/ash/experiences/arc:prefs",
     "//chromeos/ash/experiences/arc/session",
     "//chromeos/ash/experiences/arc/session:arc_base_enums",
-    "//chromeos/ash/services/assistant:lib",
     "//chromeos/ash/services/assistant/public/cpp",
     "//chromeos/ash/services/assistant/public/proto",
     "//chromeos/ash/services/bluetooth_config/public/mojom:mojom_shared_cpp_sources",
diff --git a/chrome/browser/ash/login/screens/assistant_optin_flow_screen_browsertest.cc b/chrome/browser/ash/login/screens/assistant_optin_flow_screen_browsertest.cc
deleted file mode 100644
index 004c373..0000000
--- a/chrome/browser/ash/login/screens/assistant_optin_flow_screen_browsertest.cc
+++ /dev/null
@@ -1,1186 +0,0 @@
-// 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.
-
-#include "chrome/browser/ash/login/screens/assistant_optin_flow_screen.h"
-
-#include <memory>
-#include <set>
-#include <string_view>
-
-#include "ash/constants/ash_features.h"
-#include "base/files/file_path.h"
-#include "base/functional/bind.h"
-#include "base/metrics/histogram_base.h"
-#include "base/path_service.h"
-#include "base/strings/strcat.h"
-#include "base/test/metrics/histogram_tester.h"
-#include "base/test/test_future.h"
-#include "chrome/browser/ash/login/login_wizard.h"
-#include "chrome/browser/ash/login/oobe_screen.h"
-#include "chrome/browser/ash/login/screens/sync_consent_screen.h"
-#include "chrome/browser/ash/login/test/js_checker.h"
-#include "chrome/browser/ash/login/test/login_manager_mixin.h"
-#include "chrome/browser/ash/login/test/oobe_base_test.h"
-#include "chrome/browser/ash/login/test/oobe_screen_exit_waiter.h"
-#include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
-#include "chrome/browser/ash/login/wizard_controller.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/browser/ui/ash/login/login_display_host.h"
-#include "chrome/browser/ui/webui/ash/login/assistant_optin_flow_screen_handler.h"
-#include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h"
-#include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
-#include "chrome/common/chrome_paths.h"
-#include "chromeos/ash/services/assistant/public/cpp/assistant_prefs.h"
-#include "chromeos/ash/services/assistant/public/cpp/assistant_settings.h"
-#include "chromeos/ash/services/assistant/public/cpp/features.h"
-#include "chromeos/ash/services/assistant/public/proto/activity_control_settings_common.pb.h"
-#include "chromeos/ash/services/assistant/public/proto/get_settings_ui.pb.h"
-#include "chromeos/ash/services/assistant/public/proto/settings_ui.pb.h"
-#include "chromeos/ash/services/assistant/service.h"
-#include "components/prefs/pref_service.h"
-#include "content/public/test/browser_test.h"
-#include "mojo/public/cpp/bindings/pending_receiver.h"
-#include "mojo/public/cpp/bindings/pending_remote.h"
-#include "mojo/public/cpp/bindings/receiver_set.h"
-#include "mojo/public/cpp/bindings/remote.h"
-#include "net/dns/mock_host_resolver.h"
-#include "net/test/embedded_test_server/default_handlers.h"
-#include "net/test/embedded_test_server/embedded_test_server.h"
-#include "net/test/embedded_test_server/http_request.h"
-#include "net/test/embedded_test_server/http_response.h"
-
-namespace ash {
-
-namespace {
-
-using ::net::test_server::BasicHttpResponse;
-using ::net::test_server::HttpRequest;
-using ::net::test_server::HttpResponse;
-
-constexpr char kTestUser[] = "test-user1@gmail.com";
-
-constexpr char kAssistantConsentToken[] = "consent_token";
-constexpr char kAssistantUiAuditKey[] = "ui_audit_key";
-
-constexpr char kAssistantOptInId[] = "assistant-optin-flow";
-constexpr char kAssistantOptInFlowCard[] = "card";
-constexpr char kLoading[] = "loading";
-constexpr char kValueProp[] = "valueProp";
-constexpr char kRelatedInfo[] = "relatedInfo";
-constexpr char kVoiceMatch[] = "voiceMatch";
-
-constexpr char kSettingsZippyTitle[] = "Settings-Zippy-Title";
-constexpr char kSettingsZippyDescription[] = "Settings-Zippy-Description";
-constexpr char kSettingsZippyAdditionalInfo[] =
-    "Settings-Zippy-Additional-Info";
-constexpr char kSettingsZippyLearnMoreLink[] = "Learn more";
-
-// &ensp;
-constexpr char kEnsp[] = "\xe2\x80\x82";
-
-const test::UIPath kAssistantLoading = {kAssistantOptInId,
-                                        kAssistantOptInFlowCard, kLoading};
-const test::UIPath kLoadingRetryButton = {
-    kAssistantOptInId, kAssistantOptInFlowCard, kLoading, "retry-button"};
-const test::UIPath kSettingsZippyTitleFirst = {
-    kAssistantOptInId, kAssistantOptInFlowCard, kValueProp, "title-0"};
-const test::UIPath kSettingsZippyDescriptionFirst = {
-    kAssistantOptInId, kAssistantOptInFlowCard, kValueProp, "description-0"};
-const test::UIPath kSettingsZippyAdditionalInfoFirst = {
-    kAssistantOptInId, kAssistantOptInFlowCard, kValueProp,
-    "additional-info-0"};
-
-const test::UIPath kAssistantValueProp = {kAssistantOptInId,
-                                          kAssistantOptInFlowCard, kValueProp};
-const test::UIPath kValuePropNextButton = {
-    kAssistantOptInId, kAssistantOptInFlowCard, kValueProp, "next-button"};
-const test::UIPath kValuePropSkipButton = {
-    kAssistantOptInId, kAssistantOptInFlowCard, kValueProp, "skip-button"};
-
-const test::UIPath kAssistantRelatedInfo = {
-    kAssistantOptInId, kAssistantOptInFlowCard, kRelatedInfo};
-const test::UIPath kRelatedInfoNextButton = {
-    kAssistantOptInId, kAssistantOptInFlowCard, kRelatedInfo, "next-button"};
-const test::UIPath kRelatedInfoSkipButton = {
-    kAssistantOptInId, kAssistantOptInFlowCard, kRelatedInfo, "skip-button"};
-
-const test::UIPath kAssistantVoiceMatch = {
-    kAssistantOptInId, kAssistantOptInFlowCard, kVoiceMatch};
-const test::UIPath kVoiceMatchAgreeButton = {
-    kAssistantOptInId, kAssistantOptInFlowCard, kVoiceMatch, "agree-button"};
-const test::UIPath kVoiceMatchLaterButton = {
-    kAssistantOptInId, kAssistantOptInFlowCard, kVoiceMatch, "later-button"};
-const test::UIPath kVoiceMatchEntry0 = {
-    kAssistantOptInId, kAssistantOptInFlowCard, kVoiceMatch, "voice-entry-0"};
-const test::UIPath kVoiceMatchEntry1 = {
-    kAssistantOptInId, kAssistantOptInFlowCard, kVoiceMatch, "voice-entry-1"};
-const test::UIPath kVoiceMatchEntry2 = {
-    kAssistantOptInId, kAssistantOptInFlowCard, kVoiceMatch, "voice-entry-2"};
-const test::UIPath kVoiceMatchEntry3 = {
-    kAssistantOptInId, kAssistantOptInFlowCard, kVoiceMatch, "voice-entry-3"};
-
-constexpr char kAssistantOptInScreenExitReason[] =
-    "OOBE.StepCompletionTimeByExitReason.Assistant-optin-flow.Next";
-constexpr char kAssistantOptInScreenStepCompletionTime[] =
-    "OOBE.StepCompletionTime.Assistant-optin-flow";
-
-class ScopedAssistantSettings : public assistant::AssistantSettings {
- public:
-  // Flags to configure GetSettings response.
-  static constexpr int CONSENT_UI_FLAGS_NONE = 0;
-  static constexpr int CONSENT_UI_FLAG_SKIP_ACTIVITY_CONTROL = 1;
-  static constexpr int CONSENT_UI_FLAG_WAA_DISABLED_BY_POLICY = 1 << 1;
-  static constexpr int CONSENT_UI_FLAG_ASSISTANT_DISABLED_BY_POLICY = 1 << 2;
-
-  enum class SpeakerIdEnrollmentMode {
-    // On speaker enrollment request, the client will be notified that the
-    // enrollment is done immediately.
-    IMMEDIATE,
-    // Speaker enrollment requests will not respond immediately, test will have
-    // to run through enrollment responses by calling
-    // AdvanceSpeakerIdEnrollment().
-    STEP_BY_STEP
-  };
-
-  enum class OptIn {
-    ACTIVITY_CONTROL,
-    EMAIL,
-  };
-
-  ScopedAssistantSettings() = default;
-
-  ScopedAssistantSettings(const ScopedAssistantSettings&) = delete;
-  ScopedAssistantSettings& operator=(const ScopedAssistantSettings&) = delete;
-
-  ~ScopedAssistantSettings() override = default;
-
-  void set_consent_ui_flags(int flags) { consent_ui_flags_ = flags; }
-
-  void set_speaker_id_enrollment_mode(SpeakerIdEnrollmentMode mode) {
-    speaker_id_enrollment_mode_ = mode;
-  }
-
-  void set_setting_zippy_size(int size) { setting_zippy_size_ = size; }
-
-  void set_is_minor_user(bool is_minor_user) { is_minor_user_ = is_minor_user; }
-
-  const std::set<OptIn>& collected_optins() const { return collected_optins_; }
-
-  // Advances speaker ID enrollment to the next state.
-  // Returns whether the speaker ID enrollment state changed, which amounts to
-  // whether the speaker ID enrollment is currently in progress.
-  bool AdvanceSpeakerIdEnrollmentState() {
-    switch (speaker_id_enrollment_state_) {
-      case SpeakerIdEnrollmentState::IDLE:
-        return false;
-      case SpeakerIdEnrollmentState::REQUESTED:
-        speaker_id_enrollment_state_ = SpeakerIdEnrollmentState::LISTENING;
-        speaker_id_enrollment_client_->OnListeningHotword();
-        return true;
-      case SpeakerIdEnrollmentState::LISTENING:
-        speaker_id_enrollment_state_ = SpeakerIdEnrollmentState::PROCESSING;
-        speaker_id_enrollment_client_->OnProcessingHotword();
-        return true;
-      case SpeakerIdEnrollmentState::PROCESSING:
-        ++processed_hotwords_;
-        if (processed_hotwords_ == 4) {
-          speaker_id_enrollment_state_ = SpeakerIdEnrollmentState::IDLE;
-          speaker_id_enrollment_client_->OnSpeakerIdEnrollmentDone();
-        } else {
-          speaker_id_enrollment_state_ = SpeakerIdEnrollmentState::LISTENING;
-          speaker_id_enrollment_client_->OnListeningHotword();
-        }
-        return true;
-    }
-    return false;
-  }
-
-  bool IsSpeakerIdEnrollmentActive() const {
-    return speaker_id_enrollment_state_ != SpeakerIdEnrollmentState::IDLE;
-  }
-
-  void FailSpeakerIdEnrollment() {
-    ASSERT_NE(speaker_id_enrollment_state_, SpeakerIdEnrollmentState::IDLE);
-    speaker_id_enrollment_client_->OnSpeakerIdEnrollmentFailure();
-    processed_hotwords_ = 0;
-    speaker_id_enrollment_state_ = SpeakerIdEnrollmentState::IDLE;
-  }
-
-  // assistant::AssistantSettings:
-  void GetSettings(const std::string& selector,
-                   GetSettingsCallback callback) override {}
-
-  void GetSettingsWithHeader(const std::string& selector,
-                             GetSettingsCallback callback) override {
-    assistant::SettingsUiSelector selector_proto;
-    ASSERT_TRUE(selector_proto.ParseFromString(selector));
-    EXPECT_FALSE(selector_proto.about_me_settings());
-    EXPECT_TRUE(selector_proto.has_consent_flow_ui_selector());
-    EXPECT_EQ(assistant::ActivityControlSettingsUiSelector::
-                  ASSISTANT_SUW_ONBOARDING_ON_CHROME_OS,
-              selector_proto.consent_flow_ui_selector().flow_id());
-
-    assistant::GetSettingsUiResponse response;
-
-    if (is_minor_user_) {
-      auto* header = response.mutable_header();
-      header->set_footer_button_layout(
-          assistant::SettingsResponseHeader_AcceptRejectLayout_EQUAL_WEIGHT);
-    }
-
-    auto* settings_ui = response.mutable_settings();
-    auto* gaia_user_context_ui = settings_ui->mutable_gaia_user_context_ui();
-    gaia_user_context_ui->set_is_gaia_user(true);
-    gaia_user_context_ui->set_waa_disabled_by_dasher_domain(
-        (consent_ui_flags_ & CONSENT_UI_FLAG_WAA_DISABLED_BY_POLICY));
-    gaia_user_context_ui->set_assistant_disabled_by_dasher_domain(
-        (consent_ui_flags_ & CONSENT_UI_FLAG_ASSISTANT_DISABLED_BY_POLICY));
-
-    auto* consent_flow_ui = settings_ui->mutable_consent_flow_ui();
-    consent_flow_ui->set_consent_status(
-        assistant::ConsentFlowUi_ConsentStatus_ASK_FOR_CONSENT);
-    consent_flow_ui->mutable_consent_ui()->set_accept_button_text("OK");
-    consent_flow_ui->mutable_consent_ui()->set_reject_button_text(
-        "No, thank you");
-
-    if (!(consent_ui_flags_ & CONSENT_UI_FLAG_SKIP_ACTIVITY_CONTROL)) {
-      PopulateActivityControlData(consent_flow_ui->mutable_consent_ui());
-      for (int i = 0; i < setting_zippy_size_; i++) {
-        auto* multi_consent_ui = consent_flow_ui->add_multi_consent_ui();
-        PopulateActivityControlData(multi_consent_ui);
-      }
-    }
-
-    std::string message;
-    EXPECT_TRUE(response.SerializeToString(&message));
-    std::move(callback).Run(message);
-  }
-
-  void PopulateActivityControlData(
-      assistant::ConsentFlowUi_ConsentUi* consent_ui) {
-    auto* activity_control_ui = consent_ui->mutable_activity_control_ui();
-    activity_control_ui->set_consent_token(kAssistantConsentToken);
-    activity_control_ui->set_ui_audit_key(kAssistantUiAuditKey);
-    activity_control_ui->set_title("Title");
-    activity_control_ui->set_identity(kTestUser);
-    activity_control_ui->add_intro_text_paragraph();
-    activity_control_ui->set_intro_text_paragraph(0, "Here's an intro");
-    activity_control_ui->add_footer_paragraph();
-    activity_control_ui->set_footer_paragraph(0, "A footer");
-    auto* setting = activity_control_ui->add_setting_zippy();
-    setting->set_title(kSettingsZippyTitle);
-    setting->add_description_paragraph();
-    setting->set_description_paragraph(0, kSettingsZippyDescription);
-    setting->add_additional_info_paragraph();
-    setting->set_additional_info_paragraph(0, kSettingsZippyAdditionalInfo);
-    setting->set_icon_uri("assistant_icon");
-    setting->set_setting_set_id(assistant::SettingSetId::WAA);
-  }
-
-  void UpdateSettings(const std::string& update,
-                      UpdateSettingsCallback callback) override {
-    assistant::SettingsUiUpdate update_proto;
-    ASSERT_TRUE(update_proto.ParseFromString(update));
-    EXPECT_FALSE(update_proto.has_about_me_settings_update());
-    EXPECT_FALSE(update_proto.has_assistant_device_settings_update());
-
-    assistant::SettingsUiUpdateResult update_result;
-    if (update_proto.has_consent_flow_ui_update()) {
-      EXPECT_EQ(kAssistantConsentToken,
-                update_proto.consent_flow_ui_update().consent_token());
-      EXPECT_FALSE(
-          update_proto.consent_flow_ui_update().saw_third_party_disclosure());
-      EXPECT_EQ(assistant::ActivityControlSettingsUiSelector::
-                    ASSISTANT_SUW_ONBOARDING_ON_CHROME_OS,
-                update_proto.consent_flow_ui_update().flow_id());
-      collected_optins_.insert(OptIn::ACTIVITY_CONTROL);
-      update_result.mutable_consent_flow_update_result()->set_update_status(
-          assistant::ConsentFlowUiUpdateResult::SUCCESS);
-    }
-    std::string message;
-    EXPECT_TRUE(update_result.SerializeToString(&message));
-    std::move(callback).Run(message);
-  }
-
-  void StartSpeakerIdEnrollment(
-      bool skip_cloud_enrollment,
-      base::WeakPtr<assistant::SpeakerIdEnrollmentClient> client) override {
-    if (speaker_id_enrollment_mode_ == SpeakerIdEnrollmentMode::IMMEDIATE) {
-      client->OnSpeakerIdEnrollmentDone();
-      return;
-    }
-    ASSERT_FALSE(speaker_id_enrollment_client_);
-    processed_hotwords_ = 0;
-    speaker_id_enrollment_client_ = std::move(client);
-    speaker_id_enrollment_state_ = SpeakerIdEnrollmentState::REQUESTED;
-  }
-
-  void StopSpeakerIdEnrollment() override {
-    processed_hotwords_ = 0;
-    speaker_id_enrollment_state_ = SpeakerIdEnrollmentState::IDLE;
-    speaker_id_enrollment_client_.reset();
-  }
-
-  void SyncSpeakerIdEnrollmentStatus() override {}
-
- private:
-  enum class SpeakerIdEnrollmentState {
-    IDLE,
-    REQUESTED,
-    LISTENING,
-    PROCESSING
-  };
-
-  // The service test config:
-  int consent_ui_flags_ = CONSENT_UI_FLAGS_NONE;
-  SpeakerIdEnrollmentMode speaker_id_enrollment_mode_ =
-      SpeakerIdEnrollmentMode::IMMEDIATE;
-
-  // Speaker ID enrollment state:
-  SpeakerIdEnrollmentState speaker_id_enrollment_state_ =
-      SpeakerIdEnrollmentState::IDLE;
-  base::WeakPtr<assistant::SpeakerIdEnrollmentClient>
-      speaker_id_enrollment_client_;
-  int processed_hotwords_ = 0;
-
-  // Set of opt ins given by the user.
-  std::set<OptIn> collected_optins_;
-
-  int setting_zippy_size_ = 1;
-  bool is_minor_user_ = false;
-};
-
-class AssistantOptInFlowBaseTest : public OobeBaseTest {
- public:
-  AssistantOptInFlowBaseTest() = default;
-  ~AssistantOptInFlowBaseTest() override = default;
-
-  void RegisterAdditionalRequestHandlers() override {
-    embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
-        &AssistantOptInFlowBaseTest::HandleRequest, base::Unretained(this)));
-  }
-
-  void SetUpOnMainThread() override {
-    OobeBaseTest::SetUpOnMainThread();
-    assistant_settings_ = std::make_unique<ScopedAssistantSettings>();
-
-    AssistantOptInFlowScreen* assistant_optin_flow_screen =
-        WizardController::default_controller()
-            ->GetScreen<AssistantOptInFlowScreen>();
-    original_callback_ =
-        assistant_optin_flow_screen->get_exit_callback_for_testing();
-    assistant_optin_flow_screen->set_exit_callback_for_testing(
-        base::BindRepeating(&AssistantOptInFlowBaseTest::HandleScreenExit,
-                            base::Unretained(this)));
-  }
-
-  void TearDownOnMainThread() override {
-    assistant_settings_.reset();
-    OobeBaseTest::TearDownOnMainThread();
-  }
-
-  void ShowAssistantOptInFlowScreen() {
-    login_manager_.LoginAsNewRegularUser();
-    OobeScreenExitWaiter(GetFirstSigninScreen()).Wait();
-    if (!screen_exited_) {
-      LoginDisplayHost::default_host()->StartWizard(
-          AssistantOptInFlowScreenView::kScreenId);
-    }
-  }
-
-  // Waits for the OOBE UI to complete initialization, and overrides:
-  // *   the assistant value prop webview URL with the one provided by embedded
-  //     https proxy.
-  // *   the timeout delay for sending done user action from voice match screen.
-  void SetUpAssistantScreensForTest() {
-    std::string url_template = embedded_test_server()
-                                   ->GetURL("/test_assistant/$/value_prop.html")
-                                   .spec();
-    test::OobeJS().Evaluate(test::GetOobeElementPath(kAssistantValueProp) +
-                            ".setUrlTemplateForTesting('" + url_template +
-                            "')");
-    test::OobeJS().Evaluate(test::GetOobeElementPath(kAssistantRelatedInfo) +
-                            ".setUrlTemplateForTesting('" + url_template +
-                            "')");
-    test::OobeJS().Evaluate(test::GetOobeElementPath(kAssistantVoiceMatch) +
-                            ".setDoneActionDelayForTesting(0)");
-  }
-
-  // Waits for the button specified by IDs in `button_path` to become enabled,
-  // and then taps it.
-  void TapWhenEnabled(std::initializer_list<std::string_view> button_path) {
-    test::OobeJS().CreateEnabledWaiter(true, button_path)->Wait();
-    test::OobeJS().TapOnPath(button_path);
-  }
-
-  bool ElementHasAttribute(std::initializer_list<std::string_view> element,
-                           const std::string& attribute) {
-    return test::OobeJS().GetBool(test::GetOobeElementPath(element) +
-                                  ".getAttribute('" + attribute + "')");
-  }
-
-  void WaitForElementAttribute(std::initializer_list<std::string_view> element,
-                               const std::string& attribute) {
-    test::OobeJS()
-        .CreateWaiter(test::GetOobeElementPath(element) + ".getAttribute('" +
-                      attribute + "')")
-        ->Wait();
-  }
-
-  void ExpectCollectedOptIns(
-      const std::set<ScopedAssistantSettings::OptIn>& opt_ins) {
-    EXPECT_EQ(opt_ins, assistant_settings_->collected_optins());
-  }
-
-  void WaitForScreenExit() {
-    if (screen_exited_) {
-      return;
-    }
-    base::test::TestFuture<void> waiter;
-    screen_exit_callback_ = waiter.GetCallback();
-    EXPECT_TRUE(waiter.Wait());
-  }
-
-  std::unique_ptr<ScopedAssistantSettings> assistant_settings_;
-
-  std::optional<AssistantOptInFlowScreen::Result> screen_result_;
-  base::HistogramTester histogram_tester_;
-
-  // If set, HandleRequest will return an error for the next value prop URL
-  // request..
-  bool fail_next_value_prop_url_request_ = false;
-
-  base::test::ScopedFeatureList scoped_feature_list_;
-
- private:
-  std::unique_ptr<HttpResponse> HandleRequest(const HttpRequest& request) {
-    auto response = std::make_unique<BasicHttpResponse>();
-    if (request.relative_url != "/test_assistant/en_us/value_prop.html" ||
-        fail_next_value_prop_url_request_) {
-      fail_next_value_prop_url_request_ = false;
-      response->set_code(net::HTTP_NOT_FOUND);
-    } else {
-      response->set_code(net::HTTP_OK);
-      response->set_content("Test content");
-      response->set_content_type("text/plain");
-    }
-    return std::move(response);
-  }
-
-  void HandleScreenExit(AssistantOptInFlowScreen::Result result) {
-    ASSERT_FALSE(screen_exited_);
-    screen_exited_ = true;
-    screen_result_ = result;
-    original_callback_.Run(result);
-    if (screen_exit_callback_)
-      std::move(screen_exit_callback_).Run();
-  }
-
-  bool screen_exited_ = false;
-  base::OnceClosure screen_exit_callback_;
-  AssistantOptInFlowScreen::ScreenExitCallback original_callback_;
-
-  LoginManagerMixin login_manager_{&mixin_host_};
-};
-
-class AssistantOptInFlowTest : public AssistantOptInFlowBaseTest {
- public:
-  AssistantOptInFlowTest() {
-    scoped_feature_list_.InitWithFeatures(
-        {}, {ash::features::kOobeSkipAssistant,
-             ash::assistant::features::kEnableNewEntryPoint});
-  }
-  ~AssistantOptInFlowTest() override = default;
-};
-
-IN_PROC_BROWSER_TEST_F(AssistantOptInFlowTest, Basic) {
-  auto force_lib_assistant_enabled =
-      AssistantOptInFlowScreen::ForceLibAssistantEnabledForTesting(true);
-  AssistantState::Get()->NotifyStatusChanged(assistant::AssistantStatus::READY);
-
-  SetUpAssistantScreensForTest();
-  ShowAssistantOptInFlowScreen();
-
-  OobeScreenWaiter screen_waiter(AssistantOptInFlowScreenView::kScreenId);
-  screen_waiter.Wait();
-
-  test::OobeJS().CreateVisibilityWaiter(true, kAssistantValueProp)->Wait();
-  EXPECT_TRUE(test::OobeJS().GetAttributeBool("inverse", kValuePropNextButton));
-  TapWhenEnabled(kValuePropNextButton);
-
-  test::OobeJS().CreateVisibilityWaiter(true, kAssistantRelatedInfo)->Wait();
-  EXPECT_TRUE(
-      test::OobeJS().GetAttributeBool("inverse", kRelatedInfoNextButton));
-  TapWhenEnabled(kRelatedInfoNextButton);
-
-  test::OobeJS().CreateVisibilityWaiter(true, kAssistantVoiceMatch)->Wait();
-  EXPECT_TRUE(
-      test::OobeJS().GetAttributeBool("inverse", kVoiceMatchAgreeButton));
-  TapWhenEnabled(kVoiceMatchAgreeButton);
-
-  WaitForScreenExit();
-
-  ExpectCollectedOptIns({ScopedAssistantSettings::OptIn::ACTIVITY_CONTROL});
-  PrefService* const prefs = ProfileManager::GetActiveUserProfile()->GetPrefs();
-  EXPECT_EQ(assistant::prefs::ConsentStatus::kActivityControlAccepted,
-            prefs->GetInteger(assistant::prefs::kAssistantConsentStatus));
-  EXPECT_TRUE(prefs->GetBoolean(assistant::prefs::kAssistantHotwordEnabled));
-  EXPECT_TRUE(prefs->GetBoolean(assistant::prefs::kAssistantContextEnabled));
-  EXPECT_EQ(screen_result_.value(), AssistantOptInFlowScreen::Result::NEXT);
-  histogram_tester_.ExpectTotalCount(kAssistantOptInScreenExitReason, 1);
-  histogram_tester_.ExpectTotalCount(kAssistantOptInScreenStepCompletionTime,
-                                     1);
-}
-
-IN_PROC_BROWSER_TEST_F(AssistantOptInFlowTest, DisableScreenContext) {
-  auto force_lib_assistant_enabled =
-      AssistantOptInFlowScreen::ForceLibAssistantEnabledForTesting(true);
-  AssistantState::Get()->NotifyStatusChanged(assistant::AssistantStatus::READY);
-
-  SetUpAssistantScreensForTest();
-  ShowAssistantOptInFlowScreen();
-
-  OobeScreenWaiter screen_waiter(AssistantOptInFlowScreenView::kScreenId);
-  screen_waiter.Wait();
-
-  test::OobeJS().CreateVisibilityWaiter(true, kAssistantValueProp)->Wait();
-  TapWhenEnabled(kValuePropNextButton);
-
-  test::OobeJS().CreateVisibilityWaiter(true, kAssistantRelatedInfo)->Wait();
-  TapWhenEnabled(kRelatedInfoSkipButton);
-
-  test::OobeJS().CreateVisibilityWaiter(true, kAssistantVoiceMatch)->Wait();
-  TapWhenEnabled(kVoiceMatchAgreeButton);
-
-  WaitForScreenExit();
-
-  ExpectCollectedOptIns({ScopedAssistantSettings::OptIn::ACTIVITY_CONTROL});
-  PrefService* const prefs = ProfileManager::GetActiveUserProfile()->GetPrefs();
-  EXPECT_EQ(assistant::prefs::ConsentStatus::kActivityControlAccepted,
-            prefs->GetInteger(assistant::prefs::kAssistantConsentStatus));
-  EXPECT_TRUE(prefs->GetBoolean(assistant::prefs::kAssistantHotwordEnabled));
-  EXPECT_FALSE(prefs->GetBoolean(assistant::prefs::kAssistantContextEnabled));
-  EXPECT_EQ(screen_result_.value(), AssistantOptInFlowScreen::Result::NEXT);
-  histogram_tester_.ExpectTotalCount(kAssistantOptInScreenExitReason, 1);
-  histogram_tester_.ExpectTotalCount(kAssistantOptInScreenStepCompletionTime,
-                                     1);
-}
-
-IN_PROC_BROWSER_TEST_F(AssistantOptInFlowTest, AssistantStateUpdateAfterShow) {
-  auto force_lib_assistant_enabled =
-      AssistantOptInFlowScreen::ForceLibAssistantEnabledForTesting(true);
-  SetUpAssistantScreensForTest();
-  ShowAssistantOptInFlowScreen();
-
-  OobeScreenWaiter screen_waiter(AssistantOptInFlowScreenView::kScreenId);
-  screen_waiter.Wait();
-
-  AssistantState::Get()->NotifyStatusChanged(assistant::AssistantStatus::READY);
-
-  test::OobeJS().CreateVisibilityWaiter(true, kAssistantValueProp)->Wait();
-  TapWhenEnabled(kValuePropNextButton);
-
-  test::OobeJS().CreateVisibilityWaiter(true, kAssistantRelatedInfo)->Wait();
-  TapWhenEnabled(kRelatedInfoNextButton);
-
-  test::OobeJS().CreateVisibilityWaiter(true, kAssistantVoiceMatch)->Wait();
-  TapWhenEnabled(kVoiceMatchAgreeButton);
-
-  WaitForScreenExit();
-
-  ExpectCollectedOptIns({ScopedAssistantSettings::OptIn::ACTIVITY_CONTROL});
-  PrefService* const prefs = ProfileManager::GetActiveUserProfile()->GetPrefs();
-  EXPECT_EQ(assistant::prefs::ConsentStatus::kActivityControlAccepted,
-            prefs->GetInteger(assistant::prefs::kAssistantConsentStatus));
-  EXPECT_TRUE(prefs->GetBoolean(assistant::prefs::kAssistantHotwordEnabled));
-  EXPECT_TRUE(prefs->GetBoolean(assistant::prefs::kAssistantContextEnabled));
-  EXPECT_EQ(screen_result_.value(), AssistantOptInFlowScreen::Result::NEXT);
-  histogram_tester_.ExpectTotalCount(kAssistantOptInScreenExitReason, 1);
-  histogram_tester_.ExpectTotalCount(kAssistantOptInScreenStepCompletionTime,
-                                     1);
-}
-
-// TODO(crbug.com/41486294): Flaky on ChromeOS.
-IN_PROC_BROWSER_TEST_F(AssistantOptInFlowTest,
-                       DISABLED_RetryOnWebviewLoadFail) {
-  auto force_lib_assistant_enabled =
-      AssistantOptInFlowScreen::ForceLibAssistantEnabledForTesting(true);
-  SetUpAssistantScreensForTest();
-  fail_next_value_prop_url_request_ = true;
-
-  ShowAssistantOptInFlowScreen();
-
-  AssistantState::Get()->NotifyStatusChanged(assistant::AssistantStatus::READY);
-
-  OobeScreenWaiter screen_waiter(AssistantOptInFlowScreenView::kScreenId);
-  screen_waiter.Wait();
-
-  // Value prop webview requests are set to fail - loading screen should display
-  // an error and an option to retry the request.
-  test::OobeJS().CreateVisibilityWaiter(true, kAssistantLoading)->Wait();
-  TapWhenEnabled(kLoadingRetryButton);
-
-  test::OobeJS().CreateVisibilityWaiter(true, kAssistantValueProp)->Wait();
-  TapWhenEnabled(kValuePropNextButton);
-
-  test::OobeJS().CreateVisibilityWaiter(true, kAssistantRelatedInfo)->Wait();
-  TapWhenEnabled(kRelatedInfoNextButton);
-
-  test::OobeJS().CreateVisibilityWaiter(true, kAssistantVoiceMatch)->Wait();
-  TapWhenEnabled(kVoiceMatchAgreeButton);
-
-  WaitForScreenExit();
-
-  ExpectCollectedOptIns({ScopedAssistantSettings::OptIn::ACTIVITY_CONTROL});
-  PrefService* const prefs = ProfileManager::GetActiveUserProfile()->GetPrefs();
-  EXPECT_EQ(assistant::prefs::ConsentStatus::kActivityControlAccepted,
-            prefs->GetInteger(assistant::prefs::kAssistantConsentStatus));
-  EXPECT_TRUE(prefs->GetBoolean(assistant::prefs::kAssistantHotwordEnabled));
-  EXPECT_TRUE(prefs->GetBoolean(assistant::prefs::kAssistantContextEnabled));
-  EXPECT_EQ(screen_result_.value(), AssistantOptInFlowScreen::Result::NEXT);
-  histogram_tester_.ExpectTotalCount(kAssistantOptInScreenExitReason, 1);
-  histogram_tester_.ExpectTotalCount(kAssistantOptInScreenStepCompletionTime,
-                                     1);
-}
-
-IN_PROC_BROWSER_TEST_F(AssistantOptInFlowTest, RejectValueProp) {
-  auto force_lib_assistant_enabled =
-      AssistantOptInFlowScreen::ForceLibAssistantEnabledForTesting(true);
-  SetUpAssistantScreensForTest();
-  AssistantState::Get()->NotifyStatusChanged(assistant::AssistantStatus::READY);
-
-  ShowAssistantOptInFlowScreen();
-
-  OobeScreenWaiter screen_waiter(AssistantOptInFlowScreenView::kScreenId);
-  screen_waiter.Wait();
-
-  test::OobeJS().CreateVisibilityWaiter(true, kAssistantValueProp)->Wait();
-  TapWhenEnabled(kValuePropSkipButton);
-
-  WaitForScreenExit();
-
-  ExpectCollectedOptIns({});
-  PrefService* const prefs = ProfileManager::GetActiveUserProfile()->GetPrefs();
-  EXPECT_EQ(assistant::prefs::ConsentStatus::kUnknown,
-            prefs->GetInteger(assistant::prefs::kAssistantConsentStatus));
-  EXPECT_FALSE(prefs->GetBoolean(assistant::prefs::kAssistantHotwordEnabled));
-  EXPECT_FALSE(prefs->GetBoolean(assistant::prefs::kAssistantContextEnabled));
-  EXPECT_EQ(screen_result_.value(), AssistantOptInFlowScreen::Result::NEXT);
-  histogram_tester_.ExpectTotalCount(kAssistantOptInScreenExitReason, 1);
-  histogram_tester_.ExpectTotalCount(kAssistantOptInScreenStepCompletionTime,
-                                     1);
-}
-
-// TODO(crbug.com/40917081): Flaky on ChromeOS.
-#if BUILDFLAG(IS_CHROMEOS)
-#define MAYBE_SkipShowingValueProp DISABLED_SkipShowingValueProp
-#else
-#define MAYBE_SkipShowingValueProp SkipShowingValueProp
-#endif
-IN_PROC_BROWSER_TEST_F(AssistantOptInFlowTest, MAYBE_SkipShowingValueProp) {
-  auto force_lib_assistant_enabled =
-      AssistantOptInFlowScreen::ForceLibAssistantEnabledForTesting(true);
-  assistant_settings_->set_consent_ui_flags(
-      ScopedAssistantSettings::CONSENT_UI_FLAG_SKIP_ACTIVITY_CONTROL);
-
-  SetUpAssistantScreensForTest();
-  AssistantState::Get()->NotifyStatusChanged(assistant::AssistantStatus::READY);
-
-  ShowAssistantOptInFlowScreen();
-
-  OobeScreenWaiter screen_waiter(AssistantOptInFlowScreenView::kScreenId);
-  screen_waiter.Wait();
-
-  test::OobeJS().CreateVisibilityWaiter(true, kAssistantRelatedInfo)->Wait();
-  TapWhenEnabled(kRelatedInfoNextButton);
-
-  test::OobeJS().CreateVisibilityWaiter(true, kAssistantVoiceMatch)->Wait();
-  TapWhenEnabled(kVoiceMatchAgreeButton);
-
-  WaitForScreenExit();
-
-  ExpectCollectedOptIns({});
-  PrefService* const prefs = ProfileManager::GetActiveUserProfile()->GetPrefs();
-  EXPECT_EQ(assistant::prefs::ConsentStatus::kActivityControlAccepted,
-            prefs->GetInteger(assistant::prefs::kAssistantConsentStatus));
-  EXPECT_TRUE(prefs->GetBoolean(assistant::prefs::kAssistantHotwordEnabled));
-  EXPECT_TRUE(prefs->GetBoolean(assistant::prefs::kAssistantContextEnabled));
-  EXPECT_EQ(screen_result_.value(), AssistantOptInFlowScreen::Result::NEXT);
-  histogram_tester_.ExpectTotalCount(kAssistantOptInScreenExitReason, 1);
-  histogram_tester_.ExpectTotalCount(kAssistantOptInScreenStepCompletionTime,
-                                     1);
-}
-
-IN_PROC_BROWSER_TEST_F(AssistantOptInFlowTest, SpeakerIdEnrollment) {
-  auto force_lib_assistant_enabled =
-      AssistantOptInFlowScreen::ForceLibAssistantEnabledForTesting(true);
-  assistant_settings_->set_consent_ui_flags(
-      ScopedAssistantSettings::CONSENT_UI_FLAG_SKIP_ACTIVITY_CONTROL);
-  assistant_settings_->set_speaker_id_enrollment_mode(
-      ScopedAssistantSettings::SpeakerIdEnrollmentMode::STEP_BY_STEP);
-
-  SetUpAssistantScreensForTest();
-  AssistantState::Get()->NotifyStatusChanged(assistant::AssistantStatus::READY);
-
-  ShowAssistantOptInFlowScreen();
-
-  OobeScreenWaiter screen_waiter(AssistantOptInFlowScreenView::kScreenId);
-  screen_waiter.Wait();
-
-  test::OobeJS().CreateVisibilityWaiter(true, kAssistantRelatedInfo)->Wait();
-  TapWhenEnabled(kRelatedInfoNextButton);
-
-  test::OobeJS().CreateVisibilityWaiter(true, kAssistantVoiceMatch)->Wait();
-  TapWhenEnabled(kVoiceMatchAgreeButton);
-
-  ASSERT_TRUE(assistant_settings_->AdvanceSpeakerIdEnrollmentState());
-  WaitForElementAttribute(kVoiceMatchEntry0, "active");
-  test::OobeJS().ExpectVisiblePath(kVoiceMatchLaterButton);
-
-  ASSERT_TRUE(assistant_settings_->AdvanceSpeakerIdEnrollmentState());
-  WaitForElementAttribute(kVoiceMatchEntry0, "completed");
-  test::OobeJS().ExpectVisiblePath(kVoiceMatchLaterButton);
-
-  ASSERT_TRUE(assistant_settings_->AdvanceSpeakerIdEnrollmentState());
-  WaitForElementAttribute(kVoiceMatchEntry1, "active");
-  test::OobeJS().ExpectVisiblePath(kVoiceMatchLaterButton);
-
-  ASSERT_TRUE(assistant_settings_->AdvanceSpeakerIdEnrollmentState());
-  WaitForElementAttribute(kVoiceMatchEntry1, "completed");
-
-  ASSERT_TRUE(assistant_settings_->AdvanceSpeakerIdEnrollmentState());
-  WaitForElementAttribute(kVoiceMatchEntry2, "active");
-
-  ASSERT_TRUE(assistant_settings_->AdvanceSpeakerIdEnrollmentState());
-  WaitForElementAttribute(kVoiceMatchEntry2, "completed");
-
-  ASSERT_TRUE(assistant_settings_->AdvanceSpeakerIdEnrollmentState());
-  WaitForElementAttribute(kVoiceMatchEntry3, "active");
-
-  ASSERT_TRUE(assistant_settings_->AdvanceSpeakerIdEnrollmentState());
-  WaitForElementAttribute(kVoiceMatchEntry3, "completed");
-  test::OobeJS().ExpectHiddenPath(kVoiceMatchLaterButton);
-
-  // This should finish the enrollment.
-  ASSERT_TRUE(assistant_settings_->AdvanceSpeakerIdEnrollmentState());
-  EXPECT_FALSE(assistant_settings_->IsSpeakerIdEnrollmentActive());
-
-  WaitForScreenExit();
-
-  ExpectCollectedOptIns({});
-  PrefService* const prefs = ProfileManager::GetActiveUserProfile()->GetPrefs();
-  EXPECT_EQ(assistant::prefs::ConsentStatus::kActivityControlAccepted,
-            prefs->GetInteger(assistant::prefs::kAssistantConsentStatus));
-  EXPECT_TRUE(prefs->GetBoolean(assistant::prefs::kAssistantHotwordEnabled));
-  EXPECT_TRUE(prefs->GetBoolean(assistant::prefs::kAssistantContextEnabled));
-  EXPECT_EQ(screen_result_.value(), AssistantOptInFlowScreen::Result::NEXT);
-  histogram_tester_.ExpectTotalCount(kAssistantOptInScreenExitReason, 1);
-  histogram_tester_.ExpectTotalCount(kAssistantOptInScreenStepCompletionTime,
-                                     1);
-}
-
-IN_PROC_BROWSER_TEST_F(AssistantOptInFlowTest,
-                       FeatureDisabledDuringSpeakerIdEnrollment) {
-  auto force_lib_assistant_enabled =
-      AssistantOptInFlowScreen::ForceLibAssistantEnabledForTesting(true);
-  assistant_settings_->set_consent_ui_flags(
-      ScopedAssistantSettings::CONSENT_UI_FLAG_SKIP_ACTIVITY_CONTROL);
-  assistant_settings_->set_speaker_id_enrollment_mode(
-      ScopedAssistantSettings::SpeakerIdEnrollmentMode::STEP_BY_STEP);
-
-  SetUpAssistantScreensForTest();
-  AssistantState::Get()->NotifyStatusChanged(assistant::AssistantStatus::READY);
-
-  ShowAssistantOptInFlowScreen();
-
-  OobeScreenWaiter screen_waiter(AssistantOptInFlowScreenView::kScreenId);
-  screen_waiter.Wait();
-
-  test::OobeJS().CreateVisibilityWaiter(true, kAssistantRelatedInfo)->Wait();
-  TapWhenEnabled(kRelatedInfoNextButton);
-
-  PrefService* const prefs = ProfileManager::GetActiveUserProfile()->GetPrefs();
-  prefs->SetBoolean(assistant::prefs::kAssistantEnabled, false);
-
-  test::OobeJS().CreateVisibilityWaiter(true, kAssistantVoiceMatch)->Wait();
-  TapWhenEnabled(kVoiceMatchAgreeButton);
-
-  EXPECT_FALSE(assistant_settings_->IsSpeakerIdEnrollmentActive());
-
-  WaitForScreenExit();
-
-  ExpectCollectedOptIns({});
-  EXPECT_EQ(assistant::prefs::ConsentStatus::kActivityControlAccepted,
-            prefs->GetInteger(assistant::prefs::kAssistantConsentStatus));
-  EXPECT_FALSE(prefs->GetBoolean(assistant::prefs::kAssistantHotwordEnabled));
-  EXPECT_TRUE(prefs->GetBoolean(assistant::prefs::kAssistantContextEnabled));
-  EXPECT_EQ(screen_result_.value(), AssistantOptInFlowScreen::Result::NEXT);
-  histogram_tester_.ExpectTotalCount(kAssistantOptInScreenExitReason, 1);
-  histogram_tester_.ExpectTotalCount(kAssistantOptInScreenStepCompletionTime,
-                                     1);
-}
-
-IN_PROC_BROWSER_TEST_F(AssistantOptInFlowTest,
-                       BailOutDuringSpeakerIdEnrollment) {
-  auto force_lib_assistant_enabled =
-      AssistantOptInFlowScreen::ForceLibAssistantEnabledForTesting(true);
-  assistant_settings_->set_consent_ui_flags(
-      ScopedAssistantSettings::CONSENT_UI_FLAG_SKIP_ACTIVITY_CONTROL);
-  assistant_settings_->set_speaker_id_enrollment_mode(
-      ScopedAssistantSettings::SpeakerIdEnrollmentMode::STEP_BY_STEP);
-
-  SetUpAssistantScreensForTest();
-  AssistantState::Get()->NotifyStatusChanged(assistant::AssistantStatus::READY);
-
-  ShowAssistantOptInFlowScreen();
-
-  OobeScreenWaiter screen_waiter(AssistantOptInFlowScreenView::kScreenId);
-  screen_waiter.Wait();
-
-  test::OobeJS().CreateVisibilityWaiter(true, kAssistantRelatedInfo)->Wait();
-  TapWhenEnabled(kRelatedInfoNextButton);
-
-  test::OobeJS().CreateVisibilityWaiter(true, kAssistantVoiceMatch)->Wait();
-  TapWhenEnabled(kVoiceMatchAgreeButton);
-
-  ASSERT_TRUE(assistant_settings_->AdvanceSpeakerIdEnrollmentState());
-  WaitForElementAttribute(kVoiceMatchEntry0, "active");
-  test::OobeJS().ExpectVisiblePath(kVoiceMatchLaterButton);
-
-  ASSERT_TRUE(assistant_settings_->AdvanceSpeakerIdEnrollmentState());
-  WaitForElementAttribute(kVoiceMatchEntry0, "completed");
-
-  test::OobeJS().TapOnPath(kVoiceMatchLaterButton);
-  EXPECT_FALSE(assistant_settings_->IsSpeakerIdEnrollmentActive());
-
-  WaitForScreenExit();
-
-  ExpectCollectedOptIns({});
-  PrefService* const prefs = ProfileManager::GetActiveUserProfile()->GetPrefs();
-  EXPECT_EQ(assistant::prefs::ConsentStatus::kActivityControlAccepted,
-            prefs->GetInteger(assistant::prefs::kAssistantConsentStatus));
-  EXPECT_FALSE(prefs->GetBoolean(assistant::prefs::kAssistantHotwordEnabled));
-  EXPECT_TRUE(prefs->GetBoolean(assistant::prefs::kAssistantContextEnabled));
-  EXPECT_EQ(screen_result_.value(), AssistantOptInFlowScreen::Result::NEXT);
-  histogram_tester_.ExpectTotalCount(kAssistantOptInScreenExitReason, 1);
-  histogram_tester_.ExpectTotalCount(kAssistantOptInScreenStepCompletionTime,
-                                     1);
-}
-
-IN_PROC_BROWSER_TEST_F(AssistantOptInFlowTest,
-                       SpeakerIdEnrollmentFailureAndRetry) {
-  auto force_lib_assistant_enabled =
-      AssistantOptInFlowScreen::ForceLibAssistantEnabledForTesting(true);
-  assistant_settings_->set_consent_ui_flags(
-      ScopedAssistantSettings::CONSENT_UI_FLAG_SKIP_ACTIVITY_CONTROL);
-  assistant_settings_->set_speaker_id_enrollment_mode(
-      ScopedAssistantSettings::SpeakerIdEnrollmentMode::STEP_BY_STEP);
-
-  SetUpAssistantScreensForTest();
-  AssistantState::Get()->NotifyStatusChanged(assistant::AssistantStatus::READY);
-
-  ShowAssistantOptInFlowScreen();
-
-  OobeScreenWaiter screen_waiter(AssistantOptInFlowScreenView::kScreenId);
-  screen_waiter.Wait();
-
-  test::OobeJS().CreateVisibilityWaiter(true, kAssistantRelatedInfo)->Wait();
-  TapWhenEnabled(kRelatedInfoNextButton);
-
-  test::OobeJS().CreateVisibilityWaiter(true, kAssistantVoiceMatch)->Wait();
-  TapWhenEnabled(kVoiceMatchAgreeButton);
-
-  ASSERT_TRUE(assistant_settings_->AdvanceSpeakerIdEnrollmentState());
-  WaitForElementAttribute(kVoiceMatchEntry0, "active");
-  test::OobeJS().ExpectVisiblePath(kVoiceMatchLaterButton);
-
-  assistant_settings_->FailSpeakerIdEnrollment();
-
-  // Failure should cause an error screen to be shown, with retry button
-  // available.
-  test::OobeJS().CreateVisibilityWaiter(true, kAssistantLoading)->Wait();
-
-  // Make enrollment succeed immediately next time.
-  assistant_settings_->set_speaker_id_enrollment_mode(
-      ScopedAssistantSettings::SpeakerIdEnrollmentMode::IMMEDIATE);
-
-  TapWhenEnabled(kLoadingRetryButton);
-
-  test::OobeJS().CreateVisibilityWaiter(true, kAssistantVoiceMatch)->Wait();
-  TapWhenEnabled(kVoiceMatchAgreeButton);
-
-  WaitForScreenExit();
-
-  ExpectCollectedOptIns({});
-  PrefService* const prefs = ProfileManager::GetActiveUserProfile()->GetPrefs();
-  EXPECT_EQ(assistant::prefs::ConsentStatus::kActivityControlAccepted,
-            prefs->GetInteger(assistant::prefs::kAssistantConsentStatus));
-  EXPECT_TRUE(prefs->GetBoolean(assistant::prefs::kAssistantHotwordEnabled));
-  EXPECT_TRUE(prefs->GetBoolean(assistant::prefs::kAssistantContextEnabled));
-  EXPECT_EQ(screen_result_.value(), AssistantOptInFlowScreen::Result::NEXT);
-  histogram_tester_.ExpectTotalCount(kAssistantOptInScreenExitReason, 1);
-  histogram_tester_.ExpectTotalCount(kAssistantOptInScreenStepCompletionTime,
-                                     1);
-}
-
-IN_PROC_BROWSER_TEST_F(AssistantOptInFlowTest, WAADisabledByPolicy) {
-  auto force_lib_assistant_enabled =
-      AssistantOptInFlowScreen::ForceLibAssistantEnabledForTesting(true);
-  assistant_settings_->set_consent_ui_flags(
-      ScopedAssistantSettings::CONSENT_UI_FLAG_WAA_DISABLED_BY_POLICY);
-
-  AssistantState::Get()->NotifyStatusChanged(assistant::AssistantStatus::READY);
-  SetUpAssistantScreensForTest();
-  ShowAssistantOptInFlowScreen();
-
-  WaitForScreenExit();
-
-  ExpectCollectedOptIns({});
-  PrefService* const prefs = ProfileManager::GetActiveUserProfile()->GetPrefs();
-  EXPECT_TRUE(prefs->GetBoolean(assistant::prefs::kAssistantEnabled));
-  EXPECT_FALSE(prefs->GetBoolean(assistant::prefs::kAssistantHotwordEnabled));
-  EXPECT_FALSE(prefs->GetBoolean(assistant::prefs::kAssistantContextEnabled));
-  EXPECT_EQ(screen_result_.value(), AssistantOptInFlowScreen::Result::NEXT);
-  histogram_tester_.ExpectTotalCount(kAssistantOptInScreenExitReason, 1);
-  histogram_tester_.ExpectTotalCount(kAssistantOptInScreenStepCompletionTime,
-                                     1);
-}
-
-IN_PROC_BROWSER_TEST_F(AssistantOptInFlowTest, AssistantDisabledByPolicy) {
-  auto force_lib_assistant_enabled =
-      AssistantOptInFlowScreen::ForceLibAssistantEnabledForTesting(true);
-  assistant_settings_->set_consent_ui_flags(
-      ScopedAssistantSettings::CONSENT_UI_FLAG_ASSISTANT_DISABLED_BY_POLICY);
-
-  AssistantState::Get()->NotifyStatusChanged(assistant::AssistantStatus::READY);
-  SetUpAssistantScreensForTest();
-  ShowAssistantOptInFlowScreen();
-
-  WaitForScreenExit();
-
-  ExpectCollectedOptIns({});
-  PrefService* const prefs = ProfileManager::GetActiveUserProfile()->GetPrefs();
-  EXPECT_TRUE(prefs->GetBoolean(assistant::prefs::kAssistantDisabledByPolicy));
-  EXPECT_FALSE(prefs->GetBoolean(assistant::prefs::kAssistantEnabled));
-  EXPECT_FALSE(prefs->GetBoolean(assistant::prefs::kAssistantHotwordEnabled));
-  EXPECT_FALSE(prefs->GetBoolean(assistant::prefs::kAssistantContextEnabled));
-  EXPECT_EQ(screen_result_.value(), AssistantOptInFlowScreen::Result::NEXT);
-  histogram_tester_.ExpectTotalCount(kAssistantOptInScreenExitReason, 1);
-  histogram_tester_.ExpectTotalCount(kAssistantOptInScreenStepCompletionTime,
-                                     1);
-}
-
-IN_PROC_BROWSER_TEST_F(AssistantOptInFlowTest, AssistantSkippedNoLib) {
-  auto force_lib_assistant_disabled =
-      AssistantOptInFlowScreen::ForceLibAssistantEnabledForTesting(false);
-  AssistantState::Get()->NotifyStatusChanged(assistant::AssistantStatus::READY);
-  SetUpAssistantScreensForTest();
-  ShowAssistantOptInFlowScreen();
-
-  WaitForScreenExit();
-
-  ExpectCollectedOptIns({});
-  EXPECT_EQ(screen_result_.value(),
-            AssistantOptInFlowScreen::Result::NOT_APPLICABLE);
-  histogram_tester_.ExpectTotalCount(kAssistantOptInScreenExitReason, 0);
-  histogram_tester_.ExpectTotalCount(kAssistantOptInScreenStepCompletionTime,
-                                     0);
-}
-
-class AssistantOptInFlowMinorModeTest : public AssistantOptInFlowTest {
- public:
-  AssistantOptInFlowMinorModeTest() = default;
-
-  void SetUpOnMainThread() override {
-    AssistantOptInFlowTest::SetUpOnMainThread();
-    assistant_settings_->set_is_minor_user(true);
-  }
-};
-
-IN_PROC_BROWSER_TEST_F(AssistantOptInFlowMinorModeTest,
-                       AcceptMultipleValuePropConsentsForMinors) {
-  auto force_lib_assistant_enabled =
-      AssistantOptInFlowScreen::ForceLibAssistantEnabledForTesting(true);
-  assistant_settings_->set_setting_zippy_size(2);
-  AssistantState::Get()->NotifyStatusChanged(assistant::AssistantStatus::READY);
-
-  SetUpAssistantScreensForTest();
-  ShowAssistantOptInFlowScreen();
-
-  OobeScreenWaiter screen_waiter(AssistantOptInFlowScreenView::kScreenId);
-  screen_waiter.Wait();
-
-  test::OobeJS().CreateVisibilityWaiter(true, kAssistantValueProp)->Wait();
-  EXPECT_FALSE(
-      test::OobeJS().GetAttributeBool("inverse", kValuePropNextButton));
-  test::OobeJS().ExpectElementText(kSettingsZippyTitle,
-                                   kSettingsZippyTitleFirst);
-  test::OobeJS().ExpectElementText(
-      base::StrCat(
-          {kSettingsZippyDescription, kEnsp, kSettingsZippyLearnMoreLink}),
-      kSettingsZippyDescriptionFirst);
-  test::OobeJS().ExpectElementText(kSettingsZippyAdditionalInfo,
-                                   kSettingsZippyAdditionalInfoFirst);
-  TapWhenEnabled(kValuePropNextButton);
-  EXPECT_FALSE(
-      test::OobeJS().GetAttributeBool("inverse", kValuePropNextButton));
-  TapWhenEnabled(kValuePropNextButton);
-
-  test::OobeJS().CreateVisibilityWaiter(true, kAssistantRelatedInfo)->Wait();
-  EXPECT_FALSE(
-      test::OobeJS().GetAttributeBool("inverse", kRelatedInfoNextButton));
-  TapWhenEnabled(kRelatedInfoNextButton);
-
-  test::OobeJS().CreateVisibilityWaiter(true, kAssistantVoiceMatch)->Wait();
-  EXPECT_FALSE(
-      test::OobeJS().GetAttributeBool("inverse", kVoiceMatchAgreeButton));
-  TapWhenEnabled(kVoiceMatchAgreeButton);
-
-  WaitForScreenExit();
-
-  ExpectCollectedOptIns({ScopedAssistantSettings::OptIn::ACTIVITY_CONTROL});
-  PrefService* const prefs = ProfileManager::GetActiveUserProfile()->GetPrefs();
-  EXPECT_EQ(assistant::prefs::ConsentStatus::kActivityControlAccepted,
-            prefs->GetInteger(assistant::prefs::kAssistantConsentStatus));
-  EXPECT_TRUE(prefs->GetBoolean(assistant::prefs::kAssistantHotwordEnabled));
-  EXPECT_TRUE(prefs->GetBoolean(assistant::prefs::kAssistantContextEnabled));
-  EXPECT_EQ(screen_result_.value(), AssistantOptInFlowScreen::Result::NEXT);
-  histogram_tester_.ExpectTotalCount(kAssistantOptInScreenExitReason, 1);
-  histogram_tester_.ExpectTotalCount(kAssistantOptInScreenStepCompletionTime,
-                                     1);
-}
-
-IN_PROC_BROWSER_TEST_F(AssistantOptInFlowMinorModeTest,
-                       DeclineMultipleValuePropConsentsForMinors) {
-  auto force_lib_assistant_enabled =
-      AssistantOptInFlowScreen::ForceLibAssistantEnabledForTesting(true);
-  assistant_settings_->set_setting_zippy_size(2);
-  AssistantState::Get()->NotifyStatusChanged(assistant::AssistantStatus::READY);
-
-  SetUpAssistantScreensForTest();
-  ShowAssistantOptInFlowScreen();
-
-  OobeScreenWaiter screen_waiter(AssistantOptInFlowScreenView::kScreenId);
-  screen_waiter.Wait();
-
-  test::OobeJS().CreateVisibilityWaiter(true, kAssistantValueProp)->Wait();
-  TapWhenEnabled(kValuePropSkipButton);
-  TapWhenEnabled(kValuePropSkipButton);
-
-  WaitForScreenExit();
-
-  ExpectCollectedOptIns({});
-  PrefService* const prefs = ProfileManager::GetActiveUserProfile()->GetPrefs();
-  EXPECT_EQ(assistant::prefs::ConsentStatus::kUnknown,
-            prefs->GetInteger(assistant::prefs::kAssistantConsentStatus));
-  EXPECT_FALSE(prefs->GetBoolean(assistant::prefs::kAssistantHotwordEnabled));
-  EXPECT_FALSE(prefs->GetBoolean(assistant::prefs::kAssistantContextEnabled));
-  EXPECT_EQ(screen_result_.value(), AssistantOptInFlowScreen::Result::NEXT);
-  histogram_tester_.ExpectTotalCount(kAssistantOptInScreenExitReason, 1);
-  histogram_tester_.ExpectTotalCount(kAssistantOptInScreenStepCompletionTime,
-                                     1);
-}
-
-IN_PROC_BROWSER_TEST_F(AssistantOptInFlowMinorModeTest,
-                       AcceptFirstAndDeclineSecondValuePropConsentsForMinors) {
-  auto force_lib_assistant_enabled =
-      AssistantOptInFlowScreen::ForceLibAssistantEnabledForTesting(true);
-  assistant_settings_->set_setting_zippy_size(2);
-  AssistantState::Get()->NotifyStatusChanged(assistant::AssistantStatus::READY);
-
-  SetUpAssistantScreensForTest();
-  ShowAssistantOptInFlowScreen();
-
-  OobeScreenWaiter screen_waiter(AssistantOptInFlowScreenView::kScreenId);
-  screen_waiter.Wait();
-
-  test::OobeJS().CreateVisibilityWaiter(true, kAssistantValueProp)->Wait();
-  TapWhenEnabled(kValuePropNextButton);
-  TapWhenEnabled(kValuePropSkipButton);
-
-  test::OobeJS().CreateVisibilityWaiter(true, kAssistantRelatedInfo)->Wait();
-  TapWhenEnabled(kRelatedInfoNextButton);
-
-  test::OobeJS().CreateVisibilityWaiter(true, kAssistantVoiceMatch)->Wait();
-  TapWhenEnabled(kVoiceMatchAgreeButton);
-
-  WaitForScreenExit();
-
-  ExpectCollectedOptIns({ScopedAssistantSettings::OptIn::ACTIVITY_CONTROL});
-  PrefService* const prefs = ProfileManager::GetActiveUserProfile()->GetPrefs();
-  EXPECT_EQ(assistant::prefs::ConsentStatus::kUnknown,
-            prefs->GetInteger(assistant::prefs::kAssistantConsentStatus));
-  EXPECT_TRUE(prefs->GetBoolean(assistant::prefs::kAssistantHotwordEnabled));
-  EXPECT_TRUE(prefs->GetBoolean(assistant::prefs::kAssistantContextEnabled));
-  EXPECT_EQ(screen_result_.value(), AssistantOptInFlowScreen::Result::NEXT);
-  histogram_tester_.ExpectTotalCount(kAssistantOptInScreenExitReason, 1);
-  histogram_tester_.ExpectTotalCount(kAssistantOptInScreenStepCompletionTime,
-                                     1);
-}
-
-IN_PROC_BROWSER_TEST_F(AssistantOptInFlowMinorModeTest,
-                       DeclineFirstAndAcceptSecondValuePropConsentsForMinors) {
-  auto force_lib_assistant_enabled =
-      AssistantOptInFlowScreen::ForceLibAssistantEnabledForTesting(true);
-  assistant_settings_->set_setting_zippy_size(2);
-  AssistantState::Get()->NotifyStatusChanged(assistant::AssistantStatus::READY);
-
-  SetUpAssistantScreensForTest();
-  ShowAssistantOptInFlowScreen();
-
-  OobeScreenWaiter screen_waiter(AssistantOptInFlowScreenView::kScreenId);
-  screen_waiter.Wait();
-
-  test::OobeJS().CreateVisibilityWaiter(true, kAssistantValueProp)->Wait();
-  TapWhenEnabled(kValuePropSkipButton);
-  TapWhenEnabled(kValuePropNextButton);
-
-  test::OobeJS().CreateVisibilityWaiter(true, kAssistantRelatedInfo)->Wait();
-  TapWhenEnabled(kRelatedInfoNextButton);
-
-  test::OobeJS().CreateVisibilityWaiter(true, kAssistantVoiceMatch)->Wait();
-  TapWhenEnabled(kVoiceMatchAgreeButton);
-
-  WaitForScreenExit();
-
-  ExpectCollectedOptIns({ScopedAssistantSettings::OptIn::ACTIVITY_CONTROL});
-  PrefService* const prefs = ProfileManager::GetActiveUserProfile()->GetPrefs();
-  EXPECT_EQ(assistant::prefs::ConsentStatus::kUnknown,
-            prefs->GetInteger(assistant::prefs::kAssistantConsentStatus));
-  EXPECT_TRUE(prefs->GetBoolean(assistant::prefs::kAssistantHotwordEnabled));
-  EXPECT_TRUE(prefs->GetBoolean(assistant::prefs::kAssistantContextEnabled));
-  EXPECT_EQ(screen_result_.value(), AssistantOptInFlowScreen::Result::NEXT);
-  histogram_tester_.ExpectTotalCount(kAssistantOptInScreenExitReason, 1);
-  histogram_tester_.ExpectTotalCount(kAssistantOptInScreenStepCompletionTime,
-                                     1);
-}
-
-class AssistantOptInFlowSkipFeatureTest : public AssistantOptInFlowBaseTest {
- public:
-  AssistantOptInFlowSkipFeatureTest() {
-    scoped_feature_list_.InitAndEnableFeature(
-        ash::features::kOobeSkipAssistant);
-  }
-};
-
-IN_PROC_BROWSER_TEST_F(AssistantOptInFlowSkipFeatureTest, AssistantSkipped) {
-  AssistantState::Get()->NotifyStatusChanged(assistant::AssistantStatus::READY);
-  ShowAssistantOptInFlowScreen();
-  WaitForScreenExit();
-  EXPECT_EQ(screen_result_.value(),
-            AssistantOptInFlowScreen::Result::NOT_APPLICABLE);
-
-  ExpectCollectedOptIns({});
-  histogram_tester_.ExpectTotalCount(kAssistantOptInScreenExitReason, 0);
-  histogram_tester_.ExpectTotalCount(kAssistantOptInScreenStepCompletionTime,
-                                     0);
-
-  PrefService* const prefs = ProfileManager::GetActiveUserProfile()->GetPrefs();
-  EXPECT_FALSE(prefs->GetBoolean(assistant::prefs::kAssistantHotwordEnabled));
-  EXPECT_FALSE(prefs->GetBoolean(assistant::prefs::kAssistantContextEnabled));
-}
-
-}  // namespace
-}  // namespace ash
diff --git a/chrome/browser/ash/login/screens/terms_of_service_screen_browsertest.cc b/chrome/browser/ash/login/screens/terms_of_service_screen_browsertest.cc
index 5c2f54f..194ce47 100644
--- a/chrome/browser/ash/login/screens/terms_of_service_screen_browsertest.cc
+++ b/chrome/browser/ash/login/screens/terms_of_service_screen_browsertest.cc
@@ -39,8 +39,8 @@
 #include "chrome/browser/ui/webui/ash/login/sync_consent_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/terms_of_service_screen_handler.h"
 #include "chromeos/ash/components/dbus/session_manager/fake_session_manager_client.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "components/policy/core/common/cloud/test/policy_builder.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "components/policy/proto/chrome_device_policy.pb.h"
 #include "components/prefs/pref_service.h"
 #include "components/user_manager/known_user.h"
diff --git a/chrome/browser/ash/login/users/BUILD.gn b/chrome/browser/ash/login/users/BUILD.gn
index 103c771..545076ec 100644
--- a/chrome/browser/ash/login/users/BUILD.gn
+++ b/chrome/browser/ash/login/users/BUILD.gn
@@ -60,6 +60,7 @@
     "//chromeos/ash/components/install_attributes",
     "//chromeos/ash/components/login/auth/public:authpublic",
     "//chromeos/ash/components/network",
+    "//chromeos/ash/components/policy/device_local_account",
     "//chromeos/ash/components/timezone",
     "//chromeos/ash/experiences/arc:arc_base_utils",
     "//chromeos/components/onc",
@@ -186,6 +187,7 @@
     "//chromeos/ash/components/cryptohome",
     "//chromeos/ash/components/dbus/concierge",
     "//chromeos/ash/components/dbus/userdataauth",
+    "//chromeos/ash/components/policy/device_local_account",
     "//chromeos/ash/components/settings",
     "//chromeos/ash/components/system",
     "//components/account_id",
diff --git a/chrome/browser/ash/login/users/chrome_user_manager_util.h b/chrome/browser/ash/login/users/chrome_user_manager_util.h
index 144e6dd..c87c8758 100644
--- a/chrome/browser/ash/login/users/chrome_user_manager_util.h
+++ b/chrome/browser/ash/login/users/chrome_user_manager_util.h
@@ -7,7 +7,7 @@
 
 #include <optional>
 
-#include "components/policy/core/common/device_local_account_type.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "components/policy/proto/chrome_device_policy.pb.h"
 #include "components/user_manager/user.h"
 #include "components/user_manager/user_type.h"
diff --git a/chrome/browser/ash/login/users/user_manager_delegate_impl.cc b/chrome/browser/ash/login/users/user_manager_delegate_impl.cc
index fc81208..c5dcb7c 100644
--- a/chrome/browser/ash/login/users/user_manager_delegate_impl.cc
+++ b/chrome/browser/ash/login/users/user_manager_delegate_impl.cc
@@ -22,7 +22,7 @@
 #include "chromeos/ash/components/cryptohome/cryptohome_parameters.h"
 #include "chromeos/ash/components/dbus/cryptohome/UserDataAuth.pb.h"
 #include "chromeos/ash/components/dbus/userdataauth/userdataauth_client.h"
-#include "components/policy/core/common/device_local_account_type.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "components/user_manager/user.h"
 #include "components/user_manager/user_type.h"
 #include "content/public/common/content_switches.h"
diff --git a/chrome/browser/ash/login/users/user_manager_unittest.cc b/chrome/browser/ash/login/users/user_manager_unittest.cc
index abace54..38ebd9a 100644
--- a/chrome/browser/ash/login/users/user_manager_unittest.cc
+++ b/chrome/browser/ash/login/users/user_manager_unittest.cc
@@ -40,10 +40,10 @@
 #include "chromeos/ash/components/dbus/concierge/concierge_client.h"
 #include "chromeos/ash/components/dbus/userdataauth/fake_userdataauth_client.h"
 #include "chromeos/ash/components/dbus/userdataauth/userdataauth_client.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
 #include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "components/account_id/account_id.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 #include "components/user_manager/known_user.h"
diff --git a/chrome/browser/ash/main_parts/chrome_browser_main_parts_ash.cc b/chrome/browser/ash/main_parts/chrome_browser_main_parts_ash.cc
index 4a4e49a..72fe47b 100644
--- a/chrome/browser/ash/main_parts/chrome_browser_main_parts_ash.cc
+++ b/chrome/browser/ash/main_parts/chrome_browser_main_parts_ash.cc
@@ -229,6 +229,7 @@
 #include "chromeos/ash/components/network/system_token_cert_db_storage.h"
 #include "chromeos/ash/components/network/traffic_counters_handler.h"
 #include "chromeos/ash/components/peripheral_notification/peripheral_notification_manager.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "chromeos/ash/components/power/dark_resume_controller.h"
 #include "chromeos/ash/components/report/device_metrics/use_case/real_psm_client_manager.h"
 #include "chromeos/ash/components/report/device_metrics/use_case/use_case.h"
@@ -256,7 +257,6 @@
 #include "components/metrics/metrics_service.h"
 #include "components/ownership/owner_key_util.h"
 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "components/prefs/pref_service.h"
 #include "components/quirks/quirks_manager.h"
 #include "components/safe_browsing/core/common/safe_browsing_prefs.h"
diff --git a/chrome/browser/ash/policy/core/BUILD.gn b/chrome/browser/ash/policy/core/BUILD.gn
index d9eac2b..1b1215609e 100644
--- a/chrome/browser/ash/policy/core/BUILD.gn
+++ b/chrome/browser/ash/policy/core/BUILD.gn
@@ -102,6 +102,7 @@
     "//chromeos/ash/components/dbus/userdataauth:userdataauth_proto",
     "//chromeos/ash/components/install_attributes",
     "//chromeos/ash/components/network",
+    "//chromeos/ash/components/policy/device_local_account",
     "//chromeos/ash/components/settings",
     "//chromeos/ash/components/system",
     "//chromeos/ash/experiences/arc:arc_features",
@@ -257,6 +258,7 @@
     "//chromeos/ash/components/dbus/userdataauth",
     "//chromeos/ash/components/install_attributes",
     "//chromeos/ash/components/install_attributes:test_support",
+    "//chromeos/ash/components/policy/device_local_account",
     "//chromeos/ash/components/policy/weekly_time",
     "//chromeos/ash/components/settings",
     "//chromeos/ash/components/system",
diff --git a/chrome/browser/ash/policy/core/device_local_account.h b/chrome/browser/ash/policy/core/device_local_account.h
index 72f94083..07622d97 100644
--- a/chrome/browser/ash/policy/core/device_local_account.h
+++ b/chrome/browser/ash/policy/core/device_local_account.h
@@ -8,7 +8,7 @@
 #include <string>
 #include <vector>
 
-#include "components/policy/core/common/device_local_account_type.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 
 namespace ash {
 class CrosSettings;
diff --git a/chrome/browser/ash/policy/core/device_local_account_browsertest.cc b/chrome/browser/ash/policy/core/device_local_account_browsertest.cc
index 08ca786..8076762 100644
--- a/chrome/browser/ash/policy/core/device_local_account_browsertest.cc
+++ b/chrome/browser/ash/policy/core/device_local_account_browsertest.cc
@@ -120,6 +120,7 @@
 #include "chromeos/ash/components/dbus/session_manager/fake_session_manager_client.h"
 #include "chromeos/ash/components/login/auth/public/user_context.h"
 #include "chromeos/ash/components/network/policy_certificate_provider.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "chromeos/ash/components/settings/timezone_settings.h"
 #include "chromeos/components/mgs/managed_guest_session_utils.h"
 #include "components/crx_file/crx_verifier.h"
@@ -129,7 +130,6 @@
 #include "components/policy/core/common/cloud/cloud_policy_core.h"
 #include "components/policy/core/common/cloud/cloud_policy_store.h"
 #include "components/policy/core/common/cloud/test/policy_builder.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "components/policy/core/common/external_data_fetcher.h"
 #include "components/policy/core/common/policy_map.h"
 #include "components/policy/core/common/policy_namespace.h"
diff --git a/chrome/browser/ash/policy/core/device_local_account_policy_provider.h b/chrome/browser/ash/policy/core/device_local_account_policy_provider.h
index 08d31ef..134c8d2 100644
--- a/chrome/browser/ash/policy/core/device_local_account_policy_provider.h
+++ b/chrome/browser/ash/policy/core/device_local_account_policy_provider.h
@@ -14,8 +14,8 @@
 #include "chrome/browser/ash/policy/core/device_local_account.h"
 #include "chrome/browser/ash/policy/core/device_local_account_policy_service.h"
 #include "chrome/browser/ash/policy/external_data/device_local_account_external_data_manager.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "components/policy/core/common/configuration_policy_provider.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "components/policy/core/common/policy_types.h"
 
 namespace policy {
diff --git a/chrome/browser/ash/policy/core/device_local_account_policy_service_unittest.cc b/chrome/browser/ash/policy/core/device_local_account_policy_service_unittest.cc
index 6337572..f85d626 100644
--- a/chrome/browser/ash/policy/core/device_local_account_policy_service_unittest.cc
+++ b/chrome/browser/ash/policy/core/device_local_account_policy_service_unittest.cc
@@ -34,6 +34,7 @@
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chromeos/ash/components/install_attributes/stub_install_attributes.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "chromeos/ash/components/settings/cros_settings.h"
 #include "chromeos/dbus/power/power_policy_controller.h"
 #include "components/invalidation/test_support/fake_invalidation_listener.h"
@@ -42,7 +43,6 @@
 #include "components/policy/core/common/cloud/cloud_policy_service.h"
 #include "components/policy/core/common/cloud/mock_device_management_service.h"
 #include "components/policy/core/common/cloud/test/policy_builder.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "components/policy/core/common/external_data_fetcher.h"
 #include "components/policy/core/common/mock_configuration_policy_provider.h"
 #include "components/policy/core/common/policy_bundle.h"
diff --git a/chrome/browser/ash/policy/core/device_local_account_unittest.cc b/chrome/browser/ash/policy/core/device_local_account_unittest.cc
index 1cd1e7f..1f2d4d4f 100644
--- a/chrome/browser/ash/policy/core/device_local_account_unittest.cc
+++ b/chrome/browser/ash/policy/core/device_local_account_unittest.cc
@@ -9,10 +9,10 @@
 #include "base/values.h"
 #include "chrome/browser/ash/settings/scoped_testing_cros_settings.h"
 #include "chrome/browser/ash/settings/stub_cros_settings_provider.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "chromeos/ash/components/settings/cros_settings.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
 #include "chromeos/ash/components/settings/cros_settings_provider.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/chrome/browser/ash/policy/core/device_policy_decoder.cc b/chrome/browser/ash/policy/core/device_policy_decoder.cc
index a86cf97..5796a55 100644
--- a/chrome/browser/ash/policy/core/device_policy_decoder.cc
+++ b/chrome/browser/ash/policy/core/device_policy_decoder.cc
@@ -11,6 +11,7 @@
 #include <string_view>
 #include <utility>
 
+#include "ash/constants/ash_features.h"
 #include "ash/system/privacy_hub/privacy_hub_controller.h"
 #include "base/containers/fixed_flat_map.h"
 #include "base/functional/callback.h"
@@ -26,13 +27,12 @@
 #include "chrome/browser/policy/chrome_browser_policy_connector.h"
 #include "chromeos/ash/components/dbus/dbus_thread_manager.h"
 #include "chromeos/ash/components/dbus/update_engine/update_engine_client.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
 #include "components/policy/core/browser/policy_error_map.h"
 #include "components/policy/core/common/chrome_schema.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "components/policy/core/common/external_data_fetcher.h"
 #include "components/policy/core/common/external_data_manager.h"
-#include "components/policy/core/common/features.h"
 #include "components/policy/core/common/policy_map.h"
 #include "components/policy/core/common/policy_types.h"
 #include "components/policy/core/common/schema.h"
@@ -753,7 +753,7 @@
           entry.isolated_kiosk_app().allow_downgrades());
     }
   }
-  if (policy::features::IsHeliumArcvmKioskEnabled()) {
+  if (ash::features::IsHeliumArcvmKioskEnabled()) {
     if (entry.arcvm_kiosk_app().has_package_name()) {
       entry_dict.Set(ash::kAccountsPrefDeviceLocalAccountsKeyArcvmKioskPackage,
                      entry.arcvm_kiosk_app().package_name());
diff --git a/chrome/browser/ash/policy/core/device_policy_decoder_unittest.cc b/chrome/browser/ash/policy/core/device_policy_decoder_unittest.cc
index e1aab4f..0f18bc91 100644
--- a/chrome/browser/ash/policy/core/device_policy_decoder_unittest.cc
+++ b/chrome/browser/ash/policy/core/device_policy_decoder_unittest.cc
@@ -13,10 +13,10 @@
 #include "base/time/time.h"
 #include "base/values.h"
 #include "chrome/browser/ash/policy/core/device_local_account.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "chromeos/ash/components/policy/weekly_time/weekly_time.h"
 #include "chromeos/ash/components/policy/weekly_time/weekly_time_interval.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "components/policy/core/common/policy_bundle.h"
 #include "components/policy/policy_constants.h"
 #include "components/policy/proto/chrome_device_policy.pb.h"
diff --git a/chrome/browser/ash/policy/external_data/BUILD.gn b/chrome/browser/ash/policy/external_data/BUILD.gn
index 4c21a38..d40ff45 100644
--- a/chrome/browser/ash/policy/external_data/BUILD.gn
+++ b/chrome/browser/ash/policy/external_data/BUILD.gn
@@ -34,6 +34,7 @@
     "//chrome/browser/ash/policy/handlers",
     "//chrome/browser/ash/profiles",
     "//chrome/browser/profiles:profile",
+    "//chromeos/ash/components/policy/device_local_account",
     "//chromeos/ash/components/settings",
     "//components/policy:generated",
     "//components/policy/core/browser",
diff --git a/chrome/browser/ash/policy/external_data/cloud_external_data_policy_observer.cc b/chrome/browser/ash/policy/external_data/cloud_external_data_policy_observer.cc
index 8ceef4a..0beb6d4 100644
--- a/chrome/browser/ash/policy/external_data/cloud_external_data_policy_observer.cc
+++ b/chrome/browser/ash/policy/external_data/cloud_external_data_policy_observer.cc
@@ -20,12 +20,12 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
 #include "chromeos/ash/components/settings/cros_settings_provider.h"
 #include "components/policy/core/browser/policy_error_map.h"
 #include "components/policy/core/common/cloud/cloud_policy_core.h"
 #include "components/policy/core/common/cloud/cloud_policy_store.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "components/policy/core/common/external_data_fetcher.h"
 #include "components/policy/core/common/policy_namespace.h"
 #include "components/policy/core/common/policy_service.h"
diff --git a/chrome/browser/ash/policy/external_data/cloud_external_data_policy_observer_unittest.cc b/chrome/browser/ash/policy/external_data/cloud_external_data_policy_observer_unittest.cc
index 1316168a..69352eb 100644
--- a/chrome/browser/ash/policy/external_data/cloud_external_data_policy_observer_unittest.cc
+++ b/chrome/browser/ash/policy/external_data/cloud_external_data_policy_observer_unittest.cc
@@ -33,12 +33,12 @@
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
 #include "chromeos/ash/components/install_attributes/stub_install_attributes.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "chromeos/ash/components/settings/cros_settings.h"
 #include "components/policy/core/common/cloud/cloud_policy_core.h"
 #include "components/policy/core/common/cloud/cloud_policy_store.h"
 #include "components/policy/core/common/cloud/mock_cloud_external_data_manager.h"
 #include "components/policy/core/common/cloud/test/policy_builder.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "components/policy/core/common/external_data_fetcher.h"
 #include "components/policy/core/common/mock_configuration_policy_provider.h"
 #include "components/policy/core/common/policy_map.h"
diff --git a/chrome/browser/ash/policy/fuzzer/BUILD.gn b/chrome/browser/ash/policy/fuzzer/BUILD.gn
index b254005..463d37a 100644
--- a/chrome/browser/ash/policy/fuzzer/BUILD.gn
+++ b/chrome/browser/ash/policy/fuzzer/BUILD.gn
@@ -28,7 +28,6 @@
       "//chrome/common:constants",
       "//chromeos/ash/components/attestation",
       "//chromeos/ash/components/install_attributes",
-      "//components/exo/wayland:test_controller_stub",
       "//components/exo/wayland:ui_controls_protocol_stub",
       "//components/policy:generated",
       "//components/policy/core/browser",
diff --git a/chrome/browser/ash/policy/networking/policy_certs_browsertest.cc b/chrome/browser/ash/policy/networking/policy_certs_browsertest.cc
index 306769b..ea23dca1 100644
--- a/chrome/browser/ash/policy/networking/policy_certs_browsertest.cc
+++ b/chrome/browser/ash/policy/networking/policy_certs_browsertest.cc
@@ -49,12 +49,12 @@
 #include "chromeos/ash/components/network/onc/onc_certificate_importer.h"
 #include "chromeos/ash/components/network/onc/onc_certificate_importer_impl.h"
 #include "chromeos/ash/components/network/policy_certificate_provider.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "chromeos/components/onc/onc_test_utils.h"
 #include "chromeos/test/chromeos_test_utils.h"
 #include "components/onc/onc_constants.h"
 #include "components/policy/core/browser/browser_policy_connector.h"
 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "components/policy/core/common/mock_configuration_policy_provider.h"
 #include "components/policy/core/common/policy_switches.h"
 #include "components/policy/policy_constants.h"
diff --git a/chrome/browser/ash/policy/remote_commands/BUILD.gn b/chrome/browser/ash/policy/remote_commands/BUILD.gn
index a30228f7f..bbcbb969 100644
--- a/chrome/browser/ash/policy/remote_commands/BUILD.gn
+++ b/chrome/browser/ash/policy/remote_commands/BUILD.gn
@@ -282,7 +282,6 @@
       "//chrome/browser",
       "//chrome/browser/ash/policy/remote_commands/crd",
       "//chrome/browser/ash/policy/remote_commands/crd:test_support",
-      "//components/exo/wayland:test_controller_stub",
       "//components/exo/wayland:ui_controls_protocol_stub",
       "//components/policy/proto",
     ]
diff --git a/chrome/browser/ash/policy/reporting/BUILD.gn b/chrome/browser/ash/policy/reporting/BUILD.gn
index eaddf0b..3297599 100644
--- a/chrome/browser/ash/policy/reporting/BUILD.gn
+++ b/chrome/browser/ash/policy/reporting/BUILD.gn
@@ -192,7 +192,6 @@
       # TODO(crbug.com/335294371): Remove dependency on //chrome/browser to
       # implement the proper fix.
       "//chrome/browser",
-      "//components/exo/wayland:test_controller_stub",
       "//components/exo/wayland:ui_controls_protocol_stub",
     ]
   }
diff --git a/chrome/browser/ash/policy/reporting/user_added_removed/user_added_removed_reporter_browsertest.cc b/chrome/browser/ash/policy/reporting/user_added_removed/user_added_removed_reporter_browsertest.cc
index dd60753d..aeb4735 100644
--- a/chrome/browser/ash/policy/reporting/user_added_removed/user_added_removed_reporter_browsertest.cc
+++ b/chrome/browser/ash/policy/reporting/user_added_removed/user_added_removed_reporter_browsertest.cc
@@ -34,12 +34,12 @@
 #include "chromeos/ash/components/dbus/session_manager/fake_session_manager_client.h"
 #include "chromeos/ash/components/login/auth/public/user_context.h"
 #include "chromeos/ash/components/login/login_state/login_state.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
 #include "chromeos/dbus/missive/missive_client.h"
 #include "chromeos/dbus/missive/missive_client_test_observer.h"
 #include "components/account_id/account_id.h"
 #include "components/policy/core/common/cloud/mock_cloud_policy_store.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "components/policy/proto/chrome_device_policy.pb.h"
 #include "components/policy/proto/device_management_backend.pb.h"
 #include "components/reporting/proto/synced/record.pb.h"
diff --git a/chrome/browser/ash/policy/reporting/user_session_activity/BUILD.gn b/chrome/browser/ash/policy/reporting/user_session_activity/BUILD.gn
index 5bb3fb07..0821766 100644
--- a/chrome/browser/ash/policy/reporting/user_session_activity/BUILD.gn
+++ b/chrome/browser/ash/policy/reporting/user_session_activity/BUILD.gn
@@ -23,6 +23,7 @@
     "//chrome/browser/ash/power/ml",
     "//chrome/browser/ash/profiles",
     "//chrome/browser/policy/messaging_layer/proto:user_session_activity_proto",
+    "//chromeos/ash/components/policy/device_local_account",
     "//chromeos/ash/components/settings",
     "//components/viz/host",
   ]
diff --git a/chrome/browser/ash/policy/reporting/user_session_activity/user_session_activity_reporter.cc b/chrome/browser/ash/policy/reporting/user_session_activity/user_session_activity_reporter.cc
index da6d7d63..5edab83f 100644
--- a/chrome/browser/ash/policy/reporting/user_session_activity/user_session_activity_reporter.cc
+++ b/chrome/browser/ash/policy/reporting/user_session_activity/user_session_activity_reporter.cc
@@ -20,9 +20,9 @@
 #include "chrome/browser/ash/policy/reporting/user_event_reporter_helper.h"
 #include "chrome/browser/ash/policy/reporting/user_session_activity/user_session_activity_reporter_delegate.h"
 #include "chrome/browser/policy/messaging_layer/proto/synced/user_session_activity.pb.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
 #include "components/account_id/account_id.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "components/reporting/proto/synced/record_constants.pb.h"
 #include "components/user_manager/user.h"
 #include "components/user_manager/user_manager.h"
diff --git a/chrome/browser/ash/policy/reporting/user_session_activity/user_session_activity_reporter_delegate.cc b/chrome/browser/ash/policy/reporting/user_session_activity/user_session_activity_reporter_delegate.cc
index 938ea67..5215ed8 100644
--- a/chrome/browser/ash/policy/reporting/user_session_activity/user_session_activity_reporter_delegate.cc
+++ b/chrome/browser/ash/policy/reporting/user_session_activity/user_session_activity_reporter_delegate.cc
@@ -19,8 +19,8 @@
 #include "chrome/browser/ash/policy/reporting/user_session_activity/user_session_activity_reporter.h"
 #include "chrome/browser/ash/power/ml/idle_event_notifier.h"
 #include "chrome/browser/policy/messaging_layer/proto/synced/user_session_activity.pb.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "components/user_manager/user.h"
 #include "components/user_manager/user_manager.h"
 #include "components/user_manager/user_names.h"
diff --git a/chrome/browser/ash/policy/reporting/user_session_activity/user_session_activity_reporter_delegate_unittest.cc b/chrome/browser/ash/policy/reporting/user_session_activity/user_session_activity_reporter_delegate_unittest.cc
index d65e1a1..30a8b71 100644
--- a/chrome/browser/ash/policy/reporting/user_session_activity/user_session_activity_reporter_delegate_unittest.cc
+++ b/chrome/browser/ash/policy/reporting/user_session_activity/user_session_activity_reporter_delegate_unittest.cc
@@ -18,8 +18,8 @@
 #include "chrome/browser/ash/policy/reporting/user_event_reporter_helper_testing.h"
 #include "chrome/browser/ash/power/ml/idle_event_notifier.h"
 #include "chrome/browser/policy/messaging_layer/proto/synced/user_session_activity.pb.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "components/reporting/client/mock_report_queue.h"
 #include "components/user_manager/scoped_user_manager.h"
 #include "components/user_manager/user.h"
diff --git a/chrome/browser/ash/policy/reporting/user_session_activity/user_session_activity_reporter_unittest.cc b/chrome/browser/ash/policy/reporting/user_session_activity/user_session_activity_reporter_unittest.cc
index 2c14535..69b8c055 100644
--- a/chrome/browser/ash/policy/reporting/user_session_activity/user_session_activity_reporter_unittest.cc
+++ b/chrome/browser/ash/policy/reporting/user_session_activity/user_session_activity_reporter_unittest.cc
@@ -25,8 +25,8 @@
 #include "chrome/test/base/scoped_testing_local_state.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "chromeos/dbus/power/fake_power_manager_client.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "components/reporting/client/mock_report_queue.h"
 #include "components/reporting/proto/synced/record_constants.pb.h"
 #include "components/session_manager/core/session_manager.h"
diff --git a/chrome/browser/ash/policy/status_collector/device_status_collector.cc b/chrome/browser/ash/policy/status_collector/device_status_collector.cc
index 3524c1cd..0ca8db2 100644
--- a/chrome/browser/ash/policy/status_collector/device_status_collector.cc
+++ b/chrome/browser/ash/policy/status_collector/device_status_collector.cc
@@ -25,6 +25,7 @@
 #include <string_view>
 #include <utility>
 
+#include "ash/constants/ash_features.h"
 #include "base/check.h"
 #include "base/check_op.h"
 #include "base/files/file_enumerator.h"
@@ -78,6 +79,7 @@
 #include "chromeos/ash/components/network/network_handler.h"
 #include "chromeos/ash/components/network/network_state.h"
 #include "chromeos/ash/components/network/network_state_handler.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "chromeos/ash/components/settings/cros_settings.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
 #include "chromeos/ash/components/settings/timezone_settings.h"
@@ -93,8 +95,6 @@
 #include "components/policy/core/browser/browser_policy_connector.h"
 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
 #include "components/policy/core/common/cloud/cloud_policy_util.h"
-#include "components/policy/core/common/device_local_account_type.h"
-#include "components/policy/core/common/features.h"
 #include "components/policy/proto/device_management_backend.pb.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "components/prefs/pref_registry_simple.h"
@@ -662,7 +662,7 @@
       return em::ActiveTimePeriod::SESSION_IWA_KIOSK;
 
     case DeviceLocalAccountType::kArcvmKioskApp:
-      if (policy::features::IsHeliumArcvmKioskEnabled()) {
+      if (ash::features::IsHeliumArcvmKioskEnabled()) {
         return em::ActiveTimePeriod::SESSION_ARC_KIOSK;
       }
       break;
@@ -2725,7 +2725,7 @@
       running_kiosk_app->set_app_id(account->kiosk_iwa_info.web_bundle_id());
       break;
     case DeviceLocalAccountType::kArcvmKioskApp:
-      if (policy::features::IsHeliumArcvmKioskEnabled()) {
+      if (ash::features::IsHeliumArcvmKioskEnabled()) {
         // Use package name as app ID for ARC Kiosks.
         running_kiosk_app->set_app_id(
             account->arcvm_kiosk_app_info.package_name());
@@ -3024,7 +3024,7 @@
       app_status->set_app_id(account->kiosk_iwa_info.web_bundle_id());
       break;
     case DeviceLocalAccountType::kArcvmKioskApp:
-      if (policy::features::IsHeliumArcvmKioskEnabled()) {
+      if (ash::features::IsHeliumArcvmKioskEnabled()) {
         // Use package name as app ID for ARC Kiosks.
         app_status->set_app_id(account->arcvm_kiosk_app_info.package_name());
       }
diff --git a/chrome/browser/ash/policy/status_collector/device_status_collector_browsertest.cc b/chrome/browser/ash/policy/status_collector/device_status_collector_browsertest.cc
index 8b455180..9f09d551 100644
--- a/chrome/browser/ash/policy/status_collector/device_status_collector_browsertest.cc
+++ b/chrome/browser/ash/policy/status_collector/device_status_collector_browsertest.cc
@@ -100,6 +100,7 @@
 #include "chromeos/ash/components/network/network_handler_test_helper.h"
 #include "chromeos/ash/components/network/network_state.h"
 #include "chromeos/ash/components/network/network_state_handler.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
 #include "chromeos/ash/components/settings/timezone_settings.h"
 #include "chromeos/ash/components/system/fake_statistics_provider.h"
@@ -110,7 +111,6 @@
 #include "chromeos/dbus/tpm_manager/tpm_manager_client.h"
 #include "components/account_id/account_id.h"
 #include "components/ownership/mock_owner_key_util.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "components/policy/proto/device_management_backend.pb.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
diff --git a/chrome/browser/ash/policy/uploading/status_uploader_unittest.cc b/chrome/browser/ash/policy/uploading/status_uploader_unittest.cc
index c309f11..9b802ec 100644
--- a/chrome/browser/ash/policy/uploading/status_uploader_unittest.cc
+++ b/chrome/browser/ash/policy/uploading/status_uploader_unittest.cc
@@ -21,12 +21,12 @@
 #include "chrome/browser/ash/policy/status_collector/device_status_collector.h"
 #include "chrome/browser/ash/settings/scoped_testing_cros_settings.h"
 #include "chrome/browser/ash/settings/stub_cros_settings_provider.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
 #include "chromeos/dbus/power/power_manager_client.h"
 #include "chromeos/dbus/tpm_manager/tpm_manager_client.h"
 #include "components/policy/core/common/cloud/cloud_policy_client.h"
 #include "components/policy/core/common/cloud/mock_cloud_policy_client.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "components/prefs/testing_pref_service.h"
 #include "components/session_manager/core/session_manager.h"
 #include "content/public/test/browser_task_environment.h"
diff --git a/chrome/browser/ash/printing/BUILD.gn b/chrome/browser/ash/printing/BUILD.gn
index fa575b3..2d5cd22 100644
--- a/chrome/browser/ash/printing/BUILD.gn
+++ b/chrome/browser/ash/printing/BUILD.gn
@@ -258,7 +258,6 @@
     "//base",
     "//base/test:test_support",
     "//chrome/browser",
-    "//components/exo/wayland:test_controller_stub",
     "//components/exo/wayland:ui_controls_protocol_stub",
   ]
 }
diff --git a/chrome/browser/ash/settings/device_settings_provider.cc b/chrome/browser/ash/settings/device_settings_provider.cc
index add301c5..b03703b7 100644
--- a/chrome/browser/ash/settings/device_settings_provider.cc
+++ b/chrome/browser/ash/settings/device_settings_provider.cc
@@ -433,7 +433,7 @@
         entry_dict.Set(kAccountsPrefDeviceLocalAccountsKeyKioskAppUpdateURL,
                        entry.kiosk_app().update_url());
       }
-      if (policy::features::IsHeliumArcvmKioskEnabled()) {
+      if (ash::features::IsHeliumArcvmKioskEnabled()) {
         if (entry.arcvm_kiosk_app().has_package_name()) {
           entry_dict.Set(kAccountsPrefDeviceLocalAccountsKeyArcvmKioskPackage,
                          entry.arcvm_kiosk_app().package_name());
diff --git a/chrome/browser/ash/settings/device_settings_provider_unittest.cc b/chrome/browser/ash/settings/device_settings_provider_unittest.cc
index f708b95..1d6903d 100644
--- a/chrome/browser/ash/settings/device_settings_provider_unittest.cc
+++ b/chrome/browser/ash/settings/device_settings_provider_unittest.cc
@@ -22,9 +22,9 @@
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chromeos/ash/components/install_attributes/stub_install_attributes.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
 #include "components/policy/core/common/cloud/test/policy_builder.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "components/policy/proto/chrome_device_policy.pb.h"
 #include "components/policy/proto/device_management_backend.pb.h"
 #include "testing/gmock/include/gmock/gmock.h"
diff --git a/chrome/browser/ash/test/BUILD.gn b/chrome/browser/ash/test/BUILD.gn
index 9cea795..bf987a3 100644
--- a/chrome/browser/ash/test/BUILD.gn
+++ b/chrome/browser/ash/test/BUILD.gn
@@ -26,6 +26,7 @@
     "//ash/constants",
     "//base",
     "//chrome/test:test_support_ui",
+    "//chromeos/ash/components/policy/device_local_account",
     "//chromeos/ash/components/settings",
     "//chromeos/ash/components/settings:test_support",
     "//components/account_id",
diff --git a/chrome/browser/ash/test/kiosk_app_logged_in_browser_test_mixin.cc b/chrome/browser/ash/test/kiosk_app_logged_in_browser_test_mixin.cc
index daa5d1f..95e43a5 100644
--- a/chrome/browser/ash/test/kiosk_app_logged_in_browser_test_mixin.cc
+++ b/chrome/browser/ash/test/kiosk_app_logged_in_browser_test_mixin.cc
@@ -9,9 +9,9 @@
 #include "ash/constants/ash_switches.h"
 #include "base/check.h"
 #include "base/values.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "chromeos/ash/components/settings/device_settings_cache_test_support.h"
 #include "components/account_id/account_id.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "components/policy/proto/chrome_device_policy.pb.h"
 #include "components/policy/proto/device_management_backend.pb.h"
 #include "components/prefs/pref_service.h"
diff --git a/chrome/browser/ash/test/public_account_logged_in_browser_test_mixin.cc b/chrome/browser/ash/test/public_account_logged_in_browser_test_mixin.cc
index 2dcf5e218..5abbb43 100644
--- a/chrome/browser/ash/test/public_account_logged_in_browser_test_mixin.cc
+++ b/chrome/browser/ash/test/public_account_logged_in_browser_test_mixin.cc
@@ -9,10 +9,10 @@
 #include "ash/constants/ash_switches.h"
 #include "base/check.h"
 #include "base/values.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
 #include "chromeos/ash/components/settings/device_settings_cache_test_support.h"
 #include "components/account_id/account_id.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "components/policy/proto/chrome_device_policy.pb.h"
 #include "components/policy/proto/device_management_backend.pb.h"
 #include "components/prefs/pref_service.h"
diff --git a/chrome/browser/ash/test/web_kiosk_app_logged_in_browser_test_mixin.cc b/chrome/browser/ash/test/web_kiosk_app_logged_in_browser_test_mixin.cc
index b848b998..03fd3fd8 100644
--- a/chrome/browser/ash/test/web_kiosk_app_logged_in_browser_test_mixin.cc
+++ b/chrome/browser/ash/test/web_kiosk_app_logged_in_browser_test_mixin.cc
@@ -9,9 +9,9 @@
 #include "ash/constants/ash_switches.h"
 #include "base/check.h"
 #include "base/values.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "chromeos/ash/components/settings/device_settings_cache_test_support.h"
 #include "components/account_id/account_id.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "components/policy/proto/chrome_device_policy.pb.h"
 #include "components/policy/proto/device_management_backend.pb.h"
 #include "components/prefs/pref_service.h"
diff --git a/chrome/browser/auxiliary_search/BUILD.gn b/chrome/browser/auxiliary_search/BUILD.gn
index b61e6ea2..0061088 100644
--- a/chrome/browser/auxiliary_search/BUILD.gn
+++ b/chrome/browser/auxiliary_search/BUILD.gn
@@ -25,6 +25,7 @@
     "java/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchMetrics.java",
     "java/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchMultiDataControllerImpl.java",
     "java/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchProvider.java",
+    "java/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchTopSiteProviderBridge.java",
     "java/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchUtils.java",
     "java/src/org/chromium/chrome/browser/auxiliary_search/FetchAndRankHelper.java",
     "java/src/org/chromium/chrome/browser/auxiliary_search/module/AuxiliarySearchModuleBuilder.java",
@@ -81,6 +82,7 @@
 generate_jni("jni_headers") {
   sources = [
     "java/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchBridge.java",
+    "java/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchTopSiteProviderBridge.java",
     "java/src/org/chromium/chrome/browser/auxiliary_search/FetchAndRankHelper.java",
   ]
 }
@@ -112,6 +114,7 @@
     "junit/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchMultiDataControllerImplUnitTest.java",
     "junit/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchProviderUnitTest.java",
     "junit/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchTestHelper.java",
+    "junit/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchTopSiteProviderBridgeUnitTest.java",
     "junit/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchUtilsUnitTest.java",
     "junit/src/org/chromium/chrome/browser/auxiliary_search/module/AuxiliarySearchModuleBuilderUnitTest.java",
     "junit/src/org/chromium/chrome/browser/auxiliary_search/module/AuxiliarySearchModuleMediatorUnitTest.java",
diff --git a/chrome/browser/auxiliary_search/auxiliary_search_provider.cc b/chrome/browser/auxiliary_search/auxiliary_search_provider.cc
index b99cc61ae..5f6abba 100644
--- a/chrome/browser/auxiliary_search/auxiliary_search_provider.cc
+++ b/chrome/browser/auxiliary_search/auxiliary_search_provider.cc
@@ -21,7 +21,6 @@
 #include "chrome/browser/auxiliary_search/fetch_and_rank_helper.h"
 #include "chrome/browser/auxiliary_search/proto/auxiliary_search_group.pb.h"
 #include "chrome/browser/flags/android/chrome_feature_list.h"
-#include "chrome/browser/ntp_tiles/chrome_most_visited_sites_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_keyed_service_factory.h"
 #include "chrome/browser/visited_url_ranking/visited_url_ranking_service_factory.h"
@@ -50,13 +49,6 @@
 using visited_url_ranking::VisitedURLRankingServiceFactory;
 
 namespace {
-// Must match Java Tab.INVALID_TAB_ID.
-static constexpr int kInvalidTabId = -1;
-// The next id to assign.
-static int kIdCounter = 0;
-
-constexpr int kMaxNumMostVisitedSites = 4;
-
 using BackToJavaCallback = base::OnceCallback<void(
     std::unique_ptr<std::vector<base::WeakPtr<TabAndroid>>>)>;
 
@@ -94,10 +86,9 @@
     if (base::FeatureList::IsEnabled(
             chrome::android::kAndroidAppIntegrationMultiDataSource)) {
       return std::make_unique<AuxiliarySearchProvider>(
-          VisitedURLRankingServiceFactory::GetForProfile(profile),
-          ChromeMostVisitedSitesFactory::NewForProfile(profile));
+          VisitedURLRankingServiceFactory::GetForProfile(profile));
     }
-    return std::make_unique<AuxiliarySearchProvider>(nullptr, nullptr);
+    return std::make_unique<AuxiliarySearchProvider>(nullptr);
   }
 };
 
@@ -144,31 +135,14 @@
   Java_AuxiliarySearchBridge_onDataReady(env, entries, j_callback);
 }
 
-// Converts the score to be an integer. Usually the score is between 0 and 1.0.
-int convertSiteSuggestionScore(double score) {
-  return std::max(0, static_cast<int>(score * 100));
-}
-
 }  // namespace
 
 AuxiliarySearchProvider::AuxiliarySearchProvider(
-    VisitedURLRankingService* ranking_service,
-    std::unique_ptr<ntp_tiles::MostVisitedSites> most_visited_sites)
-    : ranking_service_(ranking_service),
-      most_visited_sites_(std::move(most_visited_sites)) {}
+    VisitedURLRankingService* ranking_service)
+    : ranking_service_(ranking_service) {}
 
 AuxiliarySearchProvider::~AuxiliarySearchProvider() = default;
 
-void AuxiliarySearchProvider::Shutdown() {
-  if (most_visited_sites_) {
-    if (!observers_map_.empty()) {
-      most_visited_sites_->RemoveMostVisitedURLsObserver(this);
-    }
-    most_visited_sites_.reset();
-  }
-  observers_map_.clear();
-}
-
 void AuxiliarySearchProvider::GetNonSensitiveTabs(
     JNIEnv* env,
     const base::android::JavaParamRef<jobjectArray>& j_tabs_android,
@@ -198,91 +172,6 @@
   helper->StartFetching();
 }
 
-int AuxiliarySearchProvider::SetObserverAndTrigger(
-    JNIEnv* env,
-    const base::android::JavaRef<jobject>& j_ref_obj) {
-  auto j_ref = jni_zero::ScopedJavaGlobalRef<jobject>(j_ref_obj);
-  int id = kIdCounter++;
-  observers_map_[id] = j_ref;
-
-  // AuxiliarySearchProvider registers itself as an observer of the
-  // |most_visited_sites_|. Don't register again if it has registered before.
-  if (observers_map_.size() > 1) {
-    return id;
-  }
-
-  CHECK(most_visited_sites_);
-  most_visited_sites_->AddMostVisitedURLsObserver(this,
-                                                  kMaxNumMostVisitedSites);
-  return id;
-}
-
-void AuxiliarySearchProvider::RemoveObserver(JNIEnv* env, jint id) {
-  CHECK(observers_map_.contains(id));
-  observers_map_.erase(id);
-
-  if (observers_map_.size() == 0) {
-    most_visited_sites_->RemoveMostVisitedURLsObserver(this);
-  }
-}
-
-void AuxiliarySearchProvider::GetMostVisitedSites(JNIEnv* env) const {
-  CHECK(most_visited_sites_);
-
-  most_visited_sites_->RefreshTiles();
-}
-
-void AuxiliarySearchProvider::OnURLsAvailable(
-    const std::map<ntp_tiles::SectionType, ntp_tiles::NTPTilesVector>&
-        sections) {
-  CHECK(most_visited_sites_);
-  if (observers_map_.empty()) {
-    return;
-  }
-
-  JNIEnv* env = base::android::AttachCurrentThread();
-  std::vector<jni_zero::ScopedJavaLocalRef<jobject>> entries;
-  // Uses only personalized tiles for auxiliary search.
-  auto it = sections.find(ntp_tiles::SectionType::PERSONALIZED);
-  if (it == sections.end()) {
-    return;
-  }
-
-  for (const ntp_tiles::NTPTile& tile : it->second) {
-    // Filters the tile list to include only TOP_SITES and CUSTOM_LINKS tiles.
-    if (tile.source != ntp_tiles::TileSource::TOP_SITES &&
-        tile.source != ntp_tiles::TileSource::CUSTOM_LINKS) {
-      continue;
-    }
-
-    entries.push_back(Java_AuxiliarySearchBridge_addDataEntry(
-        env, static_cast<int>(AuxiliarySearchEntryType::kTopSite),
-        url::GURLAndroid::FromNativeGURL(env, tile.url),
-        base::android::ConvertUTF16ToJavaString(env, tile.title),
-        tile.last_visit_time.InMillisecondsSinceUnixEpoch(), kInvalidTabId,
-        /* appId= */ nullptr,
-        std::abs(static_cast<int>(
-            base::Hash(tile.url.spec() + base::UTF16ToUTF8(tile.title)))),
-        convertSiteSuggestionScore(tile.score)));
-  }
-
-  for (auto const& [id, observer] : observers_map_) {
-    Java_AuxiliarySearchBridge_onMostVisitedSitesURLsAvailable(env, observer,
-                                                               entries);
-  }
-}
-
-void AuxiliarySearchProvider::OnIconMadeAvailable(const GURL& site_url) {
-  if (observers_map_.empty()) {
-    return;
-  }
-
-  JNIEnv* env = base::android::AttachCurrentThread();
-  for (auto const& [id, observer] : observers_map_) {
-    Java_AuxiliarySearchBridge_onIconMadeAvailable(env, observer, site_url);
-  }
-}
-
 // static
 void AuxiliarySearchProvider::FilterTabsByScheme(
     std::vector<raw_ptr<TabAndroid, VectorExperimental>>& tabs) {
diff --git a/chrome/browser/auxiliary_search/auxiliary_search_provider.h b/chrome/browser/auxiliary_search/auxiliary_search_provider.h
index de74a86..dd19629 100644
--- a/chrome/browser/auxiliary_search/auxiliary_search_provider.h
+++ b/chrome/browser/auxiliary_search/auxiliary_search_provider.h
@@ -15,8 +15,6 @@
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "components/keyed_service/core/keyed_service.h"
-#include "components/ntp_tiles/most_visited_sites.h"
-#include "components/ntp_tiles/ntp_tile.h"
 
 namespace visited_url_ranking {
 class VisitedURLRankingService;
@@ -35,12 +33,10 @@
 
 // AuxiliarySearchProvider is responsible for providing the necessary
 // information for the auxiliary search.
-class AuxiliarySearchProvider : public KeyedService,
-                                public ntp_tiles::MostVisitedSites::Observer {
+class AuxiliarySearchProvider : public KeyedService {
  public:
   AuxiliarySearchProvider(
-      visited_url_ranking::VisitedURLRankingService* ranking_service,
-      std::unique_ptr<ntp_tiles::MostVisitedSites> most_visited_sites);
+      visited_url_ranking::VisitedURLRankingService* ranking_service);
 
   ~AuxiliarySearchProvider() override;
 
@@ -53,18 +49,6 @@
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& j_callback_obj) const;
 
-  // Sets an observer and immediately fetches the current most visited sites
-  // suggestions. Returns the ID of the observer.
-  int SetObserverAndTrigger(JNIEnv* env,
-                            const base::android::JavaRef<jobject>& j_ref_obj);
-
-  // Removes the observer with the given ID from the observers list, and stops
-  // observing the most visited sites if all observers are removed.
-  void RemoveObserver(JNIEnv* env, jint observerId);
-
-  // Starts a fetch of the current most visited sites suggestions.
-  void GetMostVisitedSites(JNIEnv* env) const;
-
   static void EnsureFactoryBuilt();
 
  private:
@@ -78,20 +62,10 @@
                            QueryEmptyTabList);
   FRIEND_TEST_ALL_PREFIXES(AuxiliarySearchProviderBrowserTest, NativeTabTest);
   FRIEND_TEST_ALL_PREFIXES(AuxiliarySearchProviderBrowserTest, FilterTabsTest);
-  FRIEND_TEST_ALL_PREFIXES(AuxiliarySearchProviderTest, AddAndRemoveObservers);
 
   using NonSensitiveTabsCallback =
       base::OnceCallback<void(std::vector<base::WeakPtr<TabAndroid>>)>;
 
-  // KeyedService:
-  void Shutdown() override;
-
-  // ntp_tiles::MostVisitedSites::Observer implementation.
-  void OnURLsAvailable(
-      const std::map<ntp_tiles::SectionType, ntp_tiles::NTPTilesVector>&
-          sections) override;
-  void OnIconMadeAvailable(const GURL& site_url) override;
-
   static void FilterTabsByScheme(
       std::vector<raw_ptr<TabAndroid, VectorExperimental>>& tabs);
 
@@ -99,10 +73,7 @@
       std::vector<raw_ptr<TabAndroid, VectorExperimental>> all_tabs,
       NonSensitiveTabsCallback callback) const;
 
-  std::map<int, jni_zero::ScopedJavaGlobalRef<jobject>> observers_map_;
-
   const raw_ptr<visited_url_ranking::VisitedURLRankingService> ranking_service_;
-  std::unique_ptr<ntp_tiles::MostVisitedSites> most_visited_sites_;
 };
 
 #endif  // CHROME_BROWSER_AUXILIARY_SEARCH_AUXILIARY_SEARCH_PROVIDER_H_
diff --git a/chrome/browser/auxiliary_search/auxiliary_search_provider_browsertest.cc b/chrome/browser/auxiliary_search/auxiliary_search_provider_browsertest.cc
index f957c5d..8b8d0301 100644
--- a/chrome/browser/auxiliary_search/auxiliary_search_provider_browsertest.cc
+++ b/chrome/browser/auxiliary_search/auxiliary_search_provider_browsertest.cc
@@ -45,7 +45,7 @@
         web_contents(),
         embedded_test_server()->GetURL("/android/google.html")));
     auxiliary_search_provider_ =
-        std::make_unique<AuxiliarySearchProvider>(nullptr, nullptr);
+        std::make_unique<AuxiliarySearchProvider>(nullptr);
     PersistedTabDataAndroid::OnDeferredStartup();
     content::RunAllTasksUntilIdle();
   }
diff --git a/chrome/browser/auxiliary_search/auxiliary_search_provider_unittest.cc b/chrome/browser/auxiliary_search/auxiliary_search_provider_unittest.cc
deleted file mode 100644
index cd07761a..0000000
--- a/chrome/browser/auxiliary_search/auxiliary_search_provider_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/auxiliary_search/auxiliary_search_provider.h"
-
-#include <memory>
-
-#include "components/ntp_tiles/icon_cacher.h"
-#include "components/ntp_tiles/most_visited_sites.h"
-#include "components/prefs/testing_pref_service.h"
-#include "components/visited_url_ranking/public/testing/mock_visited_url_ranking_service.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace {
-constexpr size_t kMaxNumMostVisitedSites = 4;
-}  // namespace
-
-class MockMostVisitedSites : public ntp_tiles::MostVisitedSites {
- public:
-  explicit MockMostVisitedSites(PrefService* prefs)
-      : ntp_tiles::MostVisitedSites(prefs,
-                                    /*identity_manager*/ nullptr,
-                                    /*supervised_user_service*/ nullptr,
-                                    /*top_sites*/ nullptr,
-                                    /*popular_sites*/ nullptr,
-                                    /*custom_links*/ nullptr,
-                                    /*icon_cacher*/ nullptr,
-                                    /*is_default_chrome_app_migrated*/ true,
-                                    /*is_custom_links_mixable*/ false) {}
-
-  MockMostVisitedSites(const MockMostVisitedSites&) = delete;
-  MockMostVisitedSites& operator=(const MockMostVisitedSites&) = delete;
-
-  ~MockMostVisitedSites() override = default;
-
-  MOCK_METHOD2(AddMostVisitedURLsObserver,
-               void(Observer* observer, size_t max_num_sites));
-  MOCK_METHOD1(RemoveMostVisitedURLsObserver, void(Observer* observer));
-};
-
-// Unit tests for AuxiliarySearchProvider.
-class AuxiliarySearchProviderTest : public ::testing::Test {
- public:
-  AuxiliarySearchProviderTest() {
-    auto mock_most_visited_sites =
-        std::make_unique<MockMostVisitedSites>(&testing_prefs_);
-    auxiliary_search_provider_ = std::make_unique<AuxiliarySearchProvider>(
-        &mock_visited_url_ranking_service_, std::move(mock_most_visited_sites));
-  }
-
-  AuxiliarySearchProviderTest(const AuxiliarySearchProviderTest&) = delete;
-  AuxiliarySearchProviderTest& operator=(const AuxiliarySearchProviderTest&) =
-      delete;
-
- protected:
-  TestingPrefServiceSimple testing_prefs_;
-  visited_url_ranking::MockVisitedURLRankingService
-      mock_visited_url_ranking_service_;
-  std::unique_ptr<AuxiliarySearchProvider> auxiliary_search_provider_;
-};
-
-TEST_F(AuxiliarySearchProviderTest, AddAndRemoveObservers) {
-  auto j_ref = base::android::JavaRef<jobject>();
-  MockMostVisitedSites* mock_most_visited_sites =
-      static_cast<MockMostVisitedSites*>(
-          auxiliary_search_provider_->most_visited_sites_.get());
-
-  // Verifies to start observing most visited sites when the first observer is
-  // added.
-  EXPECT_CALL(*mock_most_visited_sites,
-              AddMostVisitedURLsObserver(testing::_, kMaxNumMostVisitedSites))
-      .Times(1);
-  int id1 = auxiliary_search_provider_->SetObserverAndTrigger(nullptr, j_ref);
-  EXPECT_EQ(0, id1);
-  EXPECT_EQ(1u, auxiliary_search_provider_->observers_map_.size());
-
-  // Verifies not to call AddMostVisitedURLsObserver() again when more observer
-  // is added.
-  EXPECT_CALL(*mock_most_visited_sites,
-              AddMostVisitedURLsObserver(testing::_, kMaxNumMostVisitedSites))
-      .Times(0);
-  auto j_ref_1 = base::android::JavaRef<jobject>();
-  int id2 = auxiliary_search_provider_->SetObserverAndTrigger(nullptr, j_ref_1);
-  EXPECT_EQ(1, id2);
-  EXPECT_EQ(2u, auxiliary_search_provider_->observers_map_.size());
-
-  // Verifies still observing the most visited sites when an observer is
-  // removed.
-  EXPECT_CALL(*mock_most_visited_sites,
-              RemoveMostVisitedURLsObserver(testing::_))
-      .Times(0);
-  auxiliary_search_provider_->RemoveObserver(nullptr, id2);
-  EXPECT_EQ(1u, auxiliary_search_provider_->observers_map_.size());
-
-  // Verifies not to observe most visited sites after the last observer is
-  // removed.
-  EXPECT_CALL(*mock_most_visited_sites,
-              RemoveMostVisitedURLsObserver(testing::_))
-      .Times(1);
-  auxiliary_search_provider_->RemoveObserver(nullptr, id1);
-  EXPECT_TRUE(auxiliary_search_provider_->observers_map_.empty());
-}
diff --git a/chrome/browser/auxiliary_search/auxiliary_search_top_site_provider_bridge.cc b/chrome/browser/auxiliary_search/auxiliary_search_top_site_provider_bridge.cc
new file mode 100644
index 0000000..71e8545
--- /dev/null
+++ b/chrome/browser/auxiliary_search/auxiliary_search_top_site_provider_bridge.cc
@@ -0,0 +1,120 @@
+// 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 "auxiliary_search_top_site_provider_bridge.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_array.h"
+#include "base/android/jni_string.h"
+#include "base/hash/hash.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/auxiliary_search/auxiliary_search_provider.h"
+#include "chrome/browser/flags/android/chrome_feature_list.h"
+#include "chrome/browser/ntp_tiles/chrome_most_visited_sites_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/ntp_tiles/constants.h"
+#include "url/android/gurl_android.h"
+#include "url/url_constants.h"
+
+// Must come after all headers that specialize FromJniType() / ToJniType().
+#include "chrome/browser/auxiliary_search/jni_headers/AuxiliarySearchTopSiteProviderBridge_jni.h"
+
+namespace {
+// Must match Java Tab.INVALID_TAB_ID.
+static constexpr int kInvalidTabId = -1;
+
+constexpr int kMaxNumMostVisitedSites = 4;
+
+// Converts the score to be an integer. Usually the score is between 0 and 1.0.
+int convertSiteSuggestionScore(double score) {
+  return std::max(0, static_cast<int>(score * 100));
+}
+
+}  // namespace
+
+AuxiliarySearchTopSiteProviderBridge::AuxiliarySearchTopSiteProviderBridge(
+    std::unique_ptr<ntp_tiles::MostVisitedSites> most_visited_sites)
+    : most_visited_sites_(std::move(most_visited_sites)) {}
+
+AuxiliarySearchTopSiteProviderBridge::~AuxiliarySearchTopSiteProviderBridge() =
+    default;
+
+void AuxiliarySearchTopSiteProviderBridge::SetObserverAndTrigger(
+    JNIEnv* env,
+    const base::android::JavaRef<jobject>& j_ref_obj) {
+  observer_ = jni_zero::ScopedJavaGlobalRef<jobject>(j_ref_obj);
+
+  CHECK(most_visited_sites_);
+  most_visited_sites_->AddMostVisitedURLsObserver(this,
+                                                  kMaxNumMostVisitedSites);
+}
+
+void AuxiliarySearchTopSiteProviderBridge::Destroy(JNIEnv* env) {
+  RemoveObserver();
+  delete this;
+}
+
+void AuxiliarySearchTopSiteProviderBridge::GetMostVisitedSites(
+    JNIEnv* env) const {
+  CHECK(most_visited_sites_);
+
+  most_visited_sites_->RefreshTiles();
+}
+
+void AuxiliarySearchTopSiteProviderBridge::RemoveObserver() {
+  most_visited_sites_->RemoveMostVisitedURLsObserver(this);
+  observer_ = nullptr;
+}
+
+void AuxiliarySearchTopSiteProviderBridge::OnURLsAvailable(
+    const std::map<ntp_tiles::SectionType, ntp_tiles::NTPTilesVector>&
+        sections) {
+  CHECK(most_visited_sites_);
+
+  JNIEnv* env = base::android::AttachCurrentThread();
+  std::vector<jni_zero::ScopedJavaLocalRef<jobject>> entries;
+  // Uses only personalized tiles for auxiliary search.
+  auto it = sections.find(ntp_tiles::SectionType::PERSONALIZED);
+  if (it == sections.end()) {
+    return;
+  }
+
+  for (const ntp_tiles::NTPTile& tile : it->second) {
+    // Filters the tile list to include only TOP_SITES and CUSTOM_LINKS tiles.
+    if (tile.source != ntp_tiles::TileSource::TOP_SITES &&
+        tile.source != ntp_tiles::TileSource::CUSTOM_LINKS) {
+      continue;
+    }
+
+    entries.push_back(Java_AuxiliarySearchTopSiteProviderBridge_addDataEntry(
+        env, static_cast<int>(AuxiliarySearchEntryType::kTopSite),
+        url::GURLAndroid::FromNativeGURL(env, tile.url),
+        base::android::ConvertUTF16ToJavaString(env, tile.title),
+        tile.last_visit_time.InMillisecondsSinceUnixEpoch(), kInvalidTabId,
+        /* appId= */ nullptr,
+        std::abs(static_cast<int>(
+            base::Hash(tile.url.spec() + base::UTF16ToUTF8(tile.title)))),
+        convertSiteSuggestionScore(tile.score)));
+  }
+
+  Java_AuxiliarySearchTopSiteProviderBridge_onMostVisitedSitesURLsAvailable(
+      env, observer_, entries);
+}
+
+void AuxiliarySearchTopSiteProviderBridge::OnIconMadeAvailable(
+    const GURL& site_url) {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  Java_AuxiliarySearchTopSiteProviderBridge_onIconMadeAvailable(env, observer_,
+                                                                site_url);
+}
+
+static jlong JNI_AuxiliarySearchTopSiteProviderBridge_Init(
+    JNIEnv* env,
+    const jni_zero::JavaParamRef<jobject>& obj,
+    Profile* profile) {
+  DCHECK(profile);
+
+  return reinterpret_cast<intptr_t>(new AuxiliarySearchTopSiteProviderBridge(
+      ChromeMostVisitedSitesFactory::NewForProfile(profile)));
+}
diff --git a/chrome/browser/auxiliary_search/auxiliary_search_top_site_provider_bridge.h b/chrome/browser/auxiliary_search/auxiliary_search_top_site_provider_bridge.h
new file mode 100644
index 0000000..071c78a
--- /dev/null
+++ b/chrome/browser/auxiliary_search/auxiliary_search_top_site_provider_bridge.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_AUXILIARY_SEARCH_AUXILIARY_SEARCH_TOP_SITE_PROVIDER_BRIDGE_H_
+#define CHROME_BROWSER_AUXILIARY_SEARCH_AUXILIARY_SEARCH_TOP_SITE_PROVIDER_BRIDGE_H_
+
+#include <jni.h>
+
+#include <memory>
+
+#include "base/android/scoped_java_ref.h"
+#include "components/ntp_tiles/most_visited_sites.h"
+#include "components/ntp_tiles/ntp_tile.h"
+
+// AuxiliarySearchTopSiteProviderBridge is responsible for providing top sites
+// information for the auxiliary search.
+class AuxiliarySearchTopSiteProviderBridge
+    : public ntp_tiles::MostVisitedSites::Observer {
+ public:
+  explicit AuxiliarySearchTopSiteProviderBridge(
+      std::unique_ptr<ntp_tiles::MostVisitedSites>);
+
+  AuxiliarySearchTopSiteProviderBridge(
+      const AuxiliarySearchTopSiteProviderBridge&) = delete;
+  AuxiliarySearchTopSiteProviderBridge& operator=(
+      const AuxiliarySearchTopSiteProviderBridge&) = delete;
+
+  ~AuxiliarySearchTopSiteProviderBridge() override;
+
+  // Sets an observer and immediately fetches the current most visited sites
+  // suggestions.
+  void SetObserverAndTrigger(JNIEnv* env,
+                             const base::android::JavaRef<jobject>& j_ref_obj);
+
+  // Removes the observer and destroys the bridge.
+  void Destroy(JNIEnv* env);
+
+  // Starts a fetch of the current most visited sites suggestions.
+  void GetMostVisitedSites(JNIEnv* env) const;
+
+ private:
+  FRIEND_TEST_ALL_PREFIXES(AuxiliarySearchTopSiteProviderBridgeTest,
+                           AddAndRemoveObservers);
+
+  void RemoveObserver();
+
+  // ntp_tiles::MostVisitedSites::Observer implementation.
+  void OnURLsAvailable(
+      const std::map<ntp_tiles::SectionType, ntp_tiles::NTPTilesVector>&
+          sections) override;
+  void OnIconMadeAvailable(const GURL& site_url) override;
+
+  jni_zero::ScopedJavaGlobalRef<jobject> observer_;
+
+  std::unique_ptr<ntp_tiles::MostVisitedSites> most_visited_sites_;
+};
+
+#endif  // CHROME_BROWSER_AUXILIARY_SEARCH_AUXILIARY_SEARCH_TOP_SITE_PROVIDER_BRIDGE_H_
diff --git a/chrome/browser/auxiliary_search/auxiliary_search_top_site_provider_bridge_unittest.cc b/chrome/browser/auxiliary_search/auxiliary_search_top_site_provider_bridge_unittest.cc
new file mode 100644
index 0000000..8d10fca7
--- /dev/null
+++ b/chrome/browser/auxiliary_search/auxiliary_search_top_site_provider_bridge_unittest.cc
@@ -0,0 +1,85 @@
+// 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_top_site_provider_bridge.h"
+
+#include <memory>
+
+#include "components/ntp_tiles/icon_cacher.h"
+#include "components/ntp_tiles/most_visited_sites.h"
+#include "components/prefs/testing_pref_service.h"
+#include "components/visited_url_ranking/public/testing/mock_visited_url_ranking_service.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+constexpr size_t kMaxNumMostVisitedSites = 4;
+}  // namespace
+
+class MockMostVisitedSites : public ntp_tiles::MostVisitedSites {
+ public:
+  explicit MockMostVisitedSites(PrefService* prefs)
+      : ntp_tiles::MostVisitedSites(prefs,
+                                    /*identity_manager*/ nullptr,
+                                    /*supervised_user_service*/ nullptr,
+                                    /*top_sites*/ nullptr,
+                                    /*popular_sites*/ nullptr,
+                                    /*custom_links*/ nullptr,
+                                    /*icon_cacher*/ nullptr,
+                                    /*is_default_chrome_app_migrated*/ true,
+                                    /*is_custom_links_mixable*/ false) {}
+
+  MockMostVisitedSites(const MockMostVisitedSites&) = delete;
+  MockMostVisitedSites& operator=(const MockMostVisitedSites&) = delete;
+
+  ~MockMostVisitedSites() override = default;
+
+  MOCK_METHOD2(AddMostVisitedURLsObserver,
+               void(Observer* observer, size_t max_num_sites));
+  MOCK_METHOD1(RemoveMostVisitedURLsObserver, void(Observer* observer));
+};
+
+// Unit tests for AuxiliarySearchTopSiteProviderBridge.
+class AuxiliarySearchTopSiteProviderBridgeTest : public ::testing::Test {
+ public:
+  AuxiliarySearchTopSiteProviderBridgeTest() {
+    auto mock_most_visited_sites =
+        std::make_unique<MockMostVisitedSites>(&testing_prefs_);
+    auxiliary_search_top_site_provider_bridge_ =
+        std::make_unique<AuxiliarySearchTopSiteProviderBridge>(
+            std::move(mock_most_visited_sites));
+  }
+
+  AuxiliarySearchTopSiteProviderBridgeTest(
+      const AuxiliarySearchTopSiteProviderBridgeTest&) = delete;
+  AuxiliarySearchTopSiteProviderBridgeTest& operator=(
+      const AuxiliarySearchTopSiteProviderBridgeTest&) = delete;
+
+ protected:
+  TestingPrefServiceSimple testing_prefs_;
+  std::unique_ptr<AuxiliarySearchTopSiteProviderBridge>
+      auxiliary_search_top_site_provider_bridge_;
+};
+
+TEST_F(AuxiliarySearchTopSiteProviderBridgeTest, AddAndRemoveObservers) {
+  auto j_ref = base::android::JavaRef<jobject>();
+  MockMostVisitedSites* mock_most_visited_sites =
+      static_cast<MockMostVisitedSites*>(
+          auxiliary_search_top_site_provider_bridge_->most_visited_sites_
+              .get());
+
+  // Verifies to start observing most visited sites when the first observer is
+  // added.
+  EXPECT_CALL(*mock_most_visited_sites,
+              AddMostVisitedURLsObserver(testing::_, kMaxNumMostVisitedSites))
+      .Times(1);
+  auxiliary_search_top_site_provider_bridge_->SetObserverAndTrigger(nullptr,
+                                                                    j_ref);
+
+  // Verifies stop observing most visited sites after destroy.
+  EXPECT_CALL(*mock_most_visited_sites,
+              RemoveMostVisitedURLsObserver(testing::_))
+      .Times(1);
+  auxiliary_search_top_site_provider_bridge_->RemoveObserver();
+}
diff --git a/chrome/browser/auxiliary_search/java/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchBridge.java b/chrome/browser/auxiliary_search/java/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchBridge.java
index c9a6af1..46d038c 100644
--- a/chrome/browser/auxiliary_search/java/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchBridge.java
+++ b/chrome/browser/auxiliary_search/java/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchBridge.java
@@ -15,11 +15,9 @@
 import org.chromium.base.task.TaskTraits;
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
-import org.chromium.chrome.browser.auxiliary_search.AuxiliarySearchProvider.Observer;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.url.GURL;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -28,8 +26,6 @@
 @NullMarked
 public class AuxiliarySearchBridge {
     private long mNativeBridge;
-    private @Nullable Observer mObserver;
-    private int mObserverId;
 
     /**
      * Constructs a bridge for the auxiliary search provider.
@@ -95,36 +91,6 @@
     }
 
     /**
-     * Assigns {@link #mObserver}, possibly to null. If non-null {@param observer} is passed,
-     * requires {@link #mObserver} initially null, then fetches the current most visited site
-     * suggestions.
-     *
-     * @param observer The observer to receive suggestions when they are ready.
-     */
-    public void setObserver(@Nullable Observer observer) {
-        if (observer == null) {
-            mObserver = null;
-            AuxiliarySearchBridgeJni.get().removeObserver(mNativeBridge, mObserverId);
-            return;
-        }
-
-        mObserver = observer;
-        mObserverId = AuxiliarySearchBridgeJni.get().setObserverAndTrigger(mNativeBridge, this);
-    }
-
-    /** Starts a fetch of the current most visited sites suggestions. */
-    public void getMostVisitedSites() {
-        if (mNativeBridge == 0) {
-            if (mObserver != null) {
-                mObserver.onSiteSuggestionsAvailable(null);
-            }
-            return;
-        }
-
-        AuxiliarySearchBridgeJni.get().getMostVisitedSites(mNativeBridge);
-    }
-
-    /**
      * Helper to call previously injected callback to pass suggestion results.
      *
      * @param entries The list of fetched entries.
@@ -138,43 +104,6 @@
         callback.onResult(entries);
     }
 
-    @CalledByNative
-    @VisibleForTesting
-    static AuxiliarySearchDataEntry addDataEntry(
-            @AuxiliarySearchEntryType int type,
-            GURL url,
-            String title,
-            long lastActiveTime,
-            int tabId,
-            @Nullable String appId,
-            int visitId,
-            int score) {
-        return new AuxiliarySearchDataEntry(
-                type, url, title, lastActiveTime, tabId, appId, visitId, score);
-    }
-
-    @CalledByNative
-    @VisibleForTesting
-    void onMostVisitedSitesURLsAvailable(
-            @JniType("std::vector") List<AuxiliarySearchDataEntry> entries) {
-        if (mObserver == null) return;
-        mObserver.onSiteSuggestionsAvailable(entries);
-    }
-
-    @CalledByNative
-    void onIconMadeAvailable(@JniType("GURL") GURL siteUrl) {
-        if (mObserver == null) return;
-        mObserver.onIconMadeAvailable(siteUrl);
-    }
-
-    @Nullable Observer getObserverForTesting() {
-        return mObserver;
-    }
-
-    int getObserverIdForTesting() {
-        return mObserverId;
-    }
-
     @NativeMethods
     @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
     public interface Natives {
@@ -186,11 +115,5 @@
         void getNonSensitiveHistoryData(
                 long nativeAuxiliarySearchProvider,
                 Callback<@Nullable List<AuxiliarySearchDataEntry>> callback);
-
-        int setObserverAndTrigger(long nativeAuxiliarySearchProvider, AuxiliarySearchBridge self);
-
-        void removeObserver(long nativeAuxiliarySearchProvider, int id);
-
-        void getMostVisitedSites(long nativeAuxiliarySearchProvider);
     }
 }
diff --git a/chrome/browser/auxiliary_search/java/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchControllerImpl.java b/chrome/browser/auxiliary_search/java/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchControllerImpl.java
index 6bae180..05352b5d 100644
--- a/chrome/browser/auxiliary_search/java/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchControllerImpl.java
+++ b/chrome/browser/auxiliary_search/java/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchControllerImpl.java
@@ -37,9 +37,9 @@
                 AuxiliarySearchConfigManager.ShareTabsWithOsStateListener {
     protected final @AuxiliarySearchHostType int mHostType;
     protected final AuxiliarySearchProvider mAuxiliarySearchProvider;
+    protected final Profile mProfile;
 
     private final Context mContext;
-    private final Profile mProfile;
     private final FaviconHelper mFaviconHelper;
     private final AuxiliarySearchDonor mDonor;
     private final boolean mIsFaviconEnabled;
diff --git a/chrome/browser/auxiliary_search/java/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchMultiDataControllerImpl.java b/chrome/browser/auxiliary_search/java/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchMultiDataControllerImpl.java
index 1a939a5..1dbaf0c 100644
--- a/chrome/browser/auxiliary_search/java/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchMultiDataControllerImpl.java
+++ b/chrome/browser/auxiliary_search/java/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchMultiDataControllerImpl.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.auxiliary_search;
 
+import static org.chromium.build.NullUtil.assumeNonNull;
 import static org.chromium.chrome.browser.flags.ChromeFeatureList.sAndroidAppIntegrationMultiDataSourceHistoryContentTtlHours;
 
 import android.content.Context;
@@ -31,7 +32,7 @@
  */
 @NullMarked
 public class AuxiliarySearchMultiDataControllerImpl extends AuxiliarySearchControllerImpl
-        implements AuxiliarySearchProvider.Observer {
+        implements AuxiliarySearchTopSiteProviderBridge.Observer {
     private final long mHistoryTtlMillis;
 
     // Whether this controller is observing most visited sites.
@@ -46,6 +47,8 @@
     // A set of ActivityLifecycleDispatcher that this controller tracks.
     private Set<ActivityLifecycleDispatcher> mActivityLifecycleDispatcherSet;
 
+    // It is null when the controller doesn't observe top sites changes.
+    private @Nullable AuxiliarySearchTopSiteProviderBridge mAuxiliarySearchTopSiteProviderBridge;
     private @Nullable List<AuxiliarySearchDataEntry> mCurrentSiteSuggestionEntries;
 
     /**
@@ -62,7 +65,8 @@
                         context, profile, /* tabModelSelector= */ null, hostType),
                 AuxiliarySearchDonor.getInstance(),
                 new FaviconHelper(),
-                hostType);
+                hostType,
+                new AuxiliarySearchTopSiteProviderBridge(profile));
     }
 
     @VisibleForTesting
@@ -72,7 +76,8 @@
             AuxiliarySearchProvider auxiliarySearchProvider,
             AuxiliarySearchDonor auxiliarySearchDonor,
             FaviconHelper faviconHelper,
-            @AuxiliarySearchHostType int hostType) {
+            @AuxiliarySearchHostType int hostType,
+            AuxiliarySearchTopSiteProviderBridge auxiliarySearchTopSiteProviderBridge) {
         super(
                 context,
                 profile,
@@ -81,6 +86,7 @@
                 faviconHelper,
                 hostType);
 
+        mAuxiliarySearchTopSiteProviderBridge = auxiliarySearchTopSiteProviderBridge;
         mExpectDonating = true;
         mHistoryTtlMillis =
                 TimeUnit.HOURS.toMillis(
@@ -129,7 +135,11 @@
     public void onDeferredStartup() {
         if (mHostType == AuxiliarySearchHostType.CTA && !mIsObservingTopSites) {
             mIsObservingTopSites = true;
-            mAuxiliarySearchProvider.setObserver(this);
+            if (mAuxiliarySearchTopSiteProviderBridge == null) {
+                mAuxiliarySearchTopSiteProviderBridge =
+                        new AuxiliarySearchTopSiteProviderBridge(mProfile);
+            }
+            mAuxiliarySearchTopSiteProviderBridge.setObserver(this);
         }
     }
 
@@ -142,7 +152,9 @@
 
         if (mActivityLifecycleDispatcherSet.isEmpty()) {
             if (mIsObservingTopSites) {
-                mAuxiliarySearchProvider.setObserver(null);
+                assumeNonNull(mAuxiliarySearchTopSiteProviderBridge);
+                mAuxiliarySearchTopSiteProviderBridge.destroy();
+                mAuxiliarySearchTopSiteProviderBridge = null;
                 mIsObservingTopSites = false;
             }
         }
@@ -227,4 +239,9 @@
     boolean getExpectDonatingForTesting() {
         return mExpectDonating;
     }
+
+    @Nullable
+    AuxiliarySearchTopSiteProviderBridge getAuxiliarySearchTopSiteProviderBridgeForTesting() {
+        return mAuxiliarySearchTopSiteProviderBridge;
+    }
 }
diff --git a/chrome/browser/auxiliary_search/java/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchProvider.java b/chrome/browser/auxiliary_search/java/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchProvider.java
index 392c9d7..a14456f 100644
--- a/chrome/browser/auxiliary_search/java/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchProvider.java
+++ b/chrome/browser/auxiliary_search/java/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchProvider.java
@@ -4,8 +4,6 @@
 
 package org.chromium.chrome.browser.auxiliary_search;
 
-import static org.chromium.build.NullUtil.assertNonNull;
-
 import android.content.Context;
 import android.os.PersistableBundle;
 import android.text.TextUtils;
@@ -56,20 +54,6 @@
         int NUM_ENTRIES = 2;
     }
 
-    /** An interface to handle events in {@link MostVisitedSites}. */
-    interface Observer {
-        /** This is called when the list of most visited URLs is initially available or updated. */
-        void onSiteSuggestionsAvailable(@Nullable List<AuxiliarySearchDataEntry> entries);
-
-        /**
-         * This is called when a previously uncached icon has been fetched. Parameters guaranteed to
-         * be non-null.
-         *
-         * @param siteUrl URL of site with newly-cached icon.
-         */
-        void onIconMadeAvailable(GURL siteUrl);
-    }
-
     /* Only donate the recent 7 days accessed tabs.*/
     @VisibleForTesting static final String TAB_AGE_HOURS_PARAM = "tabs_max_hours";
     @VisibleForTesting static final String TASK_CREATED_TIME = "TaskCreatedTime";
@@ -141,15 +125,6 @@
         mAuxiliarySearchBridge.getNonSensitiveHistoryData(callback);
     }
 
-    /**
-     * Sets an observer and immediately fetches the current most visited sites suggestions.
-     *
-     * @param observer The observer to receive suggestions when they are ready.
-     */
-    public void setObserver(@Nullable Observer observer) {
-        assertNonNull(mAuxiliarySearchBridge).setObserver(observer);
-    }
-
     @VisibleForTesting
     static @Nullable AuxiliarySearchEntry createAuxiliarySearchEntry(
             int id, String title, String url, long timestamp) {
diff --git a/chrome/browser/auxiliary_search/java/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchTopSiteProviderBridge.java b/chrome/browser/auxiliary_search/java/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchTopSiteProviderBridge.java
new file mode 100644
index 0000000..80044680
--- /dev/null
+++ b/chrome/browser/auxiliary_search/java/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchTopSiteProviderBridge.java
@@ -0,0 +1,134 @@
+// 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.auxiliary_search;
+
+import androidx.annotation.VisibleForTesting;
+
+import org.jni_zero.CalledByNative;
+import org.jni_zero.JniType;
+import org.jni_zero.NativeMethods;
+
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.url.GURL;
+
+import java.util.List;
+
+/** Java bridge to provide information of top sites for the auxiliary search. */
+@NullMarked
+public class AuxiliarySearchTopSiteProviderBridge {
+    /** An interface to handle events in {@link MostVisitedSites}. */
+    interface Observer {
+        /** This is called when the list of most visited URLs is initially available or updated. */
+        void onSiteSuggestionsAvailable(@Nullable List<AuxiliarySearchDataEntry> entries);
+
+        /**
+         * This is called when a previously uncached icon has been fetched. Parameters guaranteed to
+         * be non-null.
+         *
+         * @param siteUrl URL of site with newly-cached icon.
+         */
+        void onIconMadeAvailable(GURL siteUrl);
+    }
+
+    private long mNativeBridge;
+    private @Nullable Observer mObserver;
+
+    /**
+     * Constructs a bridge for the auxiliary search top sites provider.
+     *
+     * @param profile The Profile to retrieve the corresponding information.
+     */
+    public AuxiliarySearchTopSiteProviderBridge(Profile profile) {
+        if ((!ChromeFeatureList.sAndroidAppIntegration.isEnabled()
+                        && !ChromeFeatureList.sAndroidAppIntegrationV2.isEnabled())
+                || profile.isOffTheRecord()) {
+            mNativeBridge = 0;
+        } else {
+            mNativeBridge = AuxiliarySearchTopSiteProviderBridgeJni.get().init(this, profile);
+        }
+    }
+
+    /**
+     * Assigns {@link #mObserver}. Requires {@link #mObserver} initially null, then fetches the
+     * current most visited site suggestions.
+     *
+     * @param observer The observer to receive suggestions when they are ready.
+     */
+    public void setObserver(Observer observer) {
+        assert mNativeBridge != 0;
+
+        mObserver = observer;
+        AuxiliarySearchTopSiteProviderBridgeJni.get().setObserverAndTrigger(mNativeBridge, this);
+    }
+
+    /** Destroys the native bridge and remove observer. */
+    public void destroy() {
+        AuxiliarySearchTopSiteProviderBridgeJni.get().destroy(mNativeBridge);
+        mObserver = null;
+        mNativeBridge = 0;
+    }
+
+    /** Starts a fetch of the current most visited sites suggestions. */
+    public void getMostVisitedSites() {
+        if (mNativeBridge == 0) {
+            if (mObserver != null) {
+                mObserver.onSiteSuggestionsAvailable(null);
+            }
+            return;
+        }
+
+        AuxiliarySearchTopSiteProviderBridgeJni.get().getMostVisitedSites(mNativeBridge);
+    }
+
+    @CalledByNative
+    @VisibleForTesting
+    static AuxiliarySearchDataEntry addDataEntry(
+            @AuxiliarySearchEntryType int type,
+            GURL url,
+            String title,
+            long lastActiveTime,
+            int tabId,
+            @Nullable String appId,
+            int visitId,
+            int score) {
+        return new AuxiliarySearchDataEntry(
+                type, url, title, lastActiveTime, tabId, appId, visitId, score);
+    }
+
+    @CalledByNative
+    @VisibleForTesting
+    void onMostVisitedSitesURLsAvailable(
+            @JniType("std::vector") List<AuxiliarySearchDataEntry> entries) {
+        if (mObserver == null) return;
+        mObserver.onSiteSuggestionsAvailable(entries);
+    }
+
+    @CalledByNative
+    void onIconMadeAvailable(@JniType("GURL") GURL siteUrl) {
+        if (mObserver == null) return;
+        mObserver.onIconMadeAvailable(siteUrl);
+    }
+
+    @Nullable Observer getObserverForTesting() {
+        return mObserver;
+    }
+
+    @NativeMethods
+    @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
+    public interface Natives {
+        long init(AuxiliarySearchTopSiteProviderBridge self, @JniType("Profile*") Profile profile);
+
+        void setObserverAndTrigger(
+                long nativeAuxiliarySearchTopSiteProviderBridge,
+                AuxiliarySearchTopSiteProviderBridge self);
+
+        void destroy(long nativeAuxiliarySearchTopSiteProviderBridge);
+
+        void getMostVisitedSites(long nativeAuxiliarySearchTopSiteProviderBridge);
+    }
+}
diff --git a/chrome/browser/auxiliary_search/junit/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchBridgeUnitTest.java b/chrome/browser/auxiliary_search/junit/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchBridgeUnitTest.java
index 27db9ee9..ffff039b 100644
--- a/chrome/browser/auxiliary_search/junit/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchBridgeUnitTest.java
+++ b/chrome/browser/auxiliary_search/junit/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchBridgeUnitTest.java
@@ -6,7 +6,6 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
@@ -23,7 +22,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
-import org.mockito.Mockito;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 import org.robolectric.annotation.Config;
@@ -37,7 +35,6 @@
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.url.GURL;
 import org.chromium.url.JUnitTestGURLs;
 
 import java.util.ArrayList;
@@ -147,54 +144,6 @@
                 .getNonSensitiveHistoryData(eq(NATIVE_BRIDGE), eq(callback));
     }
 
-    @Test
-    @SmallTest
-    public void testSetObserver() {
-        AuxiliarySearchProvider.Observer observer = mock(AuxiliarySearchProvider.Observer.class);
-
-        int id = 100;
-        when(mMockAuxiliarySearchBridgeJni.setObserverAndTrigger(eq(NATIVE_BRIDGE), eq(mBridge)))
-                .thenReturn(id);
-        mBridge.setObserver(observer);
-        assertEquals(observer, mBridge.getObserverForTesting());
-        assertEquals(id, mBridge.getObserverIdForTesting());
-        verify(mMockAuxiliarySearchBridgeJni).setObserverAndTrigger(eq(NATIVE_BRIDGE), eq(mBridge));
-
-        Mockito.reset(mMockAuxiliarySearchBridgeJni);
-        mBridge.setObserver(null);
-        verify(mMockAuxiliarySearchBridgeJni).removeObserver(eq(NATIVE_BRIDGE), eq(id));
-        assertNull(mBridge.getObserverForTesting());
-    }
-
-    @Test
-    @SmallTest
-    public void testGetMostVisitedSites() {
-        mBridge.getMostVisitedSites();
-        verify(mMockAuxiliarySearchBridgeJni).getMostVisitedSites(eq(NATIVE_BRIDGE));
-    }
-
-    @Test
-    @SmallTest
-    public void testOnMostVisitedSitesURLsAvailable() {
-        AuxiliarySearchProvider.Observer observer = mock(AuxiliarySearchProvider.Observer.class);
-        mBridge.setObserver(observer);
-
-        List<AuxiliarySearchDataEntry> entryList = createEntryList();
-        mBridge.onMostVisitedSitesURLsAvailable(entryList);
-        verify(observer).onSiteSuggestionsAvailable(eq(entryList));
-    }
-
-    @Test
-    @SmallTest
-    public void testOnIconMadeAvailable() {
-        AuxiliarySearchProvider.Observer observer = mock(AuxiliarySearchProvider.Observer.class);
-        mBridge.setObserver(observer);
-
-        GURL url = JUnitTestGURLs.URL_1;
-        mBridge.onIconMadeAvailable(url);
-        verify(observer).onIconMadeAvailable(eq(url));
-    }
-
     List<AuxiliarySearchDataEntry> createEntryList() {
         List<AuxiliarySearchDataEntry> entryList = new ArrayList<>();
 
diff --git a/chrome/browser/auxiliary_search/junit/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchControllerFactoryUnitTest.java b/chrome/browser/auxiliary_search/junit/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchControllerFactoryUnitTest.java
index eeba8bf9..4b43ab0 100644
--- a/chrome/browser/auxiliary_search/junit/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchControllerFactoryUnitTest.java
+++ b/chrome/browser/auxiliary_search/junit/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchControllerFactoryUnitTest.java
@@ -45,6 +45,11 @@
     @Mock private Profile mProfile;
     @Mock private TabModelSelector mTabModelSelector;
     @Mock private AuxiliarySearchBridge.Natives mMockAuxiliarySearchBridgeJni;
+
+    @Mock
+    private AuxiliarySearchTopSiteProviderBridge.Natives
+            mMockAuxiliarySearchTopSiteProviderBridgeJni;
+
     @Mock private FaviconHelper.Natives mMockFaviconHelperJni;
     @Mock private AuxiliarySearchHooks mHooks;
 
@@ -56,6 +61,8 @@
         when(mContext.getResources()).thenReturn(mResources);
 
         AuxiliarySearchBridgeJni.setInstanceForTesting(mMockAuxiliarySearchBridgeJni);
+        AuxiliarySearchTopSiteProviderBridgeJni.setInstanceForTesting(
+                mMockAuxiliarySearchTopSiteProviderBridgeJni);
         when(mMockFaviconHelperJni.init()).thenReturn(1L);
         FaviconHelperJni.setInstanceForTesting(mMockFaviconHelperJni);
         AuxiliarySearchDonor.setSkipInitializationForTesting(true);
diff --git a/chrome/browser/auxiliary_search/junit/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchMultiDataControllerImplUnitTest.java b/chrome/browser/auxiliary_search/junit/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchMultiDataControllerImplUnitTest.java
index f8c098c..ed54b1a 100644
--- a/chrome/browser/auxiliary_search/junit/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchMultiDataControllerImplUnitTest.java
+++ b/chrome/browser/auxiliary_search/junit/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchMultiDataControllerImplUnitTest.java
@@ -66,6 +66,7 @@
     @Mock private FaviconHelper mFaviconHelper;
     @Mock private ActivityLifecycleDispatcher mActivityLifecycleDispatcher;
     @Mock private AuxiliarySearchHooks mHooks;
+    @Mock private AuxiliarySearchTopSiteProviderBridge mAuxiliarySearchTopSiteProviderBridge;
 
     @Captor
     private ArgumentCaptor<Callback<List<AuxiliarySearchDataEntry>>> mEntryReadyCallbackCaptor;
@@ -216,12 +217,13 @@
     @Test
     public void testOnDeferredStartup() {
         mAuxiliarySearchMultiDataControllerImpl.onDeferredStartup();
-        verify(mAuxiliarySearchProvider).setObserver(eq(mAuxiliarySearchMultiDataControllerImpl));
-
-        Mockito.reset(mAuxiliarySearchProvider);
-        mAuxiliarySearchMultiDataControllerImpl.onDeferredStartup();
-        verify(mAuxiliarySearchProvider, never())
+        verify(mAuxiliarySearchTopSiteProviderBridge)
                 .setObserver(eq(mAuxiliarySearchMultiDataControllerImpl));
+
+        Mockito.reset(mAuxiliarySearchTopSiteProviderBridge);
+        mAuxiliarySearchMultiDataControllerImpl.onDeferredStartup();
+        verify(mAuxiliarySearchTopSiteProviderBridge, never())
+                .setObserver(any(AuxiliarySearchTopSiteProviderBridge.Observer.class));
     }
 
     @Test
@@ -236,11 +238,19 @@
     @Test
     public void testOnDestroy() {
         mAuxiliarySearchMultiDataControllerImpl.onDeferredStartup();
-        verify(mAuxiliarySearchProvider).setObserver(eq(mAuxiliarySearchMultiDataControllerImpl));
+        assertEquals(
+                mAuxiliarySearchTopSiteProviderBridge,
+                mAuxiliarySearchMultiDataControllerImpl
+                        .getAuxiliarySearchTopSiteProviderBridgeForTesting());
+        verify(mAuxiliarySearchTopSiteProviderBridge)
+                .setObserver(eq(mAuxiliarySearchMultiDataControllerImpl));
 
         mAuxiliarySearchMultiDataControllerImpl.destroy(mActivityLifecycleDispatcher);
         verify(mActivityLifecycleDispatcher).unregister(mAuxiliarySearchMultiDataControllerImpl);
-        verify(mAuxiliarySearchProvider).setObserver(eq(null));
+        verify(mAuxiliarySearchTopSiteProviderBridge).destroy();
+        assertNull(
+                mAuxiliarySearchMultiDataControllerImpl
+                        .getAuxiliarySearchTopSiteProviderBridgeForTesting());
     }
 
     @Test
@@ -249,17 +259,18 @@
         mAuxiliarySearchMultiDataControllerImpl.register(mActivityLifecycleDispatcher);
         mAuxiliarySearchMultiDataControllerImpl.register(dispatcher);
         mAuxiliarySearchMultiDataControllerImpl.onDeferredStartup();
-        verify(mAuxiliarySearchProvider).setObserver(eq(mAuxiliarySearchMultiDataControllerImpl));
+        verify(mAuxiliarySearchTopSiteProviderBridge)
+                .setObserver(eq(mAuxiliarySearchMultiDataControllerImpl));
 
-        // Verifies that the controller doesn't stop observing most visited sites when there is
-        // reference to it.
+        // Verifies that the controller doesn't destroy the native bridge when there is reference
+        // to it.
         mAuxiliarySearchMultiDataControllerImpl.destroy(dispatcher);
-        verify(mAuxiliarySearchProvider, never()).setObserver(eq(null));
+        verify(mAuxiliarySearchTopSiteProviderBridge, never()).destroy();
 
-        // Verifies that the controller will stop observing most visited sites when there isn't any
+        // Verifies that the controller will destroy the native bridge when there isn't any
         // reference to it.
         mAuxiliarySearchMultiDataControllerImpl.destroy(mActivityLifecycleDispatcher);
-        verify(mAuxiliarySearchProvider).setObserver(eq(null));
+        verify(mAuxiliarySearchTopSiteProviderBridge).destroy();
     }
 
     private void createController() {
@@ -270,7 +281,8 @@
                         mAuxiliarySearchProvider,
                         mAuxiliarySearchDonor,
                         mFaviconHelper,
-                        AuxiliarySearchController.AuxiliarySearchHostType.CTA);
+                        AuxiliarySearchController.AuxiliarySearchHostType.CTA,
+                        mAuxiliarySearchTopSiteProviderBridge);
         assertTrue(mAuxiliarySearchMultiDataControllerImpl.getExpectDonatingForTesting());
         mAuxiliarySearchMultiDataControllerImpl.register(mActivityLifecycleDispatcher);
     }
diff --git a/chrome/browser/auxiliary_search/junit/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchProviderUnitTest.java b/chrome/browser/auxiliary_search/junit/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchProviderUnitTest.java
index 2a3d157..3b5c74b 100644
--- a/chrome/browser/auxiliary_search/junit/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchProviderUnitTest.java
+++ b/chrome/browser/auxiliary_search/junit/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchProviderUnitTest.java
@@ -10,11 +10,7 @@
 import static org.junit.Assert.assertNotNull;
 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.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
@@ -294,16 +290,6 @@
 
     @Test
     @SmallTest
-    public void testSetObserver() {
-        AuxiliarySearchProvider.Observer observer = mock(AuxiliarySearchProvider.Observer.class);
-        mAuxiliarySearchProvider.setObserver(observer);
-
-        verify(mMockAuxiliarySearchBridgeJni)
-                .setObserverAndTrigger(eq(FAKE_NATIVE_PROVIDER), any(AuxiliarySearchBridge.class));
-    }
-
-    @Test
-    @SmallTest
     public void testCreationViaCTABackgroundTask() {
         mAuxiliarySearchProvider =
                 new AuxiliarySearchProvider(
diff --git a/chrome/browser/auxiliary_search/junit/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchTopSiteProviderBridgeUnitTest.java b/chrome/browser/auxiliary_search/junit/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchTopSiteProviderBridgeUnitTest.java
new file mode 100644
index 0000000..d5658b2
--- /dev/null
+++ b/chrome/browser/auxiliary_search/junit/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchTopSiteProviderBridgeUnitTest.java
@@ -0,0 +1,120 @@
+// 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.auxiliary_search;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.annotation.Config;
+
+import org.chromium.base.TimeUtils;
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.base.test.util.Features;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.url.GURL;
+import org.chromium.url.JUnitTestGURLs;
+
+import java.util.List;
+
+/** Unit tests for {@link AuxiliarySearchTopSiteProviderBridge} */
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
+@Features.EnableFeatures({ChromeFeatureList.ANDROID_APP_INTEGRATION})
+public class AuxiliarySearchTopSiteProviderBridgeUnitTest {
+    @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+    // Arbitrary non-0 value.
+    private static final long NATIVE_BRIDGE = 10L;
+
+    @Mock
+    private AuxiliarySearchTopSiteProviderBridge.Natives
+            mMockAuxiliarySearchTopSiteProviderBridgeJni;
+
+    @Mock private Profile mProfile;
+
+    private AuxiliarySearchTopSiteProviderBridge mBridge;
+
+    @Before
+    public void setUp() {
+        when(mProfile.isOffTheRecord()).thenReturn(false);
+        AuxiliarySearchTopSiteProviderBridgeJni.setInstanceForTesting(
+                mMockAuxiliarySearchTopSiteProviderBridgeJni);
+        when(mMockAuxiliarySearchTopSiteProviderBridgeJni.init(any(), eq(mProfile)))
+                .thenReturn(NATIVE_BRIDGE);
+
+        mBridge = new AuxiliarySearchTopSiteProviderBridge(mProfile);
+        assertNotNull(mBridge);
+    }
+
+    @Test
+    public void testSetObserver() {
+        AuxiliarySearchTopSiteProviderBridge.Observer observer =
+                mock(AuxiliarySearchTopSiteProviderBridge.Observer.class);
+
+        mBridge.setObserver(observer);
+        assertEquals(observer, mBridge.getObserverForTesting());
+        verify(mMockAuxiliarySearchTopSiteProviderBridgeJni)
+                .setObserverAndTrigger(eq(NATIVE_BRIDGE), eq(mBridge));
+    }
+
+    @Test
+    public void testDestroy() {
+        AuxiliarySearchTopSiteProviderBridge.Observer observer =
+                mock(AuxiliarySearchTopSiteProviderBridge.Observer.class);
+
+        mBridge.setObserver(observer);
+        assertNotNull(mBridge.getObserverForTesting());
+
+        Mockito.reset(mMockAuxiliarySearchTopSiteProviderBridgeJni);
+        mBridge.destroy();
+        verify(mMockAuxiliarySearchTopSiteProviderBridgeJni).destroy(eq(NATIVE_BRIDGE));
+        assertNull(mBridge.getObserverForTesting());
+    }
+
+    @Test
+    public void testGetMostVisitedSites() {
+        mBridge.getMostVisitedSites();
+        verify(mMockAuxiliarySearchTopSiteProviderBridgeJni).getMostVisitedSites(eq(NATIVE_BRIDGE));
+    }
+
+    @Test
+    public void testOnMostVisitedSitesURLsAvailable() {
+        AuxiliarySearchTopSiteProviderBridge.Observer observer =
+                mock(AuxiliarySearchTopSiteProviderBridge.Observer.class);
+        mBridge.setObserver(observer);
+
+        List<AuxiliarySearchDataEntry> entryList =
+                AuxiliarySearchTestHelper.createAuxiliarySearchDataEntries_TopSite(
+                        TimeUtils.uptimeMillis());
+        mBridge.onMostVisitedSitesURLsAvailable(entryList);
+        verify(observer).onSiteSuggestionsAvailable(eq(entryList));
+    }
+
+    @Test
+    public void testOnIconMadeAvailable() {
+        AuxiliarySearchTopSiteProviderBridge.Observer observer =
+                mock(AuxiliarySearchTopSiteProviderBridge.Observer.class);
+        mBridge.setObserver(observer);
+
+        GURL url = JUnitTestGURLs.URL_1;
+        mBridge.onIconMadeAvailable(url);
+        verify(observer).onIconMadeAvailable(eq(url));
+    }
+}
diff --git a/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/bar/BookmarkBarButton.java b/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/bar/BookmarkBarButton.java
index 44282d64..61d85795 100644
--- a/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/bar/BookmarkBarButton.java
+++ b/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/bar/BookmarkBarButton.java
@@ -25,8 +25,7 @@
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
 import org.chromium.chrome.browser.bookmarks.R;
-
-import java.util.function.IntConsumer;
+import org.chromium.ui.util.ClickWithMetaStateCallback;
 
 /**
  * View for a button in the bookmark bar which provides users with bookmark access from top chrome.
@@ -78,8 +77,9 @@
      *
      * @param callback the callback to notify.
      */
-    public void setClickCallback(@Nullable IntConsumer callback) {
-        setOnClickListener(callback != null ? (v) -> callback.accept(mLastEventMetaState) : null);
+    public void setClickCallback(@Nullable ClickWithMetaStateCallback callback) {
+        setOnClickListener(
+                callback != null ? (v) -> callback.onClickWithMeta(mLastEventMetaState) : null);
     }
 
     /**
diff --git a/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/bar/BookmarkBarButtonProperties.java b/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/bar/BookmarkBarButtonProperties.java
index d4254ee..c8aa61c 100644
--- a/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/bar/BookmarkBarButtonProperties.java
+++ b/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/bar/BookmarkBarButtonProperties.java
@@ -11,8 +11,7 @@
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel.WritableIntPropertyKey;
 import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey;
-
-import java.util.function.IntConsumer;
+import org.chromium.ui.util.ClickWithMetaStateCallback;
 
 /**
  * Properties for a button in the bookmark bar which provides users with bookmark access from top
@@ -25,7 +24,7 @@
      * The callback to notify of bookmark bar button click events. The callback is provided the meta
      * state of the most recent key/touch event.
      */
-    public static final WritableObjectPropertyKey<IntConsumer> CLICK_CALLBACK =
+    public static final WritableObjectPropertyKey<ClickWithMetaStateCallback> CLICK_CALLBACK =
             new WritableObjectPropertyKey<>();
 
     /** The supplier for the icon to render in the bookmark bar button. */
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 17918fb7..0a2664d 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -5395,8 +5395,7 @@
   }
 }
 
-std::vector<std::unique_ptr<content::NavigationThrottle>>
-ChromeContentBrowserClient::CreateThrottlesForNavigation(
+void ChromeContentBrowserClient::CreateThrottlesForNavigation(
     content::NavigationThrottleRegistry& registry) {
   // MetricsNavigationThrottle requires that it runs before NavigationThrottles
   // that may delay or cancel navigations, so only NavigationThrottles that
@@ -5767,8 +5766,6 @@
   registry.MaybeAddThrottle(
       web_app::IsolatedWebAppThrottle::MaybeCreateThrottleFor(&handle));
 #endif  // !BUILDFLAG(IS_ANDROID)
-
-  return {};
 }
 
 std::vector<std::unique_ptr<content::CommitDeferringCondition>>
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index 48fd2e3..4d28838 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -635,8 +635,7 @@
                                content::WebContents* web_contents) override;
   void RemovePresentationObserver(content::PresentationObserver* observer,
                                   content::WebContents* web_contents) override;
-  std::vector<std::unique_ptr<content::NavigationThrottle>>
-  CreateThrottlesForNavigation(
+  void CreateThrottlesForNavigation(
       content::NavigationThrottleRegistry& registry) override;
   std::vector<std::unique_ptr<content::CommitDeferringCondition>>
   CreateCommitDeferringConditionsForNavigation(
diff --git a/chrome/browser/chromeos/app_mode/kiosk_browser_session_unittest.cc b/chrome/browser/chromeos/app_mode/kiosk_browser_session_unittest.cc
index d30694e8..bf3a198 100644
--- a/chrome/browser/chromeos/app_mode/kiosk_browser_session_unittest.cc
+++ b/chrome/browser/chromeos/app_mode/kiosk_browser_session_unittest.cc
@@ -55,9 +55,9 @@
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "chromeos/ash/experiences/system_web_apps/types/system_web_app_delegate_map.h"
 #include "chromeos/dbus/power/fake_power_manager_client.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "components/user_manager/scoped_user_manager.h"
 #include "components/user_manager/user.h"
 #include "content/public/common/webplugininfo.h"
diff --git a/chrome/browser/collaboration/BUILD.gn b/chrome/browser/collaboration/BUILD.gn
index 3ddeefe6..c9e3511 100644
--- a/chrome/browser/collaboration/BUILD.gn
+++ b/chrome/browser/collaboration/BUILD.gn
@@ -23,6 +23,8 @@
       "//chrome/browser/settings:factory_java",
       "//chrome/browser/signin/services/android:java",
       "//chrome/browser/tab_group_sync:factory_java",
+      "//chrome/browser/tab_ui/android:java",
+      "//chrome/browser/tabmodel:java",
       "//chrome/browser/ui/android/signin:java",
       "//chrome/browser/ui/android/strings:ui_strings_grd",
       "//components/browser_ui/settings/android:java",
diff --git a/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationControllerDelegateImpl.java b/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationControllerDelegateImpl.java
index 9e6d902..6c536e8 100644
--- a/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationControllerDelegateImpl.java
+++ b/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationControllerDelegateImpl.java
@@ -8,6 +8,7 @@
 
 import android.app.Activity;
 import android.content.Intent;
+import android.text.TextUtils;
 
 import org.jni_zero.CalledByNative;
 import org.jni_zero.JNINamespace;
@@ -22,6 +23,9 @@
 import org.chromium.chrome.browser.settings.SettingsNavigationFactory;
 import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
 import org.chromium.chrome.browser.signin.services.SigninManager;
+import org.chromium.chrome.browser.tab_ui.ActionConfirmationManager;
+import org.chromium.chrome.browser.tab_ui.ActionConfirmationManager.MaybeBlockingResult;
+import org.chromium.chrome.browser.tabmodel.TabGroupTitleUtils;
 import org.chromium.chrome.browser.ui.signin.BottomSheetSigninAndHistorySyncConfig;
 import org.chromium.chrome.browser.ui.signin.BottomSheetSigninAndHistorySyncConfig.NoAccountSigninMode;
 import org.chromium.chrome.browser.ui.signin.BottomSheetSigninAndHistorySyncConfig.WithAccountSigninMode;
@@ -30,6 +34,7 @@
 import org.chromium.chrome.browser.ui.signin.account_picker.AccountPickerBottomSheetStrings;
 import org.chromium.chrome.browser.ui.signin.history_sync.HistorySyncConfig;
 import org.chromium.components.browser_ui.settings.SettingsNavigation;
+import org.chromium.components.browser_ui.widget.ActionConfirmationResult;
 import org.chromium.components.browser_ui.widget.loading.LoadingFullscreenCoordinator;
 import org.chromium.components.collaboration.CollaborationControllerDelegate;
 import org.chromium.components.collaboration.FlowType;
@@ -581,6 +586,32 @@
                 };
     }
 
+    private Callback<MaybeBlockingResult> getActionConfirmationCallback(long resultCallback) {
+        return (MaybeBlockingResult maybeBlockingResult) -> {
+            boolean accept =
+                    maybeBlockingResult.result != ActionConfirmationResult.CONFIRMATION_NEGATIVE;
+
+            if (maybeBlockingResult.finishBlocking != null) {
+                mCloseScreenRunnable = maybeBlockingResult.finishBlocking;
+            }
+
+            if (accept) {
+                CollaborationControllerDelegateImplJni.get()
+                        .runResultCallback(Outcome.SUCCESS, resultCallback);
+            } else {
+                CollaborationControllerDelegateImplJni.get()
+                        .runResultCallback(Outcome.CANCEL, resultCallback);
+            }
+        };
+    }
+
+    private ActionConfirmationManager getActionConfirmationManager() {
+        return new ActionConfirmationManager(
+                assumeNonNull(mDataSharingTabManager.getProfile()),
+                mActivity,
+                assumeNonNull(mDataSharingTabManager.getWindowAndroid().getModalDialogManager()));
+    }
+
     /**
      * Show the leave dialog screen.
      *
@@ -590,8 +621,13 @@
      */
     @CalledByNative
     void showLeaveDialog(String syncId, LocalTabGroupId localId, long resultCallback) {
-        CollaborationControllerDelegateImplJni.get()
-                .runResultCallback(Outcome.FAILURE, resultCallback);
+        SavedTabGroup existingGroup =
+                mDataSharingTabManager.getSavedTabGroupForEitherId(syncId, localId);
+
+        getActionConfirmationManager()
+                .processLeaveGroupAttempt(
+                        getSavedTabGroupTitle(existingGroup),
+                        getActionConfirmationCallback(resultCallback));
     }
 
     /**
@@ -603,8 +639,19 @@
      */
     @CalledByNative
     void showDeleteDialog(String syncId, LocalTabGroupId localId, long resultCallback) {
-        CollaborationControllerDelegateImplJni.get()
-                .runResultCallback(Outcome.FAILURE, resultCallback);
+        SavedTabGroup existingGroup =
+                mDataSharingTabManager.getSavedTabGroupForEitherId(syncId, localId);
+
+        getActionConfirmationManager()
+                .processDeleteSharedGroupAttempt(
+                        getSavedTabGroupTitle(existingGroup),
+                        getActionConfirmationCallback(resultCallback));
+    }
+
+    private String getSavedTabGroupTitle(SavedTabGroup tabGroup) {
+        return TextUtils.isEmpty(tabGroup.title)
+                ? TabGroupTitleUtils.getDefaultTitle(mActivity, tabGroup.savedTabs.size())
+                : tabGroup.title;
     }
 
     /**
diff --git a/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationControllerDelegateImplUnitTest.java b/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationControllerDelegateImplUnitTest.java
index d868501..e4e3059 100644
--- a/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationControllerDelegateImplUnitTest.java
+++ b/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationControllerDelegateImplUnitTest.java
@@ -479,4 +479,56 @@
         verify(mCollaborationControllerDelegateImplNativeMock)
                 .runResultCallback(eq(Outcome.SUCCESS), eq(resultCallback));
     }
+
+    @Test
+    public void testLeaveSharedTabGroup() {
+        createDelegate(FlowType.LEAVE_OR_DELETE);
+        long resultCallback = 1;
+        LocalTabGroupId localId = new LocalTabGroupId(new Token(1L, 2L));
+        String title = "title";
+
+        SavedTabGroup savedGroup = new SavedTabGroup();
+        savedGroup.localId = localId;
+        savedGroup.title = title;
+        doReturn(savedGroup)
+                .when(mDataSharingTabManager)
+                .getSavedTabGroupForEitherId(null, localId);
+
+        mCollaborationControllerDelegateImpl.showLeaveDialog(null, localId, resultCallback);
+        ArgumentCaptor<PropertyModel> propertyModelCaptor =
+                ArgumentCaptor.forClass(PropertyModel.class);
+        verify(mModalDialogManager).showDialog(propertyModelCaptor.capture(), anyInt());
+
+        ModalDialogProperties.Controller controller =
+                propertyModelCaptor.getValue().get(ModalDialogProperties.CONTROLLER);
+        controller.onClick(propertyModelCaptor.getValue(), ButtonType.NEGATIVE);
+        verify(mCollaborationControllerDelegateImplNativeMock)
+                .runResultCallback(eq(Outcome.CANCEL), eq(resultCallback));
+    }
+
+    @Test
+    public void testDeleteSharedTabGroup() {
+        createDelegate(FlowType.LEAVE_OR_DELETE);
+        long resultCallback = 1;
+        LocalTabGroupId localId = new LocalTabGroupId(new Token(1L, 2L));
+        String title = "title";
+
+        SavedTabGroup savedGroup = new SavedTabGroup();
+        savedGroup.localId = localId;
+        savedGroup.title = title;
+        doReturn(savedGroup)
+                .when(mDataSharingTabManager)
+                .getSavedTabGroupForEitherId(null, localId);
+
+        mCollaborationControllerDelegateImpl.showLeaveDialog(null, localId, resultCallback);
+        ArgumentCaptor<PropertyModel> propertyModelCaptor =
+                ArgumentCaptor.forClass(PropertyModel.class);
+        verify(mModalDialogManager).showDialog(propertyModelCaptor.capture(), anyInt());
+
+        ModalDialogProperties.Controller controller =
+                propertyModelCaptor.getValue().get(ModalDialogProperties.CONTROLLER);
+        controller.onClick(propertyModelCaptor.getValue(), ButtonType.POSITIVE);
+        verify(mCollaborationControllerDelegateImplNativeMock)
+                .runResultCallback(eq(Outcome.SUCCESS), eq(resultCallback));
+    }
 }
diff --git a/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationServiceFactoryTest.java b/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationServiceFactoryTest.java
index 730238d..135789b 100644
--- a/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationServiceFactoryTest.java
+++ b/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationServiceFactoryTest.java
@@ -28,6 +28,7 @@
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
 import org.chromium.components.collaboration.CollaborationControllerDelegate;
 import org.chromium.components.collaboration.CollaborationService;
+import org.chromium.components.collaboration.CollaborationServiceLeaveOrDeleteEntryPoint;
 import org.chromium.components.collaboration.CollaborationServiceShareOrManageEntryPoint;
 import org.chromium.components.collaboration.CollaborationStatus;
 import org.chromium.components.collaboration.ServiceStatus;
@@ -67,6 +68,12 @@
                             @CollaborationServiceShareOrManageEntryPoint int entry) {}
 
                     @Override
+                    public void startLeaveOrDeleteFlow(
+                            CollaborationControllerDelegate delegate,
+                            String syncId,
+                            @CollaborationServiceLeaveOrDeleteEntryPoint int entry) {}
+
+                    @Override
                     public ServiceStatus getServiceStatus() {
                         return new ServiceStatus(
                                 SigninStatus.NOT_SIGNED_IN,
diff --git a/chrome/browser/data_sharing/desktop/data_sharing_ui_delegate_desktop.cc b/chrome/browser/data_sharing/desktop/data_sharing_ui_delegate_desktop.cc
index e282615..5638dd2 100644
--- a/chrome/browser/data_sharing/desktop/data_sharing_ui_delegate_desktop.cc
+++ b/chrome/browser/data_sharing/desktop/data_sharing_ui_delegate_desktop.cc
@@ -11,7 +11,6 @@
 #include "chrome/browser/ui/browser_window/public/browser_window_features.h"
 #include "chrome/browser/ui/views/data_sharing/collaboration_controller_delegate_desktop.h"
 #include "chrome/browser/ui/views/data_sharing/data_sharing_bubble_controller.h"
-#include "chrome/browser/ui/views/data_sharing/data_sharing_open_group_helper.h"
 #include "components/collaboration/public/collaboration_service.h"
 #include "components/data_sharing/public/data_sharing_service.h"
 #include "url/gurl.h"
diff --git a/chrome/browser/extensions/api/settings_private/prefs_util.cc b/chrome/browser/extensions/api/settings_private/prefs_util.cc
index 97bb4be6..ec4b6904 100644
--- a/chrome/browser/extensions/api/settings_private/prefs_util.cc
+++ b/chrome/browser/extensions/api/settings_private/prefs_util.cc
@@ -1297,6 +1297,8 @@
   if (glic::GlicEnabling::IsEnabledByFlags()) {
     (*s_allowlist)[glic::prefs::kGlicLauncherEnabled] =
         settings_api::PrefType::kBoolean;
+    (*s_allowlist)[glic::prefs::kGlicClosedCaptioningEnabled] =
+        settings_api::PrefType::kBoolean;
     (*s_allowlist)[glic::prefs::kGlicGeolocationEnabled] =
         settings_api::PrefType::kBoolean;
     (*s_allowlist)[glic::prefs::kGlicMicrophoneEnabled] =
diff --git a/chrome/browser/extensions/api/storage/managed_value_store_cache_unittest.cc b/chrome/browser/extensions/api/storage/managed_value_store_cache_unittest.cc
index cdcc21d..cb1923d 100644
--- a/chrome/browser/extensions/api/storage/managed_value_store_cache_unittest.cc
+++ b/chrome/browser/extensions/api/storage/managed_value_store_cache_unittest.cc
@@ -18,12 +18,15 @@
 #include "content/public/test/browser_task_environment.h"
 #include "content/public/test/test_utils.h"
 #include "extensions/browser/api/storage/backend_task_runner.h"
+#include "extensions/buildflags/buildflags.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_builder.h"
 #include "extensions/common/extension_id.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
+
 namespace extensions {
 namespace {
 
diff --git a/chrome/browser/extensions/api/storage/settings_apitest.cc b/chrome/browser/extensions/api/storage/settings_apitest.cc
index 329ade5..9a9f3f3 100644
--- a/chrome/browser/extensions/api/storage/settings_apitest.cc
+++ b/chrome/browser/extensions/api/storage/settings_apitest.cc
@@ -15,7 +15,6 @@
 #include "chrome/browser/extensions/extension_apitest.h"
 #include "chrome/browser/policy/schema_registry_service.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/browser.h"
 #include "components/policy/core/browser/browser_policy_connector.h"
 #include "components/policy/core/common/mock_configuration_policy_provider.h"
 #include "components/policy/core/common/policy_bundle.h"
@@ -37,6 +36,7 @@
 #include "extensions/browser/api/storage/storage_area_namespace.h"
 #include "extensions/browser/api/storage/storage_frontend.h"
 #include "extensions/browser/extension_system.h"
+#include "extensions/buildflags/buildflags.h"
 #include "extensions/common/extension_id.h"
 #include "extensions/common/features/feature_channel.h"
 #include "extensions/test/extension_test_message_listener.h"
@@ -44,6 +44,8 @@
 #include "extensions/test/test_extension_dir.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
+static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
+
 namespace extensions {
 
 using testing::NiceMock;
@@ -290,9 +292,9 @@
   // regular and incognito mode.
   ResultCatcher catcher;
   ResultCatcher catcher_incognito;
-  catcher.RestrictToBrowserContext(browser()->profile());
+  catcher.RestrictToBrowserContext(profile());
   catcher_incognito.RestrictToBrowserContext(
-      browser()->profile()->GetPrimaryOTRProfile(/*create_if_needed=*/true));
+      profile()->GetPrimaryOTRProfile(/*create_if_needed=*/true));
 
   // Sync, local and managed follow the same storage flow (RunWithStorage),
   // whereas session follows a separate flow (RunWithSession). For the purpose
@@ -330,9 +332,9 @@
   // regular and incognito mode.
   ResultCatcher catcher;
   ResultCatcher catcher_incognito;
-  catcher.RestrictToBrowserContext(browser()->profile());
+  catcher.RestrictToBrowserContext(profile());
   catcher_incognito.RestrictToBrowserContext(
-      browser()->profile()->GetPrimaryOTRProfile(/*create_if_needed=*/true));
+      profile()->GetPrimaryOTRProfile(/*create_if_needed=*/true));
 
   StorageAreaNamespace storage_areas[2] = {StorageAreaNamespace::kSync,
                                            StorageAreaNamespace::kSession};
@@ -378,9 +380,9 @@
   // regular and incognito mode.
   ResultCatcher catcher;
   ResultCatcher catcher_incognito;
-  catcher.RestrictToBrowserContext(browser()->profile());
+  catcher.RestrictToBrowserContext(profile());
   catcher_incognito.RestrictToBrowserContext(
-      browser()->profile()->GetPrimaryOTRProfile(/*create_if_needed=*/true));
+      profile()->GetPrimaryOTRProfile(/*create_if_needed=*/true));
 
   LoadAndReplyWhenSatisfied(StorageAreaNamespace::kSync,
                             "assertNoNotifications", "assertNoNotifications",
@@ -490,9 +492,9 @@
   // We need 2 ResultCatchers because we'll be running the same test in both
   // regular and incognito mode.
   ResultCatcher catcher, catcher_incognito;
-  catcher.RestrictToBrowserContext(browser()->profile());
+  catcher.RestrictToBrowserContext(profile());
   catcher_incognito.RestrictToBrowserContext(
-      browser()->profile()->GetPrimaryOTRProfile(/*create_if_needed=*/true));
+      profile()->GetPrimaryOTRProfile(/*create_if_needed=*/true));
 
   const Extension* extension = LoadAndReplyWhenSatisfied(
       StorageAreaNamespace::kSync, "assertNoNotifications",
@@ -535,9 +537,9 @@
   // We need 2 ResultCatchers because we'll be running the same test in both
   // regular and incognito mode.
   ResultCatcher catcher, catcher_incognito;
-  catcher.RestrictToBrowserContext(browser()->profile());
+  catcher.RestrictToBrowserContext(profile());
   catcher_incognito.RestrictToBrowserContext(
-      browser()->profile()->GetPrimaryOTRProfile(/*create_if_needed=*/true));
+      profile()->GetPrimaryOTRProfile(/*create_if_needed=*/true));
 
   const Extension* extension = LoadAndReplyWhenSatisfied(
       StorageAreaNamespace::kLocal, "assertNoNotifications",
@@ -571,7 +573,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, IsStorageEnabled) {
-  StorageFrontend* frontend = StorageFrontend::Get(browser()->profile());
+  StorageFrontend* frontend = StorageFrontend::Get(profile());
   EXPECT_TRUE(frontend->IsStorageEnabled(settings_namespace::LOCAL));
   EXPECT_TRUE(frontend->IsStorageEnabled(settings_namespace::SYNC));
 
@@ -602,9 +604,12 @@
   ResultCatcher events_result_catcher_;
 };
 
+// Desktop Android supports only service worker, not persistent background.
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 INSTANTIATE_TEST_SUITE_P(PersistentBackground,
                          ExtensionSettingsManagedStorageApiTest,
                          ::testing::Values(ContextType::kPersistentBackground));
+#endif
 
 INSTANTIATE_TEST_SUITE_P(ServiceWorker,
                          ExtensionSettingsManagedStorageApiTest,
@@ -613,8 +618,7 @@
 IN_PROC_BROWSER_TEST_P(ExtensionSettingsManagedStorageApiTest,
                        ExtensionsSchemas) {
   // Verifies that the Schemas for the extensions domain are created on startup.
-  Profile* profile = browser()->profile();
-  ExtensionSystem* extension_system = ExtensionSystem::Get(profile);
+  ExtensionSystem* extension_system = ExtensionSystem::Get(profile());
   if (!extension_system->ready().is_signaled()) {
     // Wait until the extension system is ready.
     base::RunLoop run_loop;
@@ -628,7 +632,7 @@
   message_.clear();
 
   policy::SchemaRegistry* registry =
-      profile->GetPolicySchemaRegistryService()->registry();
+      profile()->GetPolicySchemaRegistryService()->registry();
   ASSERT_TRUE(registry);
   EXPECT_FALSE(registry->schema_map()->GetSchema(policy::PolicyNamespace(
       policy::POLICY_DOMAIN_EXTENSIONS, kManagedStorageExtensionId)));
@@ -721,6 +725,10 @@
   ASSERT_TRUE(RunExtensionTest("settings/managed_storage")) << message_;
 }
 
+// TODO(crbug.com/40200835): PRE_ test does not work on android_browsertest,
+// therefore, disabled the following tests until the PRE_ test issue is
+// resolved.
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 // TODO(crbug.com/40789870): This test should be rewritten. See the bug for more
 // details.
 IN_PROC_BROWSER_TEST_P(ExtensionSettingsManagedStorageApiTest,
@@ -775,11 +783,12 @@
   EXPECT_TRUE(events_result_catcher_.GetNextResult())
       << events_result_catcher_.message();
 }
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
 IN_PROC_BROWSER_TEST_P(ExtensionSettingsManagedStorageApiTest,
                        ManagedStorageDisabled) {
   // Disable the 'managed' namespace.
-  StorageFrontend* frontend = StorageFrontend::Get(browser()->profile());
+  StorageFrontend* frontend = StorageFrontend::Get(profile());
   frontend->DisableStorageForTesting(settings_namespace::MANAGED);
   EXPECT_FALSE(frontend->IsStorageEnabled(settings_namespace::MANAGED));
   // Now run the extension.
diff --git a/chrome/browser/extensions/api/storage/settings_sync_unittest.cc b/chrome/browser/extensions/api/storage/settings_sync_unittest.cc
index 765c532..6218df9 100644
--- a/chrome/browser/extensions/api/storage/settings_sync_unittest.cc
+++ b/chrome/browser/extensions/api/storage/settings_sync_unittest.cc
@@ -34,10 +34,13 @@
 #include "extensions/browser/event_router_factory.h"
 #include "extensions/browser/extension_system.h"
 #include "extensions/browser/mock_extension_system.h"
+#include "extensions/buildflags/buildflags.h"
 #include "extensions/common/extension_id.h"
 #include "extensions/common/manifest.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
+
 using value_store::ValueStore;
 
 namespace extensions {
diff --git a/chrome/browser/extensions/desktop_android/desktop_android_extensions_browser_client.cc b/chrome/browser/extensions/desktop_android/desktop_android_extensions_browser_client.cc
index 407bef1d..83a0936a 100644
--- a/chrome/browser/extensions/desktop_android/desktop_android_extensions_browser_client.cc
+++ b/chrome/browser/extensions/desktop_android/desktop_android_extensions_browser_client.cc
@@ -10,6 +10,8 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/extensions/api/management/chrome_management_api_delegate.h"
 #include "chrome/browser/extensions/api/runtime/chrome_runtime_api_delegate.h"
+#include "chrome/browser/extensions/api/storage/managed_value_store_cache.h"
+#include "chrome/browser/extensions/api/storage/sync_value_store_cache.h"
 #include "chrome/browser/extensions/chrome_extension_system_factory.h"
 #include "chrome/browser/extensions/chrome_extension_web_contents_observer.h"
 #include "chrome/browser/extensions/chrome_extensions_browser_api_provider.h"
@@ -20,6 +22,7 @@
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/profiles/profile_selections.h"
 #include "components/update_client/update_client.h"
+#include "components/value_store/value_store_factory.h"
 #include "components/version_info/version_info.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_task_traits.h"
@@ -82,6 +85,24 @@
     return new ChromeManagementAPIDelegate;
   }
 
+  // The following code is used to support chrome.storage api for sync
+  // and managed mode, until ChromeExtensionAPIClient is ported for
+  // desktop android.
+  void AddAdditionalValueStoreCaches(
+      content::BrowserContext* context,
+      const scoped_refptr<value_store::ValueStoreFactory>& factory,
+      SettingsChangedCallback observer,
+      std::map<settings_namespace::Namespace,
+               raw_ptr<ValueStoreCache, CtnExperimental>>* caches) override {
+    // Add support for chrome.storage.sync.
+    (*caches)[settings_namespace::SYNC] =
+        new SyncValueStoreCache(factory, observer, context->GetPath());
+
+    // Add support for chrome.storage.managed.
+    (*caches)[settings_namespace::MANAGED] = new ManagedValueStoreCache(
+        *Profile::FromBrowserContext(context), factory, observer);
+  }
+
  private:
   std::unique_ptr<MessagingDelegate> messaging_delegate_;
 };
diff --git a/chrome/browser/extensions/extension_navigation_throttle_unittest.cc b/chrome/browser/extensions/extension_navigation_throttle_unittest.cc
index f0f03d1..806a514c 100644
--- a/chrome/browser/extensions/extension_navigation_throttle_unittest.cc
+++ b/chrome/browser/extensions/extension_navigation_throttle_unittest.cc
@@ -46,11 +46,10 @@
 
   // Only construct an ExtensionNavigationThrottle so that we can test it in
   // isolation.
-  std::vector<std::unique_ptr<NavigationThrottle>> CreateThrottlesForNavigation(
+  void CreateThrottlesForNavigation(
       content::NavigationThrottleRegistry& registry) override {
     registry.AddThrottle(std::make_unique<ExtensionNavigationThrottle>(
         &registry.GetNavigationHandle()));
-    return {};
   }
 };
 
diff --git a/chrome/browser/extensions/external_provider_impl.cc b/chrome/browser/extensions/external_provider_impl.cc
index e0b2293..70682c0 100644
--- a/chrome/browser/extensions/external_provider_impl.cc
+++ b/chrome/browser/extensions/external_provider_impl.cc
@@ -74,7 +74,7 @@
 #include "chromeos/components/kiosk/kiosk_utils.h"
 #include "chromeos/components/mgs/managed_guest_session_utils.h"
 #else
-#include "components/policy/core/common/device_local_account_type.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #endif
 
 #if BUILDFLAG(IS_WIN)
diff --git a/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc b/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc
index 35148645..5b543e8 100644
--- a/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc
+++ b/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc
@@ -18,6 +18,7 @@
 #include "base/files/file_util.h"
 #include "base/json/values_util.h"
 #include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/notreached.h"
 #include "base/path_service.h"
@@ -265,111 +266,6 @@
 // the struct below.
 constexpr const int kNoBasePathKey = -1;
 
-using BlockType = ChromeFileSystemAccessPermissionContext::BlockType;
-
-std::vector<ChromeFileSystemAccessPermissionContext::BlockedPath>
-GenerateBlockedPath() {
-  return {
-      // Don't allow users to share their entire home directory, entire desktop
-      // or entire documents folder, but do allow sharing anything inside those
-      // directories not otherwise blocked.
-      {base::DIR_HOME, nullptr, BlockType::kDontBlockChildren},
-      {base::DIR_USER_DESKTOP, nullptr, BlockType::kDontBlockChildren},
-      {chrome::DIR_USER_DOCUMENTS, nullptr, BlockType::kDontBlockChildren},
-      // Similar restrictions for the downloads directory.
-      {chrome::DIR_DEFAULT_DOWNLOADS, nullptr, BlockType::kDontBlockChildren},
-      {chrome::DIR_DEFAULT_DOWNLOADS_SAFE, nullptr,
-       BlockType::kDontBlockChildren},
-      // The Chrome installation itself should not be modified by the web.
-      {base::DIR_EXE, nullptr, BlockType::kBlockAllChildren},
-      {base::DIR_MODULE, nullptr, BlockType::kBlockAllChildren},
-      {base::DIR_ASSETS, nullptr, BlockType::kBlockAllChildren},
-      // And neither should the configuration of at least the currently running
-      // Chrome instance (note that this does not take --user-data-dir command
-      // line overrides into account).
-      {chrome::DIR_USER_DATA, nullptr, BlockType::kBlockAllChildren},
-      // ~/.ssh is pretty sensitive on all platforms, so block access to that.
-      {base::DIR_HOME, FILE_PATH_LITERAL(".ssh"), BlockType::kBlockAllChildren},
-      // And limit access to ~/.gnupg as well.
-      {base::DIR_HOME, FILE_PATH_LITERAL(".gnupg"),
-       BlockType::kBlockAllChildren},
-#if BUILDFLAG(IS_WIN)
-      // Some Windows specific directories to block, basically all apps, the
-      // operating system itself, as well as configuration data for apps.
-      {base::DIR_PROGRAM_FILES, nullptr, BlockType::kBlockAllChildren},
-      {base::DIR_PROGRAM_FILESX86, nullptr, BlockType::kBlockAllChildren},
-      {base::DIR_PROGRAM_FILES6432, nullptr, BlockType::kBlockAllChildren},
-      {base::DIR_WINDOWS, nullptr, BlockType::kBlockAllChildren},
-      {base::DIR_ROAMING_APP_DATA, nullptr, BlockType::kBlockAllChildren},
-      {base::DIR_LOCAL_APP_DATA, nullptr, BlockType::kBlockAllChildren},
-      {base::DIR_COMMON_APP_DATA, nullptr, BlockType::kBlockAllChildren},
-      // Opening a file from an MTP device, such as a smartphone or a camera, is
-      // implemented by Windows as opening a file in the temporary internet
-      // files directory. To support that, allow opening files in that
-      // directory, but not whole directories.
-      {base::DIR_IE_INTERNET_CACHE, nullptr,
-       BlockType::kBlockNestedDirectories},
-#endif
-#if BUILDFLAG(IS_MAC)
-      // Similar Mac specific blocks.
-      {base::DIR_APP_DATA, nullptr, BlockType::kBlockAllChildren},
-      // Block access to the current bundle directory.
-      {chrome::DIR_OUTER_BUNDLE, nullptr, BlockType::kBlockAllChildren},
-      // Block access to the user's Applications directory.
-      {base::DIR_HOME, FILE_PATH_LITERAL("Applications"),
-       BlockType::kBlockAllChildren},
-      // Block access to the root Applications directory.
-      {kNoBasePathKey, FILE_PATH_LITERAL("/Applications"),
-       BlockType::kBlockAllChildren},
-      {base::DIR_HOME, FILE_PATH_LITERAL("Library"),
-       BlockType::kBlockAllChildren},
-      // Allow access to other cloud files, such as Google Drive.
-      {base::DIR_HOME, FILE_PATH_LITERAL("Library/CloudStorage"),
-       BlockType::kDontBlockChildren},
-      // Allow the site to interact with data from its corresponding natively
-      // installed (sandboxed) application. It would be nice to limit a site to
-      // access only _its_ corresponding natively installed application, but
-      // unfortunately there's no straightforward way to do that. See
-      // https://crbug.com/984641#c22.
-      {base::DIR_HOME, FILE_PATH_LITERAL("Library/Containers"),
-       BlockType::kDontBlockChildren},
-      // Allow access to iCloud files...
-      {base::DIR_HOME, FILE_PATH_LITERAL("Library/Mobile Documents"),
-       BlockType::kDontBlockChildren},
-      // ... which may also appear at this directory.
-      {base::DIR_HOME,
-       FILE_PATH_LITERAL("Library/Mobile Documents/com~apple~CloudDocs"),
-       BlockType::kDontBlockChildren},
-#endif
-#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
-      // On Linux also block access to devices via /dev.
-      {kNoBasePathKey, FILE_PATH_LITERAL("/dev"), BlockType::kBlockAllChildren},
-      // And security sensitive data in /proc and /sys.
-      {kNoBasePathKey, FILE_PATH_LITERAL("/proc"),
-       BlockType::kBlockAllChildren},
-      {kNoBasePathKey, FILE_PATH_LITERAL("/sys"), BlockType::kBlockAllChildren},
-      // And system files in /boot and /etc.
-      {kNoBasePathKey, FILE_PATH_LITERAL("/boot"),
-       BlockType::kBlockAllChildren},
-      {kNoBasePathKey, FILE_PATH_LITERAL("/etc"), BlockType::kBlockAllChildren},
-      // And block all of ~/.config, matching the similar restrictions on mac
-      // and windows.
-      {base::DIR_HOME, FILE_PATH_LITERAL(".config"),
-       BlockType::kBlockAllChildren},
-      // Block ~/.dbus as well, just in case, although there probably isn't much
-      // a website can do with access to that directory and its contents.
-      {base::DIR_HOME, FILE_PATH_LITERAL(".dbus"),
-       BlockType::kBlockAllChildren},
-#endif
-#if BUILDFLAG(IS_ANDROID)
-      {base::DIR_ANDROID_APP_DATA, nullptr, BlockType::kBlockAllChildren},
-      {base::DIR_CACHE, nullptr, BlockType::kBlockAllChildren},
-#endif
-      // TODO(crbug.com/40095723): Refine this list, for example add
-      // XDG_CONFIG_HOME when it is not set ~/.config?
-  };
-}
-
 // A wrapper around `base::NormalizeFilePath` that returns its result instead of
 // using an out parameter.
 base::FilePath NormalizeFilePath(const base::FilePath& path) {
@@ -385,46 +281,123 @@
   return normalized_path;
 }
 
-// Checks if `path` should be blocked by the `rules`.
-// The BlockType of the nearest ancestor of a path to check is what
-// ultimately determines if a path is blocked or not. If a blocked path is a
-// descendent of another blocked path, then it may override the
-// child-blocking policy of its ancestor. For example, if /home blocks all
-// children, but /home/downloads does not, then /home/downloads/file.ext
-// will *not* be blocked.
-bool ShouldBlockAccessToPath(
-    const base::FilePath& path,
-    HandleType handle_type,
-    std::vector<ChromeFileSystemAccessPermissionContext::BlockPathRule> rules,
-    std::vector<ChromeFileSystemAccessPermissionContext::BlockedPath>
-        blocked_paths,
-    const base::FilePath& profile_path) {
-  DCHECK(!path.empty());
-#if BUILDFLAG(IS_ANDROID)
-  // The only check for content-URIs is that they are not from an internal
-  // FileProvider.
-  if (path.IsContentUri()) {
-    base::android::BuildInfo* info = base::android::BuildInfo::GetInstance();
-    return base::StartsWith(
-        path.value(), base::StrCat({"content://", info->package_name(), "."}),
-        base::CompareCase::INSENSITIVE_ASCII);
-  }
-#endif
-  DCHECK(path.IsAbsolute());
+using BlockType = ChromeFileSystemAccessPermissionContext::BlockType;
 
-  bool normalize_file_paths = base::FeatureList::IsEnabled(
-      features::kFileSystemAccessSymbolicLinkCheck);
-
-  base::FilePath check_path =
-      normalize_file_paths ? NormalizeFilePath(path) : path;
-
+std::unique_ptr<ChromeFileSystemAccessPermissionContext::BlockPathRules>
+GenerateBlockPaths(bool should_normalize_file_path) {
+  static constexpr ChromeFileSystemAccessPermissionContext::BlockPath
+      kBlockPaths[] = {
+          // Don't allow users to share their entire home directory, entire
+          // desktop or entire documents folder, but do allow sharing anything
+          // inside those directories not otherwise blocked.
+          {base::DIR_HOME, nullptr, BlockType::kDontBlockChildren},
+          {base::DIR_USER_DESKTOP, nullptr, BlockType::kDontBlockChildren},
+          {chrome::DIR_USER_DOCUMENTS, nullptr, BlockType::kDontBlockChildren},
+          // Similar restrictions for the downloads directory.
+          {chrome::DIR_DEFAULT_DOWNLOADS, nullptr,
+           BlockType::kDontBlockChildren},
+          {chrome::DIR_DEFAULT_DOWNLOADS_SAFE, nullptr,
+           BlockType::kDontBlockChildren},
+          // The Chrome installation itself should not be modified by the web.
+          {base::DIR_EXE, nullptr, BlockType::kBlockAllChildren},
+          {base::DIR_MODULE, nullptr, BlockType::kBlockAllChildren},
+          {base::DIR_ASSETS, nullptr, BlockType::kBlockAllChildren},
+          // And neither should the configuration of at least the currently
+          // running
+          // Chrome instance (note that this does not take --user-data-dir
+          // command
+          // line overrides into account).
+          {chrome::DIR_USER_DATA, nullptr, BlockType::kBlockAllChildren},
+          // ~/.ssh is pretty sensitive on all platforms, so block access to
+          // that.
+          {base::DIR_HOME, FILE_PATH_LITERAL(".ssh"),
+           BlockType::kBlockAllChildren},
+          // And limit access to ~/.gnupg as well.
+          {base::DIR_HOME, FILE_PATH_LITERAL(".gnupg"),
+           BlockType::kBlockAllChildren},
 #if BUILDFLAG(IS_WIN)
-  // On Windows, local UNC paths are rejected, as UNC path can be written in a
-  // way that can bypass the blocklist.
-  if (MaybeIsLocalUNCPath(check_path)) {
-    return true;
-  }
+          // Some Windows specific directories to block, basically all apps, the
+          // operating system itself, as well as configuration data for apps.
+          {base::DIR_PROGRAM_FILES, nullptr, BlockType::kBlockAllChildren},
+          {base::DIR_PROGRAM_FILESX86, nullptr, BlockType::kBlockAllChildren},
+          {base::DIR_PROGRAM_FILES6432, nullptr, BlockType::kBlockAllChildren},
+          {base::DIR_WINDOWS, nullptr, BlockType::kBlockAllChildren},
+          {base::DIR_ROAMING_APP_DATA, nullptr, BlockType::kBlockAllChildren},
+          {base::DIR_LOCAL_APP_DATA, nullptr, BlockType::kBlockAllChildren},
+          {base::DIR_COMMON_APP_DATA, nullptr, BlockType::kBlockAllChildren},
+          // Opening a file from an MTP device, such as a smartphone or a
+          // camera, is
+          // implemented by Windows as opening a file in the temporary internet
+          // files directory. To support that, allow opening files in that
+          // directory, but not whole directories.
+          {base::DIR_IE_INTERNET_CACHE, nullptr,
+           BlockType::kBlockNestedDirectories},
 #endif
+#if BUILDFLAG(IS_MAC)
+          // Similar Mac specific blocks.
+          {base::DIR_APP_DATA, nullptr, BlockType::kBlockAllChildren},
+          // Block access to the current bundle directory.
+          {chrome::DIR_OUTER_BUNDLE, nullptr, BlockType::kBlockAllChildren},
+          // Block access to the user's Applications directory.
+          {base::DIR_HOME, FILE_PATH_LITERAL("Applications"),
+           BlockType::kBlockAllChildren},
+          // Block access to the root Applications directory.
+          {kNoBasePathKey, FILE_PATH_LITERAL("/Applications"),
+           BlockType::kBlockAllChildren},
+          {base::DIR_HOME, FILE_PATH_LITERAL("Library"),
+           BlockType::kBlockAllChildren},
+          // Allow access to other cloud files, such as Google Drive.
+          {base::DIR_HOME, FILE_PATH_LITERAL("Library/CloudStorage"),
+           BlockType::kDontBlockChildren},
+          // Allow the site to interact with data from its corresponding
+          // natively
+          // installed (sandboxed) application. It would be nice to limit a site
+          // to
+          // access only _its_ corresponding natively installed application, but
+          // unfortunately there's no straightforward way to do that. See
+          // https://crbug.com/984641#c22.
+          {base::DIR_HOME, FILE_PATH_LITERAL("Library/Containers"),
+           BlockType::kDontBlockChildren},
+          // Allow access to iCloud files...
+          {base::DIR_HOME, FILE_PATH_LITERAL("Library/Mobile Documents"),
+           BlockType::kDontBlockChildren},
+          // ... which may also appear at this directory.
+          {base::DIR_HOME,
+           FILE_PATH_LITERAL("Library/Mobile Documents/com~apple~CloudDocs"),
+           BlockType::kDontBlockChildren},
+#endif
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
+          // On Linux also block access to devices via /dev.
+          {kNoBasePathKey, FILE_PATH_LITERAL("/dev"),
+           BlockType::kBlockAllChildren},
+          // And security sensitive data in /proc and /sys.
+          {kNoBasePathKey, FILE_PATH_LITERAL("/proc"),
+           BlockType::kBlockAllChildren},
+          {kNoBasePathKey, FILE_PATH_LITERAL("/sys"),
+           BlockType::kBlockAllChildren},
+          // And system files in /boot and /etc.
+          {kNoBasePathKey, FILE_PATH_LITERAL("/boot"),
+           BlockType::kBlockAllChildren},
+          {kNoBasePathKey, FILE_PATH_LITERAL("/etc"),
+           BlockType::kBlockAllChildren},
+          // And block all of ~/.config, matching the similar restrictions on
+          // mac
+          // and windows.
+          {base::DIR_HOME, FILE_PATH_LITERAL(".config"),
+           BlockType::kBlockAllChildren},
+          // Block ~/.dbus as well, just in case, although there probably isn't
+          // much
+          // a website can do with access to that directory and its contents.
+          {base::DIR_HOME, FILE_PATH_LITERAL(".dbus"),
+           BlockType::kBlockAllChildren},
+#endif
+#if BUILDFLAG(IS_ANDROID)
+          {base::DIR_ANDROID_APP_DATA, nullptr, BlockType::kBlockAllChildren},
+          {base::DIR_CACHE, nullptr, BlockType::kBlockAllChildren},
+#endif
+          // TODO(crbug.com/40095723): Refine this list, for example add
+          // XDG_CONFIG_HOME when it is not set ~/.config?
+      };
 
   // ChromeOS supports multi-user sign-in. base::DIR_HOME only returns the
   // profile path for the primary user, the first user to sign in. We want to
@@ -433,42 +406,105 @@
   //
   // TODO(crbug.com/375490221): Improve the ChromeOS blocklist logic.
   constexpr bool kUseProfilePathForDirHome = BUILDFLAG(IS_CHROMEOS);
+  // Populate the hard-coded rules.
+  auto block_path_rules = std::make_unique<
+      ChromeFileSystemAccessPermissionContext::BlockPathRules>();
 
-  // Add the hard-coded rules to the dynamic rules.
-  for (const auto& block : blocked_paths) {
-    base::FilePath blocked_path;
-    if (block.base_path_key != kNoBasePathKey) {
-      if (kUseProfilePathForDirHome && block.base_path_key == base::DIR_HOME) {
-        blocked_path = profile_path;
-      } else if (!base::PathService::Get(block.base_path_key, &blocked_path)) {
+  for (const auto& blocked_path : kBlockPaths) {
+    base::FilePath path;
+    if (blocked_path.base_path_key != kNoBasePathKey) {
+      if (kUseProfilePathForDirHome &&
+          blocked_path.base_path_key == base::DIR_HOME) {
+        block_path_rules->profile_based_block_path_rules_.emplace_back(
+            blocked_path.path, blocked_path.type);
         continue;
       }
-      if (block.path) {
-        blocked_path = blocked_path.Append(block.path);
+
+      if (!base::PathService::Get(blocked_path.base_path_key, &path)) {
+        continue;
+      }
+
+      if (blocked_path.path) {
+        path = path.Append(blocked_path.path);
       }
     } else {
-      DCHECK(block.path);
-      blocked_path = base::FilePath(block.path);
+      DCHECK(blocked_path.path);
+      path = base::FilePath(blocked_path.path);
     }
-    rules.emplace_back(blocked_path, block.type);
+    block_path_rules->block_path_rules_.emplace_back(
+        should_normalize_file_path ? NormalizeFilePath(path) : path,
+        blocked_path.type);
   }
 
+  return block_path_rules;
+}
+
+// Checks if `path` should be blocked by the `rules`.
+// The BlockType of the nearest ancestor of a path to check is what
+// ultimately determines if a path is blocked or not. If a blocked path is a
+// descendent of another blocked path, then it may override the
+// child-blocking policy of its ancestor. For example, if /home blocks all
+// children, but /home/downloads does not, then /home/downloads/file.ext
+// will *not* be blocked.
+bool ShouldBlockAccessToPath(
+    bool should_normalize_file_path,
+    base::FilePath path,
+    HandleType handle_type,
+    std::vector<ChromeFileSystemAccessPermissionContext::BlockPathRule>
+        extra_rules,
+    ChromeFileSystemAccessPermissionContext::BlockPathRules block_path_rules,
+    base::FilePath profile_path) {
+  DCHECK(!path.empty());
+  DCHECK(path.IsAbsolute());
+
+  if (should_normalize_file_path) {
+    path = NormalizeFilePath(path);
+    profile_path = NormalizeFilePath(profile_path);
+    for (auto& rule : extra_rules) {
+      rule.path = NormalizeFilePath(rule.path);
+    }
+  }
+
+#if BUILDFLAG(IS_WIN)
+  // On Windows, local UNC paths are rejected, as UNC path can be written in a
+  // way that can bypass the blocklist.
+  if (MaybeIsLocalUNCPath(path)) {
+    return true;
+  }
+#endif
+
   base::FilePath nearest_ancestor;
   BlockType nearest_ancestor_block_type = BlockType::kDontBlockChildren;
-  for (const auto& block : rules) {
-    base::FilePath blocked_path =
-        normalize_file_paths ? NormalizeFilePath(block.path) : block.path;
-
-    if (check_path == blocked_path || check_path.IsParent(blocked_path)) {
-      VLOG(1) << "Blocking access to " << check_path
-              << " because it is a parent of " << blocked_path;
+  auto should_block_with_rule = [&](const base::FilePath& block_path,
+                                    BlockType block_type) -> bool {
+    if (path == block_path || path.IsParent(block_path)) {
+      VLOG(1) << "Blocking access to " << path << " because it is a parent of "
+              << block_path;
       return true;
     }
 
-    if (blocked_path.IsParent(check_path) &&
-        (nearest_ancestor.empty() || nearest_ancestor.IsParent(blocked_path))) {
-      nearest_ancestor = blocked_path;
-      nearest_ancestor_block_type = block.type;
+    if (block_path.IsParent(path) &&
+        (nearest_ancestor.empty() || nearest_ancestor.IsParent(block_path))) {
+      nearest_ancestor = block_path;
+      nearest_ancestor_block_type = block_type;
+    }
+    return false;
+  };
+
+  for (const auto* block_rules_ptr :
+       {&extra_rules, &block_path_rules.block_path_rules_}) {
+    for (const auto& block : *block_rules_ptr) {
+      if (should_block_with_rule(block.path, block.type)) {
+        return true;
+      }
+    }
+  }
+
+  for (const auto& rule : block_path_rules.profile_based_block_path_rules_) {
+    if (should_block_with_rule(
+            rule.path ? profile_path.Append(rule.path) : profile_path,
+            rule.type)) {
+      return true;
     }
   }
 
@@ -476,6 +512,8 @@
   // nearest ancestor does not block access to its children. Grant access.
   if (nearest_ancestor.empty() ||
       nearest_ancestor_block_type == BlockType::kDontBlockChildren) {
+    VLOG(1) << "Not blocking access to " << path << " because it is inside "
+            << nearest_ancestor << " and it's kDontBlockChildren";
     return false;
   }
 
@@ -483,12 +521,14 @@
   // access to directories. Grant access.
   if (handle_type == HandleType::kFile &&
       nearest_ancestor_block_type == BlockType::kBlockNestedDirectories) {
+    VLOG(1) << "Not blocking access to " << path << " because it is inside "
+            << nearest_ancestor << " and it's kBlockNestedDirectories";
     return false;
   }
 
   // The nearest ancestor blocks access to its children, so block access.
-  VLOG(1) << "Blocking access to " << check_path << " because it is inside "
-          << nearest_ancestor;
+  VLOG(1) << "Blocking access to " << path << " because it is inside "
+          << nearest_ancestor << " and it's kBlockAllChildren";
   return true;
 }
 
@@ -1221,6 +1261,16 @@
   std::unique_ptr<base::RetainingOneShotTimer> cleanup_timer;
 };
 
+ChromeFileSystemAccessPermissionContext::BlockPathRules::BlockPathRules() =
+    default;
+ChromeFileSystemAccessPermissionContext::BlockPathRules::~BlockPathRules() =
+    default;
+ChromeFileSystemAccessPermissionContext::BlockPathRules::BlockPathRules(
+    const BlockPathRules& other) = default;
+ChromeFileSystemAccessPermissionContext::BlockPathRules&
+ChromeFileSystemAccessPermissionContext::BlockPathRules::operator=(
+    const BlockPathRules& other) = default;
+
 ChromeFileSystemAccessPermissionContext::
     ChromeFileSystemAccessPermissionContext(content::BrowserContext* context,
                                             const base::Clock* clock)
@@ -1229,7 +1279,9 @@
           ContentSettingsType::FILE_SYSTEM_ACCESS_CHOOSER_DATA,
           HostContentSettingsMapFactory::GetForProfile(context)),
       profile_(context),
-      clock_(clock) {
+      clock_(clock),
+      should_normalize_file_path_(base::FeatureList::IsEnabled(
+          features::kFileSystemAccessSymbolicLinkCheck)) {
   DETACH_FROM_SEQUENCE(sequence_checker_);
   content_settings_ = base::WrapRefCounted(
       HostContentSettingsMapFactory::GetForProfile(profile_));
@@ -1264,12 +1316,32 @@
   }
 #endif
 
-  blocked_paths_ = GenerateBlockedPath();
+  ResetBlockPaths();
 }
 
 ChromeFileSystemAccessPermissionContext::
     ~ChromeFileSystemAccessPermissionContext() = default;
 
+void ChromeFileSystemAccessPermissionContext::ResetBlockPaths() {
+  is_block_path_rules_init_complete_ = false;
+  base::ThreadPool::PostTaskAndReplyWithResult(
+      FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
+      base::BindOnce(&GenerateBlockPaths, should_normalize_file_path_),
+      base::BindOnce(&ChromeFileSystemAccessPermissionContext::UpdateBlockPaths,
+                     weak_factory_.GetWeakPtr()));
+}
+
+void ChromeFileSystemAccessPermissionContext::ResetBlockPathsForTesting() {
+  ResetBlockPaths();
+}
+
+void ChromeFileSystemAccessPermissionContext::UpdateBlockPaths(
+    std::unique_ptr<BlockPathRules> block_path_rules) {
+  block_path_rules_ = std::move(block_path_rules);
+  is_block_path_rules_init_complete_ = true;
+  block_rules_check_callbacks_.Notify(*block_path_rules_.get());
+}
+
 bool ChromeFileSystemAccessPermissionContext::RevokeActiveGrants(
     const url::Origin& origin,
     base::FilePath file_path) {
@@ -1845,6 +1917,21 @@
 
 #endif  // BUILDFLAG(ENTERPRISE_CLOUD_CONTENT_ANALYSIS)
 
+void ChromeFileSystemAccessPermissionContext::
+    CheckShouldBlockAccessToPathAndReply(
+        base::FilePath path,
+        HandleType handle_type,
+        std::vector<BlockPathRule> extra_rules,
+        base::OnceCallback<void(bool)> callback,
+        BlockPathRules block_path_rules) {
+  base::ThreadPool::PostTaskAndReplyWithResult(
+      FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
+      base::BindOnce(&ShouldBlockAccessToPath, should_normalize_file_path_,
+                     path, handle_type, extra_rules, block_path_rules,
+                     profile_path_override_.value_or(profile_->GetPath())),
+      std::move(callback));
+}
+
 void ChromeFileSystemAccessPermissionContext::CheckPathAgainstBlocklist(
     const content::PathInfo& path_info,
     HandleType handle_type,
@@ -1860,6 +1947,19 @@
     return;
   }
 
+#if BUILDFLAG(IS_ANDROID)
+  // The only check for content-URIs is that they are not from an internal
+  // FileProvider.
+  if (path_info.path.IsContentUri()) {
+    base::android::BuildInfo* info = base::android::BuildInfo::GetInstance();
+    std::move(callback).Run(base::StartsWith(
+        path_info.path.value(),
+        base::StrCat({"content://", info->package_name(), "."}),
+        base::CompareCase::INSENSITIVE_ASCII));
+    return;
+  }
+#endif  // BUILDFLAG(IS_ANDROID)
+
   // Unlike the DIR_USER_DATA check, this handles the --user-data-dir override.
   // We check for the user data dir in two different ways: directly, via the
   // profile manager, where it exists (it does not in unit tests), and via the
@@ -1874,12 +1974,20 @@
         BlockType::kBlockAllChildren);
   }
 
-  base::ThreadPool::PostTaskAndReplyWithResult(
-      FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
-      base::BindOnce(&ShouldBlockAccessToPath, path_info.path, handle_type,
-                     extra_rules, blocked_paths_,
-                     profile_path_override_.value_or(profile_->GetPath())),
-      std::move(callback));
+  if (is_block_path_rules_init_complete_) {
+    // The rules initialization is completed, we can just post the task to a
+    // anonymous blocking traits.
+    CheckShouldBlockAccessToPathAndReply(path_info.path, handle_type,
+                                         extra_rules, std::move(callback),
+                                         *block_path_rules_.get());
+    return;
+  }
+  // The check must be performed after the rules initialization is done.
+  block_rules_check_subscription_.push_back(block_rules_check_callbacks_.Add(
+      base::BindOnce(&ChromeFileSystemAccessPermissionContext::
+                         CheckShouldBlockAccessToPathAndReply,
+                     weak_factory_.GetWeakPtr(), path_info.path, handle_type,
+                     extra_rules, std::move(callback))));
 }
 
 void ChromeFileSystemAccessPermissionContext::PerformAfterWriteChecks(
diff --git a/chrome/browser/file_system_access/chrome_file_system_access_permission_context.h b/chrome/browser/file_system_access/chrome_file_system_access_permission_context.h
index 46a2019..5e1aa12 100644
--- a/chrome/browser/file_system_access/chrome_file_system_access_permission_context.h
+++ b/chrome/browser/file_system_access/chrome_file_system_access_permission_context.h
@@ -12,6 +12,7 @@
 #include "base/callback_list.h"
 #include "base/files/file_path.h"
 #include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
 #include "base/scoped_observation.h"
 #include "base/sequence_checker.h"
 #include "base/time/clock.h"
@@ -137,14 +138,38 @@
     kDontBlockChildren
   };
 
-  // Describes a rule for blocking a directory, which can be constructed
-  // dynamically (based on state) or statically (from `kBlockedPaths`).
+  // Describes a rule for blocking a directory, which can be
+  // - constructed dynamically based on the profile path
+  // - provided by the caller
+  // - or constructed statically during `UpdateBlockPaths()`.
   struct BlockPathRule {
     base::FilePath path;
     BlockType type;
   };
 
-  struct BlockedPath {
+  // Describes a rule for blocking a directory, but the file path will be
+  // determined during the check time when profile path is provided.
+  struct ProfileBasedBlockPathRule {
+    // Path that will be appended to the profile path.
+    const base::FilePath::CharType* path;
+    BlockType type;
+  };
+
+  // Contains two lists of the block rules: one for `BlockPathRule`s which has
+  // the path set and normalized, and another for `ProfileBasedBlockPathRule`s
+  // which can only be determined when performing the checks.
+  class BlockPathRules {
+   public:
+    BlockPathRules();
+    ~BlockPathRules();
+    BlockPathRules(const BlockPathRules& other);
+    BlockPathRules& operator=(const BlockPathRules& other);
+
+    std::vector<BlockPathRule> block_path_rules_;
+    std::vector<ProfileBasedBlockPathRule> profile_based_block_path_rules_;
+  };
+
+  struct BlockPath {
     // base::BasePathKey value (or one of the platform specific extensions to
     // it) for a path that should be blocked. Specify kNoBasePathKey if |path|
     // should be used instead.
@@ -371,6 +396,9 @@
   // KeyedService:
   void Shutdown() override;
 
+  // This is needed when updating path with ScopedPathOverride.
+  void ResetBlockPathsForTesting();
+
  protected:
   SEQUENCE_CHECKER(sequence_checker_);
 
@@ -398,6 +426,13 @@
       std::vector<bool> allowed);
 #endif
 
+  void CheckShouldBlockAccessToPathAndReply(
+      base::FilePath path,
+      HandleType handle_type,
+      std::vector<BlockPathRule> extra_rules,
+      base::OnceCallback<void(bool)> callback,
+      BlockPathRules block_path_rules);
+
   // Checks whether the file or directory at `path` corresponds to a directory
   // Chrome considers sensitive (i.e. system files). Calls `callback` with
   // whether the path is on the blocklist.
@@ -545,6 +580,9 @@
   bool RevokeActiveGrants(const url::Origin& origin,
                           base::FilePath file_path = base::FilePath());
 
+  void ResetBlockPaths();
+  void UpdateBlockPaths(std::unique_ptr<BlockPathRules> block_path_rules);
+
   base::WeakPtr<ChromeFileSystemAccessPermissionContext> GetWeakPtr();
 
   const raw_ptr<content::BrowserContext, DanglingUntriaged> profile_;
@@ -581,7 +619,18 @@
 
   std::optional<base::FilePath> profile_path_override_;
 
-  std::vector<BlockedPath> blocked_paths_;
+  // The normalization flag should be consistent during the initialization and
+  // checking, so we store is as a member variable.
+  bool should_normalize_file_path_ = false;
+
+  // The initialization of `block_path_rules_` needs to be done in a blocking
+  // sequence so it's asynchronous. When we need to check if a path should be
+  // blocked, we need to wait until the initialization completes, hence the
+  // `WaitableEvent` and `OnceCallbackList`.
+  std::unique_ptr<BlockPathRules> block_path_rules_;
+  bool is_block_path_rules_init_complete_ = false;
+  std::vector<base::CallbackListSubscription> block_rules_check_subscription_;
+  base::OnceCallbackList<void(BlockPathRules)> block_rules_check_callbacks_;
 
   base::WeakPtrFactory<ChromeFileSystemAccessPermissionContext> weak_factory_{
       this};
diff --git a/chrome/browser/file_system_access/chrome_file_system_access_permission_context_unittest.cc b/chrome/browser/file_system_access/chrome_file_system_access_permission_context_unittest.cc
index f6561fd..01d7f60 100644
--- a/chrome/browser/file_system_access/chrome_file_system_access_permission_context_unittest.cc
+++ b/chrome/browser/file_system_access/chrome_file_system_access_permission_context_unittest.cc
@@ -566,6 +566,7 @@
        ConfirmSensitiveEntryAccess_DontBlockAllChildren) {
   base::FilePath home_dir = temp_dir_.GetPath().AppendASCII("home");
   ScopedHomeDirOverride home_override = OverrideHomeDir(home_dir);
+  permission_context_->ResetBlockPathsForTesting();
 
   // The Home directory itself should not be allowed.
   EXPECT_EQ(ConfirmSensitiveEntryAccessSync(
@@ -592,6 +593,7 @@
        ConfirmSensitiveEntryAccess_BlockAllChildren) {
   base::FilePath app_dir = temp_dir_.GetPath().AppendASCII("app");
   base::ScopedPathOverride app_override(base::DIR_EXE, app_dir, true, true);
+  permission_context_->ResetBlockPathsForTesting();
 
   // The App directory itself should not be allowed.
   EXPECT_TRUE(IsOpenAbort(app_dir, HandleType::kDirectory));
@@ -605,6 +607,7 @@
   base::FilePath app_data_dir = temp_dir_.GetPath().AppendASCII("app_data");
   base::ScopedPathOverride app_data_override(base::DIR_ANDROID_APP_DATA,
                                              app_data_dir, true, true);
+  permission_context_->ResetBlockPathsForTesting();
 
   // The android app data directory, its parent and paths inside should not be
   // allowed.
@@ -617,6 +620,7 @@
   base::FilePath cache_dir = temp_dir_.GetPath().AppendASCII("cache");
   base::ScopedPathOverride cache_override(base::DIR_CACHE, cache_dir, true,
                                           true);
+  permission_context_->ResetBlockPathsForTesting();
   // The android cache directory, its parent and paths inside should not be
   // allowed.
   EXPECT_TRUE(IsOpenAbort(cache_dir, HandleType::kDirectory));
@@ -637,6 +641,7 @@
     base::FilePath download_dir = user_data_dir.AppendASCII("downloads");
     base::ScopedPathOverride download_override(chrome::DIR_DEFAULT_DOWNLOADS,
                                                download_dir, true, true);
+    permission_context_->ResetBlockPathsForTesting();
 
     // The User Data directory itself should not be allowed.
     EXPECT_FALSE(IsOpenAllowed(user_data_dir, HandleType::kDirectory));
@@ -662,8 +667,10 @@
     base::FilePath download_dir = profile_path.AppendASCII("downloads");
     base::ScopedPathOverride download_override(chrome::DIR_DEFAULT_DOWNLOADS,
                                                download_dir, true, true);
+    permission_context_->ResetBlockPathsForTesting();
 
     EXPECT_FALSE(IsOpenAllowed(profile_path, HandleType::kDirectory));
+    DLOG(ERROR) << "HERE";
     EXPECT_FALSE(
         IsOpenAllowed(profile_path.AppendASCII("foo"), HandleType::kFile));
     EXPECT_FALSE(IsOpenAllowed(profile_path.DirName(), HandleType::kDirectory));
@@ -681,6 +688,7 @@
   base::FilePath internet_cache = user_data_dir.AppendASCII("INetCache");
   base::ScopedPathOverride internet_cache_override(base::DIR_IE_INTERNET_CACHE,
                                                    internet_cache, true, true);
+  permission_context_->ResetBlockPathsForTesting();
 
   // The nested INetCache directory itself should not be allowed.
   EXPECT_FALSE(IsOpenAllowed(internet_cache, HandleType::kDirectory));
@@ -703,6 +711,7 @@
   base::ScopedPathOverride app_override(base::DIR_EXE, temp_dir_.GetPath(),
                                         true, true);
   base::ScopedPathOverride home_override(base::DIR_HOME, home_dir, true, true);
+  permission_context_->ResetBlockPathsForTesting();
 
   // `base::DIR_HOME` and paths inside of it should not be allowed.
   EXPECT_FALSE(IsOpenAllowed(home_dir, HandleType::kDirectory));
@@ -727,6 +736,7 @@
        ConfirmSensitiveEntryAccess_RelativePathBlock) {
   base::FilePath home_dir = temp_dir_.GetPath().AppendASCII("home");
   ScopedHomeDirOverride home_override = OverrideHomeDir(home_dir);
+  permission_context_->ResetBlockPathsForTesting();
 
   // ~/.ssh should be blocked.
   EXPECT_EQ(ConfirmSensitiveEntryAccessSync(
@@ -779,6 +789,7 @@
        ConfirmSensitiveEntryAccess_DontBlockAllChildren_Overlapping) {
   base::FilePath home_dir = temp_dir_.GetPath().AppendASCII("home");
   ScopedHomeDirOverride home_override = OverrideHomeDir(home_dir);
+  permission_context_->ResetBlockPathsForTesting();
 
   // The Home directory itself should not be allowed.
   EXPECT_EQ(ConfirmSensitiveEntryAccessSync(
@@ -843,6 +854,7 @@
        ConfirmSensitiveEntryAccess_BlockAllChildren_UserApplicationsDir) {
   base::FilePath home_dir = temp_dir_.GetPath().AppendASCII("home");
   ScopedHomeDirOverride home_override = OverrideHomeDir(home_dir);
+  permission_context_->ResetBlockPathsForTesting();
 
   // User's Applications directory should be blocked.
   base::FilePath user_applications_dir(home_dir.AppendASCII("Applications"));
@@ -866,6 +878,7 @@
   ASSERT_FALSE(outer_bundle_path.empty());
   base::ScopedPathOverride bundle_override(chrome::DIR_OUTER_BUNDLE,
                                            outer_bundle_path, true, true);
+  permission_context_->ResetBlockPathsForTesting();
 
   EXPECT_EQ(ConfirmSensitiveEntryAccessSync(
                 permission_context(), PathInfo(outer_bundle_path),
@@ -980,6 +993,7 @@
   ASSERT_TRUE(base::CreateDirectory(app_dir));
 
   base::ScopedPathOverride app_override(base::DIR_EXE, app_dir, true, true);
+  permission_context_->ResetBlockPathsForTesting();
 
   CreateSymbolicLinkResult result =
       CreateSymbolicLinkForTesting(app_dir, symlink1);
@@ -1026,6 +1040,7 @@
 
   // Set the blocked path to a symbolic link.
   base::ScopedPathOverride app_override(base::DIR_EXE, symlink1, true, true);
+  permission_context_->ResetBlockPathsForTesting();
 
   // The target of the blocked symbolic link should be blocked.
   EXPECT_EQ(
@@ -1038,6 +1053,7 @@
        ConfirmSensitiveEntryAccess_DangerousFile) {
   base::FilePath home_dir = temp_dir_.GetPath().AppendASCII("home");
   ScopedHomeDirOverride home_override = OverrideHomeDir(home_dir);
+  permission_context_->ResetBlockPathsForTesting();
 
   // Saving files with a harmless extension should be allowed.
   EXPECT_EQ(
@@ -1327,6 +1343,7 @@
        GetWellKnownDirectoryPath_Base_OK) {
   base::ScopedPathOverride user_desktop_override(
       base::DIR_USER_DESKTOP, temp_dir_.GetPath(), true, true);
+  permission_context_->ResetBlockPathsForTesting();
   EXPECT_EQ(permission_context()->GetWellKnownDirectoryPath(
                 blink::mojom::WellKnownDirectory::kDirDesktop, kTestOrigin),
             temp_dir_.GetPath());
@@ -1336,6 +1353,7 @@
        GetWellKnownDirectoryPath_Chrome_OK) {
   base::ScopedPathOverride user_documents_override(
       chrome::DIR_USER_DOCUMENTS, temp_dir_.GetPath(), true, true);
+  permission_context_->ResetBlockPathsForTesting();
   EXPECT_EQ(permission_context()->GetWellKnownDirectoryPath(
                 blink::mojom::WellKnownDirectory::kDirDocuments, kTestOrigin),
             temp_dir_.GetPath());
@@ -1348,6 +1366,8 @@
       ->SkipSanitizeDownloadTargetPathForTesting();
   DownloadPrefs::FromBrowserContext(browser_context())
       ->SetDownloadPath(temp_dir_.GetPath());
+  permission_context_->ResetBlockPathsForTesting();
+
 #if BUILDFLAG(IS_ANDROID)
   // Android always uses the system Download directory (/storage/emulated/...).
   ASSERT_TRUE(base::android::GetDownloadsDirectory(&expected_downloads));
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 9613d8b..97d2bc3 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -306,13 +306,13 @@
     "expiry_milestone": 170
   },
   {
-    "name": "android-surface-color-update",
-    "owners": [ "jtanaristy@google.com", "wenyufu@google.com" ],
-    "expiry_milestone": 140
+    "name": "android-sms-otp-filling",
+    "owners": ["kazinova@google.com", "vasilii@chromium.org", "chrome-password-manager-team@google.com"],
+    "expiry_milestone": 145
   },
   {
-    "name": "android-tab-declutter",
-    "owners": [ "wylieb@google.com", "clank-tab-dev@google.com" ],
+    "name": "android-surface-color-update",
+    "owners": [ "jtanaristy@google.com", "wenyufu@google.com" ],
     "expiry_milestone": 140
   },
   {
@@ -331,6 +331,11 @@
     "expiry_milestone": 142
   },
   {
+    "name": "android-tab-declutter-auto-delete",
+    "owners": [ "wylieb@google.com", "clank-tab-dev@google.com" ],
+    "expiry_milestone": 145
+  },
+  {
     "name": "android-tab-declutter-performance-improvements",
     "owners": [ "wylieb@google.com", "clank-tab-dev@google.com" ],
     "expiry_milestone": 140
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index d8f227c..e43e0f0 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -212,6 +212,12 @@
     "Enable the new updated progress bar";
 #endif  // BUILDFLAG(IS_ANDROID)
 
+#if BUILDFLAG(IS_ANDROID)
+const char kAndroidSmsOtpFillingName[] = "Enable SMS OTP filling";
+const char kAndroidSmsOtpFillingDescription[] =
+    "Enables filling of OTPs received via SMS on Android";
+#endif  // BUILDFLAG(IS_ANDROID)
+
 #if BUILDFLAG(IS_CHROMEOS)
 const char kIgnoreDeviceFlexArcEnabledPolicyName[] =
     "Ignore VPN Apps Enabling on ChromeOS Flex";
@@ -4530,9 +4536,10 @@
 const char kAndroidSurfaceColorUpdateDescription[] =
     "If enabled, updates the android surface colors for toolbar/omnibox.";
 
-const char kAndroidTabDeclutterName[] = "Android Tab Declutter";
-const char kAndroidTabDeclutterDescription[] =
-    "Enables auto-archival and deletion of inactive tabs.";
+const char kAndroidTabDeclutterAutoDeleteName[] =
+    "Android Tab Declutter Auto Deletion";
+const char kAndroidTabDeclutterAutoDeleteDescription[] =
+    "Enables auto-deletion of inactive tabs.";
 
 const char kAndroidTabDeclutterArchiveAllButActiveTabName[] =
     "Archive all tabs except active";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 97080e12..506c53ec 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -146,6 +146,11 @@
 extern const char kAndroidProgressBarVisualUpdateDescription[];
 #endif  // BUILDFLAG(IS_ANDROID)
 
+#if BUILDFLAG(IS_ANDROID)
+extern const char kAndroidSmsOtpFillingName[];
+extern const char kAndroidSmsOtpFillingDescription[];
+#endif  // BUILDFLAG(IS_ANDROID)
+
 #if BUILDFLAG(IS_CHROMEOS)
 extern const char kIgnoreDeviceFlexArcEnabledPolicyName[];
 extern const char kIgnoreDeviceFlexArcEnabledPolicyDescription[];
@@ -2650,8 +2655,8 @@
 extern const char kAndroidSurfaceColorUpdateName[];
 extern const char kAndroidSurfaceColorUpdateDescription[];
 
-extern const char kAndroidTabDeclutterName[];
-extern const char kAndroidTabDeclutterDescription[];
+extern const char kAndroidTabDeclutterAutoDeleteName[];
+extern const char kAndroidTabDeclutterAutoDeleteDescription[];
 
 extern const char kAndroidTabDeclutterArchiveAllButActiveTabName[];
 extern const char kAndroidTabDeclutterArchiveAllButActiveTabDescription[];
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
index 6f270ca..cbbb38f 100644
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -200,10 +200,10 @@
     &kAndroidOpenPdfInlineBackport,
     &kAndroidPdfAssistContent,
     &kAndroidSurfaceColorUpdate,
-    &kAndroidTabDeclutter,
     &kAndroidTabDeclutterArchiveAllButActiveTab,
     &kAndroidTabDeclutterArchiveDuplicateTabs,
     &kAndroidTabDeclutterArchiveTabGroups,
+    &kAndroidTabDeclutterAutoDelete,
     &kAndroidTabDeclutterDedupeTabIdsKillSwitch,
     &kAndroidTabDeclutterRescueKillswitch,
     &kAndroidTabDeclutterPerformanceImprovements,
@@ -311,7 +311,6 @@
     &kNativePageTransitionHardwareCapture,
     &kNavBarColorAnimation,
     &kNavBarColorMatchesTabBackground,
-    &kNavigationCaptureRefactorAndroid,
     &kNewTabSearchEngineUrlAndroid,
     &kNewTabPageAndroidTriggerForPrerender2,
     &kNotificationPermissionVariant,
@@ -557,16 +556,12 @@
 
 BASE_FEATURE(kAndroidPdfAssistContent,
              "AndroidPdfAssistContent",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 BASE_FEATURE(kAndroidSurfaceColorUpdate,
              "AndroidSurfaceColorUpdate",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-BASE_FEATURE(kAndroidTabDeclutter,
-             "AndroidTabDeclutter",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 BASE_FEATURE(kAndroidTabDeclutterArchiveAllButActiveTab,
              "AndroidTabDeclutterArchiveAllButActiveTab",
              base::FEATURE_DISABLED_BY_DEFAULT);
@@ -579,6 +574,10 @@
              "AndroidTabDeclutterArchiveTabGroups",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+BASE_FEATURE(kAndroidTabDeclutterAutoDelete,
+             "AndroidTabDeclutterAutoDelete",
+             base::FEATURE_ENABLED_BY_DEFAULT);
+
 BASE_FEATURE(kAndroidTabDeclutterDedupeTabIdsKillSwitch,
              "AndroidTabDeclutterDedupeTabIdsKillSwitch",
              base::FEATURE_ENABLED_BY_DEFAULT);
@@ -1012,10 +1011,6 @@
              "NavBarColorMatchesTabBackground",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
-BASE_FEATURE(kNavigationCaptureRefactorAndroid,
-             "NavigationCaptureRefactorAndroid",
-             base::FEATURE_DISABLED_BY_DEFAULT);
-
 BASE_FEATURE(kNewTabSearchEngineUrlAndroid,
              "NewTabSearchEngineUrlAndroid",
              base::FEATURE_ENABLED_BY_DEFAULT);
diff --git a/chrome/browser/flags/android/chrome_feature_list.h b/chrome/browser/flags/android/chrome_feature_list.h
index f41d050..a76bb28 100644
--- a/chrome/browser/flags/android/chrome_feature_list.h
+++ b/chrome/browser/flags/android/chrome_feature_list.h
@@ -9,7 +9,6 @@
 
 #include "base/feature_list.h"
 #include "base/metrics/field_trial_params.h"
-#include "chrome/common/chrome_features.h"
 
 namespace chrome {
 namespace android {
@@ -38,10 +37,10 @@
 BASE_DECLARE_FEATURE(kAndroidOpenPdfInlineBackport);
 BASE_DECLARE_FEATURE(kAndroidPdfAssistContent);
 BASE_DECLARE_FEATURE(kAndroidSurfaceColorUpdate);
-BASE_DECLARE_FEATURE(kAndroidTabDeclutter);
 BASE_DECLARE_FEATURE(kAndroidTabDeclutterArchiveAllButActiveTab);
 BASE_DECLARE_FEATURE(kAndroidTabDeclutterArchiveDuplicateTabs);
 BASE_DECLARE_FEATURE(kAndroidTabDeclutterArchiveTabGroups);
+BASE_DECLARE_FEATURE(kAndroidTabDeclutterAutoDelete);
 BASE_DECLARE_FEATURE(kAndroidTabDeclutterRescueKillswitch);
 BASE_DECLARE_FEATURE(kAndroidTabDeclutterDedupeTabIdsKillSwitch);
 BASE_DECLARE_FEATURE(kAndroidTabDeclutterPerformanceImprovements);
@@ -155,7 +154,6 @@
 BASE_DECLARE_FEATURE(kNativePageTransitionHardwareCapture);
 BASE_DECLARE_FEATURE(kNavBarColorAnimation);
 BASE_DECLARE_FEATURE(kNavBarColorMatchesTabBackground);
-BASE_DECLARE_FEATURE(kNavigationCaptureRefactorAndroid);
 BASE_DECLARE_FEATURE(kNewTabSearchEngineUrlAndroid);
 BASE_DECLARE_FEATURE(kNewTabPageAndroidTriggerForPrerender2);
 BASE_DECLARE_FEATURE(kNotificationPermissionVariant);
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 90fcf99..0ee2fc3 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
@@ -188,13 +188,13 @@
     public static final String ANDROID_PDF_ASSIST_CONTENT = "AndroidPdfAssistContent";
     public static final String ANDROID_PROGRESS_BAR_VISUAL_UPDATE = "AndroidProgressBarVisualUpdate";
     public static final String ANDROID_SURFACE_COLOR_UPDATE = "AndroidSurfaceColorUpdate";
-    public static final String ANDROID_TAB_DECLUTTER = "AndroidTabDeclutter";
     public static final String ANDROID_TAB_DECLUTTER_ARCHIVE_ALL_BUT_ACTIVE =
             "AndroidTabDeclutterArchiveAllButActiveTab";
     public static final String ANDROID_TAB_DECLUTTER_ARCHIVE_DUPLICATE_TABS =
             "AndroidTabDeclutterArchiveDuplicateTabs";
     public static final String ANDROID_TAB_DECLUTTER_ARCHIVE_TAB_GROUPS =
             "AndroidTabDeclutterArchiveTabGroups";
+    public static final String ANDROID_TAB_DECLUTTER_AUTO_DELETE = "AndroidTabDeclutterAutoDelete";
     public static final String ANDROID_TAB_DECLUTTER_DEDUPE_TAB_IDS_KILL_SWITCH =
             "AndroidTabDeclutterDedupeTabIdsKillSwitch";
     public static final String ANDROID_TAB_DECLUTTER_PERFORMANCE_IMPROVEMENTS =
@@ -993,14 +993,14 @@
             newMutableFlagWithSafeDefault(ANDROID_DUMP_ON_SCROLL_WITHOUT_RESOURCE, false);
     public static final MutableFlagWithSafeDefault sAndroidNativePagesInNewTab =
             newMutableFlagWithSafeDefault(ANDROID_NATIVE_PAGES_IN_NEW_TAB, false);
-    public static final MutableFlagWithSafeDefault sAndroidTabDeclutter =
-            newMutableFlagWithSafeDefault(ANDROID_TAB_DECLUTTER, true);
     public static final MutableFlagWithSafeDefault sAndroidTabDeclutterArchiveAllButActiveTab =
             newMutableFlagWithSafeDefault(ANDROID_TAB_DECLUTTER_ARCHIVE_ALL_BUT_ACTIVE, false);
     public static final MutableFlagWithSafeDefault sAndroidTabDeclutterArchiveDuplicateTabs =
             newMutableFlagWithSafeDefault(ANDROID_TAB_DECLUTTER_ARCHIVE_DUPLICATE_TABS, true);
     public static final MutableFlagWithSafeDefault sAndroidTabDeclutterArchiveTabGroups =
             newMutableFlagWithSafeDefault(ANDROID_TAB_DECLUTTER_ARCHIVE_TAB_GROUPS, false);
+    public static final MutableFlagWithSafeDefault sAndroidTabDeclutterAutoDelete =
+            newMutableFlagWithSafeDefault(ANDROID_TAB_DECLUTTER_AUTO_DELETE, false);
     public static final MutableFlagWithSafeDefault sAndroidTabDeclutterPerformanceImprovements =
             newMutableFlagWithSafeDefault(ANDROID_TAB_DECLUTTER_PERFORMANCE_IMPROVEMENTS, false);
     public static final MutableFlagWithSafeDefault sAndroidTabDeclutterRescueKillSwitch =
@@ -1492,30 +1492,15 @@
                     "android_native_pages_in_new_tab_history_enabled", true);
     public static final MutableBooleanParamWithSafeDefault
             sAndroidNativePagesInNewTabRecentTabsEnabled =
-            sAndroidNativePagesInNewTab.newBooleanParam(
-                    "android_native_pages_in_new_tab_recent_tabs_enabled", true);
-    public static final MutableBooleanParamWithSafeDefault sAndroidTabDeclutterArchiveEnabled =
-            sAndroidTabDeclutter.newBooleanParam("android_tab_declutter_archive_enabled", true);
-    public static final MutableIntParamWithSafeDefault sAndroidTabDeclutterArchiveTimeDeltaHours =
-            sAndroidTabDeclutter.newIntParam(
-                    "android_tab_declutter_archive_time_delta_hours", 21 * 24);
+                    sAndroidNativePagesInNewTab.newBooleanParam(
+                            "android_native_pages_in_new_tab_recent_tabs_enabled", true);
     public static final MutableBooleanParamWithSafeDefault sAndroidTabDeclutterAutoDeleteEnabled =
-            sAndroidTabDeclutter.newBooleanParam(
+            sAndroidTabDeclutterAutoDelete.newBooleanParam(
                     "android_tab_declutter_auto_delete_enabled", false);
     public static final MutableIntParamWithSafeDefault
             sAndroidTabDeclutterAutoDeleteTimeDeltaHours =
-                    sAndroidTabDeclutter.newIntParam(
+                    sAndroidTabDeclutterAutoDelete.newIntParam(
                             "android_tab_declutter_auto_delete_time_delta_hours", 60 * 24);
-    public static final MutableIntParamWithSafeDefault sAndroidTabDeclutterIntervalTimeDeltaHours =
-            sAndroidTabDeclutter.newIntParam(
-                    "android_tab_declutter_interval_time_delta_hours", 7 * 24);
-    public static final MutableIntParamWithSafeDefault sAndroidTabDeclutterMaxSimultaneousArchives =
-            sAndroidTabDeclutter.newIntParam(
-                    "android_tab_declutter_max_simultaneous_archives", 100);
-    public static final MutableIntParamWithSafeDefault
-            sAndroidTabDeclutterIphMessageDismissThreshold =
-                    sAndroidTabDeclutter.newIntParam(
-                            "android_tab_declutter_iph_message_dismiss_threshold", 3);
     public static final MutableBooleanParamWithSafeDefault
             sDisableBottomControlsStackerYOffsetDispatching =
                     sBottomBrowserControlsRefactor.newBooleanParam(
diff --git a/chrome/browser/glic/BUILD.gn b/chrome/browser/glic/BUILD.gn
index 5f4cbd6d..3a3db741 100644
--- a/chrome/browser/glic/BUILD.gn
+++ b/chrome/browser/glic/BUILD.gn
@@ -151,7 +151,8 @@
     "widget/glic_widget.h",
     "widget/glic_window_animator.cc",
     "widget/glic_window_animator.h",
-    "widget/glic_window_controller.cc",
+    "widget/glic_window_controller_impl.cc",
+    "widget/glic_window_controller_impl.h",
     "widget/glic_window_hotkey_delegate.cc",
     "widget/glic_window_resize_animation.cc",
     "widget/glic_window_resize_animation.h",
@@ -236,6 +237,8 @@
     "test_support/interactive_glic_test.h",
     "test_support/interactive_test_util.cc",
     "test_support/interactive_test_util.h",
+    "test_support/mock_glic_window_controller.cc",
+    "test_support/mock_glic_window_controller.h",
   ]
   deps = [
     ":glic",
diff --git a/chrome/browser/glic/fre/glic_fre_controller_browsertest.cc b/chrome/browser/glic/fre/glic_fre_controller_browsertest.cc
index 18623bb..3c4557be 100644
--- a/chrome/browser/glic/fre/glic_fre_controller_browsertest.cc
+++ b/chrome/browser/glic/fre/glic_fre_controller_browsertest.cc
@@ -163,10 +163,18 @@
   EXPECT_TRUE(glic_fre_controller()->IsShowingDialog());
 }
 
+// TODO(crbug.com/402310277): Re-enable this test.
+#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
+#define MAYBE_FreDialogCloseAndReopenForDifferentTab \
+  DISABLED_FreDialogCloseAndReopenForDifferentTab
+#else
+#define MAYBE_FreDialogCloseAndReopenForDifferentTab \
+  FreDialogCloseAndReopenForDifferentTab
+#endif  // BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
 // Tests that, when the FRE dialog is already open in an inactive tab, trying to
 // show it in the active tab closes the existing dialog and opens a new one.
 IN_PROC_BROWSER_TEST_F(GlicFreControllerBrowserTest,
-                       FreDialogCloseAndReopenForDifferentTab) {
+                       MAYBE_FreDialogCloseAndReopenForDifferentTab) {
   // Open the FRE dialog in a tab.
   chrome::AddTabAt(browser(), GURL("about:blank"), -1, true);
   browser()->tab_strip_model()->ActivateTabAt(0);
diff --git a/chrome/browser/glic/glic_keyed_service.cc b/chrome/browser/glic/glic_keyed_service.cc
index 0a4604d..7f3f6b60 100644
--- a/chrome/browser/glic/glic_keyed_service.cc
+++ b/chrome/browser/glic/glic_keyed_service.cc
@@ -33,7 +33,7 @@
 #include "chrome/browser/glic/host/host.h"
 #include "chrome/browser/glic/host/webui_contents_container.h"
 #include "chrome/browser/glic/widget/glic_widget.h"
-#include "chrome/browser/glic/widget/glic_window_controller.h"
+#include "chrome/browser/glic/widget/glic_window_controller_impl.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_attributes_storage.h"
 #include "chrome/browser/profiles/profile_manager.h"
@@ -85,10 +85,10 @@
       metrics_(std::make_unique<GlicMetrics>(profile, enabling_.get())),
       host_(std::make_unique<Host>(profile)),
       window_controller_(
-          std::make_unique<GlicWindowController>(profile,
-                                                 identity_manager,
-                                                 this,
-                                                 enabling_.get())),
+          std::make_unique<GlicWindowControllerImpl>(profile,
+                                                     identity_manager,
+                                                     this,
+                                                     enabling_.get())),
       focused_tab_manager_(profile, *window_controller_),
       screenshot_capturer_(std::make_unique<GlicScreenshotCapturer>()),
       auth_controller_(std::make_unique<AuthController>(profile,
@@ -210,6 +210,11 @@
   }
 }
 
+GlicWindowController& GlicKeyedService::window_controller() {
+  CHECK(window_controller_);
+  return *window_controller_.get();
+}
+
 void GlicKeyedService::GuestAdded(content::WebContents* guest_contents) {
   host().GuestAdded(guest_contents);
 }
@@ -386,6 +391,17 @@
   actor_controller_->StopTask();
 }
 
+bool GlicKeyedService::IsActorCoordinatorActingOnTab(
+    const content::WebContents* tab) const {
+  return actor_controller_ &&
+         actor_controller_->IsActorCoordinatorActingOnTab(tab);
+}
+
+actor::ActorCoordinator& GlicKeyedService::GetActorCoordinatorForTesting() {
+  CHECK(actor_controller_);
+  return actor_controller_->GetActorCoordinatorForTesting();  // IN-TEST
+}
+
 void GlicKeyedService::CaptureScreenshot(
     mojom::WebClientHandler::CaptureScreenshotCallback callback) {
   screenshot_capturer_->CaptureScreenshot(
diff --git a/chrome/browser/glic/glic_keyed_service.h b/chrome/browser/glic/glic_keyed_service.h
index 0fc8c24..3431c01 100644
--- a/chrome/browser/glic/glic_keyed_service.h
+++ b/chrome/browser/glic/glic_keyed_service.h
@@ -21,6 +21,10 @@
 class Profile;
 class ProfileManager;
 
+namespace actor {
+class ActorCoordinator;
+}  // namespace actor
+
 namespace contextual_cueing {
 class ContextualCueingService;
 }  // namespace contextual_cueing
@@ -37,7 +41,7 @@
 class GlicMetrics;
 class GlicProfileManager;
 class GlicScreenshotCapturer;
-class GlicWindowController;
+class GlicWindowControllerImpl;
 
 // The GlicKeyedService is created for each eligible (i.e. non-incognito,
 // non-system, etc.) browser profile if Glic flags are enabled, regardless
@@ -89,7 +93,7 @@
   GlicEnabling& enabling() { return *enabling_.get(); }
 
   GlicMetrics* metrics() { return metrics_.get(); }
-  GlicWindowController& window_controller() { return *window_controller_; }
+  GlicWindowController& window_controller();
 
   // Called when a webview guest is created within a chrome://glic WebUI.
   void GuestAdded(content::WebContents* guest_contents);
@@ -196,6 +200,13 @@
 
   void StopActorTask();
 
+  // Returns true if the associated ActorCoordinator is active on the given
+  // `tab`. This can be used by callers to customize certain behaviour that
+  // might interfere with the ActorCoordinator.
+  bool IsActorCoordinatorActingOnTab(const content::WebContents* tab) const;
+
+  actor::ActorCoordinator& GetActorCoordinatorForTesting();
+
   void CaptureScreenshot(
       glic::mojom::WebClientHandler::CaptureScreenshotCallback callback);
 
@@ -246,7 +257,7 @@
   std::unique_ptr<GlicEnabling> enabling_;
   std::unique_ptr<GlicMetrics> metrics_;
   std::unique_ptr<Host> host_;
-  std::unique_ptr<GlicWindowController> window_controller_;
+  std::unique_ptr<GlicWindowControllerImpl> window_controller_;
   GlicFocusedTabManager focused_tab_manager_;
   std::unique_ptr<GlicScreenshotCapturer> screenshot_capturer_;
   std::unique_ptr<AuthController> auth_controller_;
diff --git a/chrome/browser/glic/glic_profile_manager.cc b/chrome/browser/glic/glic_profile_manager.cc
index 8878639..0eb12b1 100644
--- a/chrome/browser/glic/glic_profile_manager.cc
+++ b/chrome/browser/glic/glic_profile_manager.cc
@@ -13,6 +13,7 @@
 #include "chrome/browser/glic/glic_keyed_service_factory.h"
 #include "chrome/browser/global_features.h"
 #include "chrome/browser/lifetime/termination_notification.h"
+#include "chrome/browser/profiles/nuke_profile_directory_utils.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/browser.h"
@@ -147,6 +148,9 @@
 void GlicProfileManager::ShouldPreloadForProfile(
     Profile* profile,
     ShouldPreloadCallback callback) {
+  if (IsProfileDirectoryMarkedForDeletion(profile->GetPath())) {
+    return;
+  }
   if (!base::FeatureList::IsEnabled(features::kGlicWarming) ||
       !GlicEnabling::IsReadyForProfile(profile)) {
     base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
diff --git a/chrome/browser/glic/host/glic_actor_controller.cc b/chrome/browser/glic/host/glic_actor_controller.cc
index aab7d5c4..ccdb251 100644
--- a/chrome/browser/glic/host/glic_actor_controller.cc
+++ b/chrome/browser/glic/host/glic_actor_controller.cc
@@ -101,6 +101,18 @@
   actor_coordinator_->StopTask();
 }
 
+bool GlicActorController::IsActorCoordinatorActingOnTab(
+    const content::WebContents* tab) const {
+  return actor_coordinator_ && actor_coordinator_->HasTaskForTab(tab);
+}
+
+actor::ActorCoordinator& GlicActorController::GetActorCoordinatorForTesting() {
+  if (!actor_coordinator_) {
+    actor_coordinator_ = std::make_unique<actor::ActorCoordinator>(profile_);
+  }
+  return *actor_coordinator_;
+}
+
 void GlicActorController::OnTaskStarted(
     const optimization_guide::proto::BrowserAction& action,
     const mojom::GetTabContextOptions& options,
diff --git a/chrome/browser/glic/host/glic_actor_controller.h b/chrome/browser/glic/host/glic_actor_controller.h
index 5a30ae68..9776a1b 100644
--- a/chrome/browser/glic/host/glic_actor_controller.h
+++ b/chrome/browser/glic/host/glic_actor_controller.h
@@ -20,6 +20,10 @@
 class ActorCoordinator;
 }
 
+namespace content {
+class WebContents;
+}  // namespace content
+
 namespace tabs {
 class TabInterface;
 }  // namespace tabs
@@ -42,6 +46,10 @@
 
   void StopTask();
 
+  bool IsActorCoordinatorActingOnTab(const content::WebContents* tab) const;
+
+  actor::ActorCoordinator& GetActorCoordinatorForTesting();
+
  private:
   // Handles a new task being started, and then performs the action that
   // initiated the task.
diff --git a/chrome/browser/glic/host/glic_api_uitest.cc b/chrome/browser/glic/host/glic_api_uitest.cc
index 020f4a8f..998dba19 100644
--- a/chrome/browser/glic/host/glic_api_uitest.cc
+++ b/chrome/browser/glic/host/glic_api_uitest.cc
@@ -1078,56 +1078,51 @@
 }
 
 IN_PROC_BROWSER_TEST_F(GlicApiTestWithOneTab, testManualResizeChanged) {
-  window_controller().OnWidgetUserResizeStarted();
+  window_controller().GetGlicWidget()->OnNativeWidgetUserResizeStarted();
 
   // Check that the web client is notified of the beginning of the user
   // initiated resizing event.
   ExecuteJsTest();
 
-  window_controller().OnWidgetUserResizeEnded();
+  window_controller().GetGlicWidget()->OnNativeWidgetUserResizeEnded();
 
   // Check that the web client is notified of the ending of the user
   // initiated resizing event.
   ContinueJsTest();
 }
 
-// TODO(crbug.com/409712213): Test fails for Windows.
-#if BUILDFLAG(IS_WIN)
-#define MAYBE_testResizeWindowTooSmall DISABLED_testResizeWindowTooSmall
-#else
-#define MAYBE_testResizeWindowTooSmall testResizeWindowTooSmall
-#endif
-IN_PROC_BROWSER_TEST_F(GlicApiTestWithOneTab, MAYBE_testResizeWindowTooSmall) {
+IN_PROC_BROWSER_TEST_F(GlicApiTestWithOneTab, testResizeWindowTooSmall) {
   // Web client requests the window to be resized to 0x0, bellow the minimum
   // dimensions (see GlicWindowController#GetLastRequestedSizeClamped), so it
-  // gets discarded in favor of the initial size.≈
+  // gets discarded in favor of the initial size.
+  gfx::Size expected_size = GlicWidget::GetInitialSize();
+  GlicWidget* glic_widget = window_controller().GetGlicWidget();
+  ASSERT_TRUE(glic_widget);
+
   ExecuteJsTest();
-  gfx::Size initial_size = GlicWidget::GetInitialSize();
-  gfx::Size min_real_size = window_controller().GetSize();
-  ASSERT_EQ(initial_size.width(), min_real_size.width());
-  ASSERT_EQ(initial_size.height(), min_real_size.height());
+
+  gfx::Rect final_widget_bounds = glic_widget->GetWindowBoundsInScreen();
+  ASSERT_EQ(expected_size,
+            glic_widget->WidgetToVisibleBounds(final_widget_bounds).size());
 }
 
 IN_PROC_BROWSER_TEST_F(GlicApiTestWithOneTab, testResizeWindowTooLarge) {
-  // Web client requests the window to be resized to 2000x2000, above the
+  // Web client requests the window to be resized to 20000x20000, above the
   // maximum dimensions (see GlicWindowController#GetLastRequestedSizeClamped),
-  // so it gets discarded in favor of the display work area.
+  // so it gets discarded in favor of the max size. This max size is still
+  // larger than the display work area so we clamp the dimensions down to fit on
+  // screen.
   ExecuteJsTest();
   gfx::Rect display_bounds =
       display::Screen::GetScreen()->GetPrimaryDisplay().work_area();
-  gfx::Size max_real_size = window_controller().GetSize();
-  ASSERT_EQ(display_bounds.width(), max_real_size.width());
-  ASSERT_EQ(display_bounds.height(), max_real_size.height());
+  GlicWidget* glic_widget = window_controller().GetGlicWidget();
+  ASSERT_TRUE(glic_widget);
+  gfx::Rect final_widget_bounds = glic_widget->GetWindowBoundsInScreen();
+
+  ASSERT_TRUE(display_bounds.Contains(final_widget_bounds));
 }
 
-// TODO(crbug.com/409712213): Test fails for Windows.
-#if BUILDFLAG(IS_WIN)
-#define MAYBE_testResizeWindowWithinBounds DISABLED_testResizeWindowWithinBounds
-#else
-#define MAYBE_testResizeWindowWithinBounds testResizeWindowWithinBounds
-#endif
-IN_PROC_BROWSER_TEST_F(GlicApiTestWithOneTab,
-                       MAYBE_testResizeWindowWithinBounds) {
+IN_PROC_BROWSER_TEST_F(GlicApiTestWithOneTab, testResizeWindowWithinBounds) {
   // Web client requests the window to be resized to 800x700, which are valid
   // dimensions.
   gfx::Size expected_size = gfx::Size(800, 700);
@@ -1135,9 +1130,11 @@
       {.params = base::Value(base::Value::Dict()
                                  .Set("width", expected_size.width())
                                  .Set("height", expected_size.height()))});
-  gfx::Size valid_real_size = window_controller().GetSize();
-  ASSERT_EQ(expected_size.width(), valid_real_size.width());
-  ASSERT_EQ(expected_size.height(), valid_real_size.height());
+  GlicWidget* glic_widget = window_controller().GetGlicWidget();
+  ASSERT_TRUE(glic_widget);
+  gfx::Rect final_widget_bounds = glic_widget->GetWindowBoundsInScreen();
+  ASSERT_EQ(expected_size,
+            glic_widget->WidgetToVisibleBounds(final_widget_bounds).size());
 }
 
 class GlicApiTestPageContextEligibilityTest : public GlicApiTest {
diff --git a/chrome/browser/glic/test_support/mock_glic_window_controller.cc b/chrome/browser/glic/test_support/mock_glic_window_controller.cc
new file mode 100644
index 0000000..0c8f9a0f
--- /dev/null
+++ b/chrome/browser/glic/test_support/mock_glic_window_controller.cc
@@ -0,0 +1,12 @@
+// 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/test_support/mock_glic_window_controller.h"
+
+namespace glic {
+
+MockGlicWindowController::MockGlicWindowController() = default;
+MockGlicWindowController::~MockGlicWindowController() = default;
+
+}  // namespace glic
diff --git a/chrome/browser/glic/test_support/mock_glic_window_controller.h b/chrome/browser/glic/test_support/mock_glic_window_controller.h
new file mode 100644
index 0000000..5950a806
--- /dev/null
+++ b/chrome/browser/glic/test_support/mock_glic_window_controller.h
@@ -0,0 +1,85 @@
+// 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_TEST_SUPPORT_MOCK_GLIC_WINDOW_CONTROLLER_H_
+#define CHROME_BROWSER_GLIC_TEST_SUPPORT_MOCK_GLIC_WINDOW_CONTROLLER_H_
+
+#include "chrome/browser/glic/widget/glic_window_controller.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace glic {
+
+class MockGlicWindowController
+    : public testing::NiceMock<GlicWindowController> {
+ public:
+  MockGlicWindowController();
+  ~MockGlicWindowController();
+
+  MOCK_METHOD(void,
+              Toggle,
+              (BrowserWindowInterface*, bool, mojom::InvocationSource),
+              (override));
+  MOCK_METHOD(void, ShowAfterSignIn, (base::WeakPtr<Browser>), (override));
+  MOCK_METHOD(void,
+              ToggleWhenNotAlwaysDetached,
+              (Browser*, bool, mojom::InvocationSource),
+              (override));
+  MOCK_METHOD(void, FocusIfOpen, (), (override));
+  MOCK_METHOD(void, Attach, (), (override));
+  MOCK_METHOD(void, Detach, (), (override));
+  MOCK_METHOD(void, Shutdown, (), (override));
+  MOCK_METHOD(void,
+              Resize,
+              (const gfx::Size&, base::TimeDelta, base::OnceClosure),
+              (override));
+  MOCK_METHOD(void, EnableDragResize, (bool), (override));
+  MOCK_METHOD(gfx::Size, GetSize, (), (override));
+  MOCK_METHOD(void,
+              SetDraggableAreas,
+              (const std::vector<gfx::Rect>&),
+              (override));
+  MOCK_METHOD(void, SetMinimumWidgetSize, (const gfx::Size&), (override));
+  MOCK_METHOD(void, Close, (), (override));
+  MOCK_METHOD(void, CloseWithReason, (views::Widget::ClosedReason), (override));
+  MOCK_METHOD(void, ShowTitleBarContextMenuAt, (gfx::Point), (override));
+  MOCK_METHOD(bool,
+              ShouldStartDrag,
+              (const gfx::Point&, const gfx::Point&),
+              (override));
+  MOCK_METHOD(void, HandleWindowDragWithOffset, (gfx::Vector2d), (override));
+  MOCK_METHOD(const mojom::PanelState&, GetPanelState, (), (const, override));
+  MOCK_METHOD(void, AddStateObserver, (StateObserver*), (override));
+  MOCK_METHOD(void, RemoveStateObserver, (StateObserver*), (override));
+  MOCK_METHOD(bool, IsActive, (), (override));
+  MOCK_METHOD(bool, IsShowing, (), (const, override));
+  MOCK_METHOD(bool, IsPanelOrFreShowing, (), (const, override));
+  MOCK_METHOD(bool, IsAttached, (), (const, override));
+  MOCK_METHOD(bool, IsDetached, (), (const, override));
+  MOCK_METHOD(base::CallbackListSubscription,
+              AddWindowActivationChangedCallback,
+              (WindowActivationChangedCallback),
+              (override));
+  MOCK_METHOD(void, Preload, (), (override));
+  MOCK_METHOD(void, PreloadFre, (), (override));
+  MOCK_METHOD(void, Reload, (), (override));
+  MOCK_METHOD(bool, IsWarmed, (), (const, override));
+  MOCK_METHOD(base::WeakPtr<GlicWindowController>, GetWeakPtr, (), (override));
+  MOCK_METHOD(GlicView*, GetGlicView, (), (override));
+  MOCK_METHOD(GlicWidget*, GetGlicWidget, (), (override));
+  MOCK_METHOD(content::WebContents*, GetFreWebContents, (), (override));
+  MOCK_METHOD(Browser*, attached_browser, (), (override));
+  MOCK_METHOD(State, state, (), (const, override));
+  MOCK_METHOD(GlicFreController*, fre_controller, (), (override));
+  MOCK_METHOD(GlicWindowAnimator*, window_animator, (), (override));
+  MOCK_METHOD(Profile*, profile, (), (override));
+  MOCK_METHOD(bool, IsDragging, (), (override));
+  MOCK_METHOD(void, ShowGlicModal, (std::u16string), (override));
+  MOCK_METHOD(gfx::Rect, GetInitialBounds, (Browser*), (override));
+  MOCK_METHOD(void, ShowDetachedForTesting, (), (override));
+  MOCK_METHOD(void, SetPreviousPositionForTesting, (gfx::Point), (override));
+};
+}  // namespace glic
+
+#endif  // CHROME_BROWSER_GLIC_TEST_SUPPORT_MOCK_GLIC_WINDOW_CONTROLLER_H_
diff --git a/chrome/browser/glic/widget/glic_widget.cc b/chrome/browser/glic/widget/glic_widget.cc
index 9b541a92..bd99a36 100644
--- a/chrome/browser/glic/widget/glic_widget.cc
+++ b/chrome/browser/glic/widget/glic_widget.cc
@@ -45,7 +45,7 @@
   gfx::Outsets outsets;
 #if BUILDFLAG(IS_WIN)
   RECT bounds_rect = bounds.ToRECT();
-  int frame_thickness = ui::GetResizeFrameOnlyThickness(
+  int frame_thickness = ui::GetFrameThickness(
       MonitorFromRect(&bounds_rect, MONITOR_DEFAULTTONEAREST));
   // On Windows, the presence of a frame means that we need to adjust both the
   // width and height of the widget by 2*frame thickness, and center the content
diff --git a/chrome/browser/glic/widget/glic_window_controller.h b/chrome/browser/glic/widget/glic_window_controller.h
index f388e2c3..ad9d3a2d 100644
--- a/chrome/browser/glic/widget/glic_window_controller.h
+++ b/chrome/browser/glic/widget/glic_window_controller.h
@@ -25,37 +25,32 @@
 #include "chrome/browser/glic/widget/local_hotkey_manager.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
+#include "chrome/common/chrome_features.h"
 #include "content/public/browser/web_contents.h"
 #include "ui/base/interaction/element_tracker.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_observer.h"
 
 class Browser;
-class WindowFinder;
 namespace gfx {
 class Size;
 class Point;
 }  // namespace gfx
 
 namespace glic {
-
 // Distance the detached window should be from the top and the right of the
 // display when opened unassociated to a browser.
 inline constexpr static int kDefaultDetachedTopRightDistance = 48;
 
 DECLARE_CUSTOM_ELEMENT_EVENT_TYPE(kGlicWidgetAttached);
 
-class GlicEnabling;
 class GlicWidget;
 class GlicKeyedService;
 class GlicView;
 class GlicWindowAnimator;
-class ScopedGlicButtonIndicator;
 class GlicFreController;
-class GlicButton;
 class Host;
 enum class AttachChangeReason;
-class GlicModalManager;
 
 // This class owns and manages the glic window. This class has the same lifetime
 // as the GlicKeyedService, so it exists if and only if the profile exists.
@@ -63,9 +58,7 @@
 // See the |State| enum below for the lifecycle of the window. When the glic
 // window is open |attached_browser_| indicates if the window is attached or
 // standalone. See |IsAttached|
-class GlicWindowController : public views::WidgetObserver,
-                             public Host::Observer,
-                             public Host::Delegate {
+class GlicWindowController : public Host::Delegate {
  public:
   // Observes the state of the glic window.
   class StateObserver : public base::CheckedObserver {
@@ -76,160 +69,141 @@
 
   GlicWindowController(const GlicWindowController&) = delete;
   GlicWindowController& operator=(const GlicWindowController&) = delete;
-
-  GlicWindowController(Profile* profile,
-                       signin::IdentityManager* identity_manager,
-                       GlicKeyedService* service,
-                       GlicEnabling* enabling);
-  ~GlicWindowController() override;
+  GlicWindowController() = default;
+  ~GlicWindowController() = default;
 
   // Show, summon, or activate the panel if needed, or close it if it's already
   // active and prevent_close is false.
-  void Toggle(BrowserWindowInterface* browser,
-              bool prevent_close,
-              mojom::InvocationSource source);
+  virtual void Toggle(BrowserWindowInterface* browser,
+                      bool prevent_close,
+                      mojom::InvocationSource source) = 0;
 
   // If the panel is opened, but sign-in is required, we provide a sign-in
   // button which closes the panel. This is called after the user signs in to
   // open the panel again.
-  void ShowAfterSignIn(base::WeakPtr<Browser> browser);
+  virtual void ShowAfterSignIn(base::WeakPtr<Browser> browser) = 0;
 
   // Handle Toggle when AlwaysDetached is true.
-  void ToggleWhenNotAlwaysDetached(Browser* new_attached_browser,
-                                   bool prevent_close,
-                                   mojom::InvocationSource source);
+  virtual void ToggleWhenNotAlwaysDetached(Browser* new_attached_browser,
+                                           bool prevent_close,
+                                           mojom::InvocationSource source) = 0;
 
-  void FocusIfOpen();
+  virtual void FocusIfOpen() = 0;
 
   // Attaches glic to the last focused Chrome window.
-  void Attach();
+  virtual void Attach() = 0;
 
   // Detaches glic if attached and moves it to the top right of the current
   // display.
-  void Detach();
+  virtual void Detach() = 0;
 
   // Destroy the glic panel and its web contents.
-  void Shutdown();
+  virtual void Shutdown() = 0;
 
   // Sets the size of the glic window to the specified dimensions. Callback runs
   // when the animation finishes or is destroyed, or soon if the window
   // doesn't exist yet. In this last case `size` will be used for the initial
   // size when creating the widget later.
-  void Resize(const gfx::Size& size,
-              base::TimeDelta duration,
-              base::OnceClosure callback);
+  virtual void Resize(const gfx::Size& size,
+                      base::TimeDelta duration,
+                      base::OnceClosure callback) = 0;
 
   // Allows the user to manually resize the widget by dragging. If the widget
   // hasn't been created yet, apply this setting when it is created. No effect
   // if the widget doesn't exist or the feature flag is disabled.
-  void EnableDragResize(bool enabled);
+  virtual void EnableDragResize(bool enabled) = 0;
 
   // Returns the current size of the glic window.
-  gfx::Size GetSize();
+  virtual gfx::Size GetSize() = 0;
 
   // Sets the areas of the view from which it should be draggable.
-  void SetDraggableAreas(const std::vector<gfx::Rect>& draggable_areas);
+  virtual void SetDraggableAreas(
+      const std::vector<gfx::Rect>& draggable_areas) = 0;
 
   // Sets the minimum widget size that the widget will allow the user to resize
   // to.
-  void SetMinimumWidgetSize(const gfx::Size& size);
+  virtual void SetMinimumWidgetSize(const gfx::Size& size) = 0;
 
   // Close the panel but keep the glic WebContents alive in the background.
-  void Close();
+  virtual void Close() = 0;
 
   // Used when the native window is closed directly.
-  void CloseWithReason(views::Widget::ClosedReason reason);
-
-  // Sets the audio ducking status.  Returns true if the operation succeeded.
-  bool SetAudioDucking(bool enabled);
+  virtual void CloseWithReason(views::Widget::ClosedReason reason) = 0;
 
   // Displays a context menu when the user right clicks on the title bar.
   // This is probably Windows only.
-  void ShowTitleBarContextMenuAt(gfx::Point event_loc);
+  virtual void ShowTitleBarContextMenuAt(gfx::Point event_loc) = 0;
 
   // Returns true if the mouse has been dragged more than a minimum distance
   // from `initial_press_loc`, so a mouse down followed by a move of less than
   // the minimum number of pixels doesn't start a window drag.
-  bool ShouldStartDrag(const gfx::Point& initial_press_loc,
-                       const gfx::Point& mouse_location);
+  virtual bool ShouldStartDrag(const gfx::Point& initial_press_loc,
+                               const gfx::Point& mouse_location) = 0;
 
   // Drags the glic window following the current mouse location with the given
   // `mouse_offset` and checks if the glic window is at a position where it
   // could attach to a browser window when a drag ends.
-  void HandleWindowDragWithOffset(gfx::Vector2d mouse_offset);
+  virtual void HandleWindowDragWithOffset(gfx::Vector2d mouse_offset) = 0;
 
   // Host::Delegate implementation.
-  const mojom::PanelState& GetPanelState() const override;
+  const mojom::PanelState& GetPanelState() const override = 0;
 
-  void AddStateObserver(StateObserver* observer);
-  void RemoveStateObserver(StateObserver* observer);
+  virtual void AddStateObserver(StateObserver* observer) = 0;
+  virtual void RemoveStateObserver(StateObserver* observer) = 0;
 
   // Returns whether the views::Widget associated with the glic window is active
   // (e.g. will receive keyboard events).
-  bool IsActive();
+  virtual bool IsActive() = 0;
 
   // Returns true if the state is anything other than kClosed.
-  // Virtual for testing.
-  virtual bool IsShowing() const;
+  virtual bool IsShowing() const = 0;
 
   // Returns true if either the glic panel or the FRE are showing.
-  virtual bool IsPanelOrFreShowing() const;
+  virtual bool IsPanelOrFreShowing() const = 0;
 
   // Returns whether or not the glic window is currently attached to a browser.
   // Virtual for testing.
-  virtual bool IsAttached() const;
+  virtual bool IsAttached() const = 0;
 
   // Returns wehether or not the glic window is currently showing detached.
-  bool IsDetached() const;
+  virtual bool IsDetached() const = 0;
 
   using WindowActivationChangedCallback =
       base::RepeatingCallback<void(bool active)>;
 
   // Registers |callback| to be called whenever the window activation changes.
-  base::CallbackListSubscription AddWindowActivationChangedCallback(
-      WindowActivationChangedCallback callback);
+  virtual base::CallbackListSubscription AddWindowActivationChangedCallback(
+      WindowActivationChangedCallback callback) = 0;
 
   // Warms the glic web contents.
-  void Preload();
+  virtual void Preload() = 0;
 
   // Warms the fre web contents.
-  void PreloadFre();
+  virtual void PreloadFre() = 0;
 
   // Reloads the glic web contents or the FRE's web contents (depending on
   // which is currently visible).
-  void Reload();
+  virtual void Reload() = 0;
 
   // Returns whether or not the glic web contents are loaded (this can also be
   // true if `IsActive()` (i.e., if the contents are loaded in the glic window).
-  bool IsWarmed() const;
+  virtual bool IsWarmed() const = 0;
 
   // Returns a WeakPtr to this instance. It can be destroyed at any time if the
   // profile is deleted or if the browser shuts down.
-  base::WeakPtr<GlicWindowController> GetWeakPtr();
+  virtual base::WeakPtr<GlicWindowController> GetWeakPtr() = 0;
 
-  // views::WidgetObserver implementation, monitoring the glic window widget.
-  void OnWidgetActivationChanged(views::Widget* widget, bool active) override;
-  void OnWidgetDestroyed(views::Widget* widget) override;
-  void OnWidgetBoundsChanged(views::Widget* widget,
-                             const gfx::Rect& new_bounds) override;
-  void OnWidgetUserResizeStarted() override;
-  void OnWidgetUserResizeEnded() override;
-
-  GlicView* GetGlicView();
-
-  // Called when the programmatic resize has finished. Public for use by
-  // GlicWindowResizeAnimation.
-  void ResizeFinished();
+  virtual GlicView* GetGlicView() = 0;
 
   // Returns the widget that backs the glic window.
-  GlicWidget* GetGlicWidget();
+  virtual GlicWidget* GetGlicWidget() = 0;
 
   // Returns the WebContents used for the first-run experience, or nullptr if
   // none.
-  content::WebContents* GetFreWebContents();
+  virtual content::WebContents* GetFreWebContents() = 0;
 
   // Return the Browser to which the panel is attached, or null if detached.
-  Browser* attached_browser() { return attached_browser_; }
+  virtual Browser* attached_browser() = 0;
 
   // Possible states for the glic window. Public for testing.
   //   * Closed (aka hidden, invisible)
@@ -249,242 +223,27 @@
     kClosingToReopenDetached,
     kCloseAnimation,
   };
-  State state() const { return state_; }
+  virtual State state() const = 0;
 
-  void ShowDetachedForTesting();
+  virtual GlicFreController* fre_controller() = 0;
 
-  GlicFreController* fre_controller() { return fre_controller_.get(); }
+  virtual GlicWindowAnimator* window_animator() = 0;
 
-  GlicWindowAnimator* window_animator() { return glic_window_animator_.get(); }
+  virtual Profile* profile() = 0;
 
-  Profile* profile() { return profile_; }
+  virtual bool IsDragging() = 0;
+
+  virtual void ShowGlicModal(std::u16string label) = 0;
+
+  virtual gfx::Rect GetInitialBounds(Browser* browser) = 0;
+
+  virtual void ShowDetachedForTesting() = 0;
+  virtual void SetPreviousPositionForTesting(gfx::Point position) = 0;
 
   // Helper function to get the always detached flag.
-  static bool AlwaysDetached();
-
-  bool IsDragging() { return in_move_loop_; }
-
-  void ShowGlicModal(std::u16string label);
-
- private:
-  FRIEND_TEST_ALL_PREFIXES(GlicWindowControllerUiTest, TestInitialBounds);
-  FRIEND_TEST_ALL_PREFIXES(GlicWindowControllerWithPreviousPostionUiTest,
-                           TestInitialBounds);
-
-  Host& host() const;
-
-  // Sets the floating attributes of the glic window.
-  //
-  // When set to true, the glic window is set to have a `kFloatingWindow`
-  // z-order, and on the Mac is set to be "activation independent" (to allow the
-  // user to interact with it without causing Chromium to be activated), and
-  // visible on every space (including fullscreen ones).
-  //
-  // When set to false, the glic window is set to have a `kNormal` z-order, and
-  // on the Mac, all special activation and visibility properties are cleared.
-  void SetGlicWindowToFloatingMode(bool floating);
-
-  gfx::Rect GetInitialBounds(Browser* browser);
-
-  // Return the default detached bounds which are just below the tab strip
-  // button on the active browser.
-  std::optional<gfx::Rect> GetInitialDetachedBoundsFromBrowser(
-      Browser* browser,
-      const gfx::Size& target_size);
-
-  // Return the default detached bounds when there is no active browser. The
-  // position is relative to the top right of the current display.
-  gfx::Rect GetInitialDetachedBoundsNoBrowser(const gfx::Size& target_size);
-
-  // Return the default bounds when attached to the browser which cover the tab
-  // strip button on the active browser.
-  gfx::Rect GetInitialAttachedBounds(Browser& browser);
-
-  // Creates the glic view, waits for the web client to initialize, and then
-  // shows the glic window. If `browser` is non-nullptr then glic will be
-  // attached to the browser. Otherwise glic will be detached.
-  void Show(Browser* browser, mojom::InvocationSource source);
-
-  // Close the widget and reopen in detached mode.
-  void CloseAndReopenDetached(mojom::InvocationSource source);
-
-  void SetupGlicWidget(Browser* browser);
-  void SetupGlicWidgetAccessibilityText();
-
-  // Host::Observer implementation.
-  void WebClientInitializeFailed() override;
-  void LoginPageCommitted() override;
-  void ClientReadyToShow(const mojom::OpenPanelInfo& open_info) override;
-
-  // Called once glic is completely loaded and any animations have finished.
-  // This is the end of the opening process and |state_| will be set to kOpen.
-  void GlicLoadedAndReadyToDisplay();
-
-  void SetDraggingAreasAndWatchForMouseEvents();
-
-  // Internal closing implementation. reopen_detached_source must be set
-  // if and only if the internal state is kClosingToReopenDetached.
-  void CloseInternal(
-      std::optional<mojom::InvocationSource> reopen_detached_source);
-
-  // Finishes closing off the widget after running the closing animation.
-  void CloseFinish(
-      bool reopen_detached,
-      std::optional<mojom::InvocationSource> reopen_detached_source);
-
-  // Called when the Detach() animation ends.
-  void DetachFinished();
-
-  // Causes an immediate close (eg, for during shutdown).
-  void ForceClose();
-
-  // Save the top-right corner position for re-opening.
-  void SaveWidgetPosition();
-
-  // Clear the previous position if the widget would not be on an existing
-  // display when shown.
-  void MaybeResetPreviousPosition(const gfx::Size& target_size);
-
-  // Determines the correct position for the glic window when attached to a
-  // browser window. The top right of the widget should be placed here.
-  gfx::Point GetTopRightPositionForAttachedGlicWindow(GlicButton* glic_button);
-
-  // Runs an animation to move glic to its target position.
-  // TODO(crbug.com/410629338): Reimplement attachment.
-  void AttachToBrowser(Browser& browser, AttachChangeReason reason);
-
-  // Keep part of glic window within the visible region.
-  void AdjustPositionIfNeeded();
-
-  // Handles end-of-drag:
-  //  - If glic is within attachment distance of a browser window's glic button,
-  //    attach the glic window to the button's position.
-  //  - If glic is still detached and has moved to a display with a different
-  //    work area size, possibly resize the window.
-  void OnDragComplete();
-
-  // Finds a browser within attachment distance of glic to toggle the attachment
-  // indicator.
-  void HandleGlicButtonIndicator();
-
-  // Find and return a browser within attachment distance. Returns nullptr if no
-  // browsers are within attachment distance.
-  Browser* FindBrowserForAttachment();
-
-  // Updates the position of the glic window to that of the glic button of
-  // `browser`'s window. This position change is animated if `animate` is true.
-  void MovePositionToBrowserGlicButton(const Browser& browser, bool animate);
-
-  // Called when the move animation finishes when attaching.
-  void AttachAnimationFinished();
-
-  // This method should be called anytime:
-  //  * state_ transitions to or from kClosed.
-  //  * attached_browser_ changes.
-  void NotifyIfPanelStateChanged();
-  mojom::PanelState ComputePanelState() const;
-
-  // When the attached browser is closed, this is invoked so we can clean up.
-  void AttachedBrowserDidClose(BrowserWindowInterface* browser);
-
-  // Returns true if a browser is occluded at point in screen coordinates.
-  bool IsBrowserOccludedAtPoint(Browser* browser, gfx::Point point);
-
-  // Return the last size Resize() was called with, or the default initial size
-  // if Resize() hasn't been called. The return value is clamped to fit between
-  // the minimum and maximum sizes.
-  gfx::Size GetLastRequestedSizeClamped() const;
-
-  // Possibly adjusts the size of the window appropriate for the current
-  // display workspace, but only if it's different than the current target size.
-  void MaybeAdjustSizeForDisplay(bool animate);
-
-  // Modifies `state_` to the given new state.
-  void SetWindowState(State new_state);
-
-  // Returns true of the window is showing and the content is loaded.
-  bool IsWindowOpenAndReady();
-
-  // Observes the glic widget.
-  base::ScopedObservation<views::Widget, views::WidgetObserver>
-      glic_widget_observation_{this};
-
-  // Used for observing closing of the pinned browser.
-  std::optional<base::CallbackListSubscription> browser_close_subscription_;
-
-  // List of callbacks to be notified when window activation has changed.
-  base::RepeatingCallbackList<void(bool)> window_activation_callback_list_;
-
-  const raw_ptr<Profile> profile_;
-
-  // Contains the glic webview.
-  std::unique_ptr<GlicWidget> glic_widget_;
-
-  std::unique_ptr<GlicWindowAnimator> glic_window_animator_;
-
-  // True if we've hit a login page (and have not yet shown).
-  bool login_page_committed_ = false;
-
-  // This member contains the last size that glic requested. This should be
-  // reset every time glic is closed but is currently cached.
-  std::optional<gfx::Size> glic_size_;
-
-  // Whether the widget should be user resizable, kept here in case it's
-  // specified before the widget is created.
-  bool user_resizable_ = true;
-
-  // Used to monitor key and mouse events from native window.
-  class WindowEventObserver;
-  std::unique_ptr<WindowEventObserver> window_event_observer_;
-
-  // True while RunMoveLoop() has been called on a widget.
-  bool in_move_loop_ = false;
-
-  // This is the last panel state sent to observers. It should only be updated
-  // in `NotifyIfPanelStateChanged`.
-  mojom::PanelState panel_state_;
-
-  raw_ptr<GlicWebClientAccess> web_client_;
-
-  // Modified only by calling `SetWindowState`.
-  State state_ = State::kClosed;
-
-  // If State != kClosed, then the UI must either be associated with a browser
-  // window, or standalone. That is tracked by this member.
-  raw_ptr<Browser> attached_browser_ = nullptr;
-
-  base::ObserverList<StateObserver> state_observers_;
-
-  // The announcement should happen the first time focus is lost after the FRE.
-  bool do_focus_loss_announcement_ = false;
-
-  // Whether the user is currently drag-resizing the widget.
-  bool user_resizing_ = false;
-
-  // The invocation source requesting the opening of the web client. Note that
-  // this value is retained until it is consumed by the web client. Because
-  // opening the glic window may not actually load the client, there's no
-  // guarantee that this value is sent to the web client.
-  std::optional<mojom::InvocationSource> opening_source_;
-
-  std::optional<gfx::Point> previous_position_ = std::nullopt;
-
-  std::unique_ptr<ScopedGlicButtonIndicator> scoped_glic_button_indicator_;
-
-  std::unique_ptr<GlicFreController> fre_controller_;
-
-  std::unique_ptr<WindowFinder> window_finder_;
-
-  std::unique_ptr<GlicModalManager> glic_modal_manager_;
-
-  std::unique_ptr<LocalHotkeyManager> application_hotkey_manager_;
-  std::unique_ptr<LocalHotkeyManager> glic_window_hotkey_manager_;
-
-  raw_ptr<GlicKeyedService> glic_service_;  // Owns this.
-  raw_ptr<GlicEnabling> enabling_;
-  base::ScopedObservation<Host, Host::Observer> host_observation_{this};
-
-  base::WeakPtrFactory<GlicWindowController> weak_ptr_factory_{this};
+  static bool AlwaysDetached() {
+    return base::FeatureList::IsEnabled(features::kGlicDetached);
+  }
 };
 
 }  // namespace glic
diff --git a/chrome/browser/glic/widget/glic_window_controller.cc b/chrome/browser/glic/widget/glic_window_controller_impl.cc
similarity index 86%
rename from chrome/browser/glic/widget/glic_window_controller.cc
rename to chrome/browser/glic/widget/glic_window_controller_impl.cc
index 54b8d25..d32eb26 100644
--- a/chrome/browser/glic/widget/glic_window_controller.cc
+++ b/chrome/browser/glic/widget/glic_window_controller_impl.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 "chrome/browser/glic/widget/glic_window_controller.h"
+#include "chrome/browser/glic/widget/glic_window_controller_impl.h"
 
 #include <algorithm>
 
@@ -168,7 +168,7 @@
 }  // namespace
 
 // Helper class for observing mouse and key events from native window.
-class GlicWindowController::WindowEventObserver : public ui::EventObserver {
+class GlicWindowControllerImpl::WindowEventObserver : public ui::EventObserver {
  public:
   WindowEventObserver(glic::GlicWindowController* glic_window_controller,
                       glic::GlicView* glic_view)
@@ -287,7 +287,7 @@
   gfx::Point initial_press_loc_;
 };
 
-GlicWindowController::GlicWindowController(
+GlicWindowControllerImpl::GlicWindowControllerImpl(
     Profile* profile,
     signin::IdentityManager* identity_manager,
     GlicKeyedService* glic_service,
@@ -304,9 +304,9 @@
   host_observation_.Observe(&glic_service_->host());
 }
 
-GlicWindowController::~GlicWindowController() = default;
+GlicWindowControllerImpl::~GlicWindowControllerImpl() = default;
 
-void GlicWindowController::WebClientInitializeFailed() {
+void GlicWindowControllerImpl::WebClientInitializeFailed() {
   if (state_ == State::kWaitingForGlicToLoad) {
     // TODO(crbug.com/388328847): The web client failed to initialize. Decide
     // what the fallback behavior is. Additionally, we probably need some kind
@@ -319,7 +319,7 @@
   }
 }
 
-void GlicWindowController::LoginPageCommitted() {
+void GlicWindowControllerImpl::LoginPageCommitted() {
   login_page_committed_ = true;
   if (state_ == State::kWaitingForGlicToLoad && !host().IsReady()) {
     // TODO(crbug.com/388328847): Temporarily allow showing the UI when a login
@@ -330,8 +330,8 @@
 }
 
 // Monitoring the glic widget.
-void GlicWindowController::OnWidgetActivationChanged(views::Widget* widget,
-                                                     bool active) {
+void GlicWindowControllerImpl::OnWidgetActivationChanged(views::Widget* widget,
+                                                         bool active) {
   if (GetGlicWidget() != widget) {
     return;
   }
@@ -350,20 +350,21 @@
 }
 
 // Monitoring the glic widget.
-void GlicWindowController::OnWidgetDestroyed(views::Widget* widget) {
+void GlicWindowControllerImpl::OnWidgetDestroyed(views::Widget* widget) {
   // This is used to handle the case where the native window is closed
   // directly (e.g., Windows context menu close on the title bar).
   // Conceptually this should synchronously call Close(), but the Widget
   // implementation currently does not support this.
   if (GetGlicWidget() == widget) {
     base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
-        FROM_HERE, base::BindOnce(&GlicWindowController::Close,
+        FROM_HERE, base::BindOnce(&GlicWindowControllerImpl::Close,
                                   weak_ptr_factory_.GetWeakPtr()));
   }
 }
 
-void GlicWindowController::OnWidgetBoundsChanged(views::Widget* widget,
-                                                 const gfx::Rect& new_bounds) {
+void GlicWindowControllerImpl::OnWidgetBoundsChanged(
+    views::Widget* widget,
+    const gfx::Rect& new_bounds) {
   if (in_move_loop_ && !AlwaysDetached()) {
     // While in a move loop, look for nearby browsers to toggle the drop to
     // attach indicator.
@@ -371,7 +372,7 @@
   }
 }
 
-void GlicWindowController::OnWidgetUserResizeStarted() {
+void GlicWindowControllerImpl::OnWidgetUserResizeStarted() {
   user_resizing_ = true;
   glic_service_->metrics()->OnWidgetUserResizeStarted();
   if (GlicWebClientAccess* client = host().GetPrimaryWebClient()) {
@@ -379,7 +380,7 @@
   }
 }
 
-void GlicWindowController::OnWidgetUserResizeEnded() {
+void GlicWindowControllerImpl::OnWidgetUserResizeEnded() {
   glic_service_->metrics()->OnWidgetUserResizeEnded();
   if (GlicWebClientAccess* client = host().GetPrimaryWebClient()) {
     client->ManualResizeChanged(false);
@@ -397,16 +398,16 @@
   user_resizing_ = false;
 }
 
-void GlicWindowController::ShowAfterSignIn(base::WeakPtr<Browser> browser) {
+void GlicWindowControllerImpl::ShowAfterSignIn(base::WeakPtr<Browser> browser) {
   Toggle(browser.get(), true,
          // Prefer the source that triggered the sign-in, but if that's not
          // available, report it as coming from the sign-in flow.
          opening_source_.value_or(mojom::InvocationSource::kAfterSignIn));
 }
 
-void GlicWindowController::Toggle(BrowserWindowInterface* bwi,
-                                  bool prevent_close,
-                                  mojom::InvocationSource source) {
+void GlicWindowControllerImpl::Toggle(BrowserWindowInterface* bwi,
+                                      bool prevent_close,
+                                      mojom::InvocationSource source) {
   // If `bwi` is non-null, the glic button was clicked on a specific window and
   // glic should be attached to that window. Otherwise glic was invoked from the
   // hotkey or other OS-level entrypoint.
@@ -479,7 +480,7 @@
   }
 }
 
-void GlicWindowController::ToggleWhenNotAlwaysDetached(
+void GlicWindowControllerImpl::ToggleWhenNotAlwaysDetached(
     Browser* new_attached_browser,
     bool prevent_close,
     mojom::InvocationSource source) {
@@ -566,30 +567,36 @@
   }
 }
 
-void GlicWindowController::FocusIfOpen() {
+void GlicWindowControllerImpl::FocusIfOpen() {
   if (IsShowing() && !IsActive()) {
     GetGlicWidget()->Activate();
     GetGlicView()->web_view()->GetWebContents()->Focus();
   }
 }
 
-void GlicWindowController::ShowDetachedForTesting() {
+void GlicWindowControllerImpl::ShowDetachedForTesting() {
   glic::GlicProfileManager::GetInstance()->SetActiveGlic(glic_service_);
   Show(nullptr, mojom::InvocationSource::kOsHotkey);
 }
 
-Host& GlicWindowController::host() const {
+void GlicWindowControllerImpl::SetPreviousPositionForTesting(
+    gfx::Point position) {
+  previous_position_ = position;
+}
+
+Host& GlicWindowControllerImpl::host() const {
   return glic_service_->host();
 }
 
-void GlicWindowController::Show(Browser* browser,
-                                mojom::InvocationSource source) {
+void GlicWindowControllerImpl::Show(Browser* browser,
+                                    mojom::InvocationSource source) {
   // At this point State must be kClosed, and all glic window state must be
   // unset.
   CHECK(!attached_browser_);
   opening_source_ = source;
   if (!glic_service_->GetAuthController().CheckAuthBeforeShowSync(
-          base::BindOnce(&GlicWindowController::ShowAfterSignIn, GetWeakPtr(),
+          base::BindOnce(&GlicWindowControllerImpl::ShowAfterSignIn,
+                         weak_ptr_factory_.GetWeakPtr(),
                          browser ? browser->AsWeakPtr() : nullptr))) {
     return;
   }
@@ -626,16 +633,11 @@
   glic_service_->metrics()->OnGlicWindowShown();
 }
 
-// static
-bool GlicWindowController::AlwaysDetached() {
-  return base::FeatureList::IsEnabled(features::kGlicDetached);
-}
-
-void GlicWindowController::ShowGlicModal(std::u16string label) {
+void GlicWindowControllerImpl::ShowGlicModal(std::u16string label) {
   glic_modal_manager_->ShowModal(std::move(label), glic_widget_.get());
 }
 
-void GlicWindowController::SetupGlicWidget(Browser* browser) {
+void GlicWindowControllerImpl::SetupGlicWidget(Browser* browser) {
   auto initial_bounds = GetInitialBounds(browser);
   glic_window_hotkey_manager_ = MakeGlicWindowHotkeyManager(GetWeakPtr());
   glic_widget_ = GlicWidget::Create(profile_, initial_bounds,
@@ -668,13 +670,13 @@
   // bug where the window position was not restored after closing with the
   // context menu close menu item.
   GetGlicWidget()->MakeCloseSynchronous(base::BindOnce(
-      &GlicWindowController::CloseWithReason, base::Unretained(this)));
+      &GlicWindowControllerImpl::CloseWithReason, base::Unretained(this)));
 
   // Immediately hook up the WebView to the WebContents.
   GetGlicView()->SetWebContents(host().webui_contents());
 }
 
-void GlicWindowController::SetupGlicWidgetAccessibilityText() {
+void GlicWindowControllerImpl::SetupGlicWidgetAccessibilityText() {
   auto* widget_delegate = glic_widget_->widget_delegate();
   if (opening_source_ == mojom::InvocationSource::kFre) {
     widget_delegate->SetAccessibleTitle(
@@ -689,7 +691,7 @@
   }
 }
 
-void GlicWindowController::SetGlicWindowToFloatingMode(bool floating) {
+void GlicWindowControllerImpl::SetGlicWindowToFloatingMode(bool floating) {
   GetGlicWidget()->SetZOrderLevel(floating ? ui::ZOrderLevel::kFloatingWindow
                                            : ui::ZOrderLevel::kNormal);
 #if BUILDFLAG(IS_MAC)
@@ -699,7 +701,7 @@
 #endif
 }
 
-gfx::Rect GlicWindowController::GetInitialBounds(Browser* browser) {
+gfx::Rect GlicWindowControllerImpl::GetInitialBounds(Browser* browser) {
   if (browser && !AlwaysDetached()) {
     return GetInitialAttachedBounds(*browser);
   }
@@ -721,7 +723,7 @@
       GetInitialDetachedBoundsNoBrowser(target_size));
 }
 
-gfx::Rect GlicWindowController::GetInitialDetachedBoundsNoBrowser(
+gfx::Rect GlicWindowControllerImpl::GetInitialDetachedBoundsNoBrowser(
     const gfx::Size& target_size) {
   // Get the default position offset equal distances from the top right corner
   // of the work area (which excludes system UI such as the taskbar).
@@ -733,7 +735,7 @@
   return {{initial_x, initial_y}, target_size};
 }
 
-gfx::Rect GlicWindowController::GetInitialAttachedBounds(Browser& browser) {
+gfx::Rect GlicWindowControllerImpl::GetInitialAttachedBounds(Browser& browser) {
   GlicButton* glic_button = GetGlicButton(browser);
   CHECK(glic_button);
 
@@ -754,7 +756,7 @@
 }
 
 std::optional<gfx::Rect>
-GlicWindowController::GetInitialDetachedBoundsFromBrowser(
+GlicWindowControllerImpl::GetInitialDetachedBoundsFromBrowser(
     Browser* browser,
     const gfx::Size& target_size) {
   if (!browser) {
@@ -776,7 +778,7 @@
                                          : std::nullopt;
 }
 
-void GlicWindowController::ClientReadyToShow(
+void GlicWindowControllerImpl::ClientReadyToShow(
     const mojom::OpenPanelInfo& open_info) {
   DVLOG(1) << "Glic client ready to show " << open_info.web_client_mode;
   glic_service_->metrics()->set_starting_mode(open_info.web_client_mode);
@@ -791,7 +793,7 @@
   }
 }
 
-void GlicWindowController::GlicLoadedAndReadyToDisplay() {
+void GlicWindowControllerImpl::GlicLoadedAndReadyToDisplay() {
   login_page_committed_ = false;
   if (state_ == State::kClosed || state_ == State::kOpen) {
     return;
@@ -817,7 +819,7 @@
   NotifyIfPanelStateChanged();
 }
 
-void GlicWindowController::SetDraggingAreasAndWatchForMouseEvents() {
+void GlicWindowControllerImpl::SetDraggingAreasAndWatchForMouseEvents() {
   if (window_event_observer_) {
     return;
   }
@@ -830,34 +832,34 @@
       {{0, 0, GetGlicView()->width(), GlicWidget::GetInitialSize().height()}});
 }
 
-GlicView* GlicWindowController::GetGlicView() {
+GlicView* GlicWindowControllerImpl::GetGlicView() {
   if (!GetGlicWidget()) {
     return nullptr;
   }
   return static_cast<GlicView*>(GetGlicWidget()->GetContentsView());
 }
 
-GlicWidget* GlicWindowController::GetGlicWidget() {
+GlicWidget* GlicWindowControllerImpl::GetGlicWidget() {
   return glic_widget_.get();
 }
 
-content::WebContents* GlicWindowController::GetFreWebContents() {
+content::WebContents* GlicWindowControllerImpl::GetFreWebContents() {
   return fre_controller_->GetWebContents();
 }
 
-gfx::Point GlicWindowController::GetTopRightPositionForAttachedGlicWindow(
+gfx::Point GlicWindowControllerImpl::GetTopRightPositionForAttachedGlicWindow(
     GlicButton* glic_button) {
   // The widget should be placed so its top right corner matches the visible top
   // right corner of the glic button.
   return glic_button->GetBoundsWithInset().top_right();
 }
 
-void GlicWindowController::AttachedBrowserDidClose(
+void GlicWindowControllerImpl::AttachedBrowserDidClose(
     BrowserWindowInterface* browser) {
   ForceClose();
 }
 
-void GlicWindowController::Attach() {
+void GlicWindowControllerImpl::Attach() {
   if (!GetGlicWidget()) {
     return;
   }
@@ -873,7 +875,7 @@
   AttachToBrowser(*browser, AttachChangeReason::kMenu);
 }
 
-void GlicWindowController::Detach() {
+void GlicWindowControllerImpl::Detach() {
   if (state_ != State::kOpen || !attached_browser_) {
     return;
   }
@@ -888,15 +890,16 @@
 
   glic_window_animator_->AnimatePosition(
       new_position, kAnimationDuration,
-      base::BindOnce(&GlicWindowController::DetachFinished, GetWeakPtr()));
+      base::BindOnce(&GlicWindowControllerImpl::DetachFinished,
+                     weak_ptr_factory_.GetWeakPtr()));
 }
 
-void GlicWindowController::DetachFinished() {
+void GlicWindowControllerImpl::DetachFinished() {
   SetWindowState(State::kOpen);
 }
 
-void GlicWindowController::AttachToBrowser(Browser& browser,
-                                           AttachChangeReason reason) {
+void GlicWindowControllerImpl::AttachToBrowser(Browser& browser,
+                                               AttachChangeReason reason) {
   CHECK(!AlwaysDetached());
   CHECK(GetGlicWidget());
   attached_browser_ = &browser;
@@ -915,7 +918,7 @@
   SetGlicWindowToFloatingMode(false);
 
   browser_close_subscription_ = browser.RegisterBrowserDidClose(
-      base::BindRepeating(&GlicWindowController::AttachedBrowserDidClose,
+      base::BindRepeating(&GlicWindowControllerImpl::AttachedBrowserDidClose,
                           base::Unretained(this)));
 
   // Trigger custom event for testing.
@@ -923,9 +926,9 @@
       kGlicWidgetAttached, GetGlicButton(browser));
 }
 
-void GlicWindowController::Resize(const gfx::Size& size,
-                                  base::TimeDelta duration,
-                                  base::OnceClosure callback) {
+void GlicWindowControllerImpl::Resize(const gfx::Size& size,
+                                      base::TimeDelta duration,
+                                      base::OnceClosure callback) {
   glic_size_ = size;
   glic_service_->metrics()->OnGlicWindowResize();
 
@@ -950,7 +953,7 @@
   }
 }
 
-void GlicWindowController::EnableDragResize(bool enabled) {
+void GlicWindowControllerImpl::EnableDragResize(bool enabled) {
   user_resizable_ = enabled;
   if (!GetGlicWidget()) {
     return;
@@ -968,7 +971,7 @@
   }
 }
 
-gfx::Size GlicWindowController::GetSize() {
+gfx::Size GlicWindowControllerImpl::GetSize() {
   if (!GetGlicWidget()) {
     return gfx::Size();
   }
@@ -976,7 +979,7 @@
   return GetGlicWidget()->GetSize();
 }
 
-void GlicWindowController::SetDraggableAreas(
+void GlicWindowControllerImpl::SetDraggableAreas(
     const std::vector<gfx::Rect>& draggable_areas) {
   GlicView* glic_view = GetGlicView();
   if (!glic_view) {
@@ -986,7 +989,7 @@
   glic_view->SetDraggableAreas(draggable_areas);
 }
 
-void GlicWindowController::SetMinimumWidgetSize(const gfx::Size& size) {
+void GlicWindowControllerImpl::SetMinimumWidgetSize(const gfx::Size& size) {
   if (!GetGlicWidget()) {
     return;
   }
@@ -994,15 +997,16 @@
   glic_widget_->SetMinimumSize(size);
 }
 
-void GlicWindowController::CloseWithReason(views::Widget::ClosedReason reason) {
+void GlicWindowControllerImpl::CloseWithReason(
+    views::Widget::ClosedReason reason) {
   Close();
 }
 
-void GlicWindowController::Close() {
-  GlicWindowController::CloseInternal(std::nullopt);
+void GlicWindowControllerImpl::Close() {
+  GlicWindowControllerImpl::CloseInternal(std::nullopt);
 }
 
-void GlicWindowController::CloseInternal(
+void GlicWindowControllerImpl::CloseInternal(
     std::optional<mojom::InvocationSource> reopen_detached_source) {
   if (state_ == State::kCloseAnimation || state_ == State::kClosed) {
     return;
@@ -1023,15 +1027,15 @@
     SetWindowState(State::kCloseAnimation);
     GlicButton* glic_button = GetGlicButton(*attached_browser_);
     glic_window_animator_->RunCloseAnimation(
-        glic_button,
-        base::BindOnce(&GlicWindowController::CloseFinish, GetWeakPtr(),
-                       reopen_detached, reopen_detached_source));
+        glic_button, base::BindOnce(&GlicWindowControllerImpl::CloseFinish,
+                                    weak_ptr_factory_.GetWeakPtr(),
+                                    reopen_detached, reopen_detached_source));
   } else {
     CloseFinish(reopen_detached, reopen_detached_source);
   }
 }
 
-void GlicWindowController::CloseFinish(
+void GlicWindowControllerImpl::CloseFinish(
     bool reopen_detached,
     std::optional<mojom::InvocationSource> reopen_detached_source) {
   if (state_ == State::kClosed) {
@@ -1060,12 +1064,12 @@
   }
 }
 
-void GlicWindowController::ForceClose() {
+void GlicWindowControllerImpl::ForceClose() {
   CloseFinish(/*reopen_detached=*/false,
               /*reopen_detached_source=*/std::nullopt);
 }
 
-void GlicWindowController::CloseAndReopenDetached(
+void GlicWindowControllerImpl::CloseAndReopenDetached(
     mojom::InvocationSource source) {
   if (state_ != State::kOpen) {
     return;
@@ -1075,7 +1079,7 @@
   CloseInternal(source);
 }
 
-void GlicWindowController::SaveWidgetPosition() {
+void GlicWindowControllerImpl::SaveWidgetPosition() {
   if (GetGlicWidget() && GetGlicWidget()->IsVisible()) {
     previous_position_ = GetGlicWidget()->GetWindowBoundsInScreen().origin();
     profile_->GetPrefs()->SetInteger(prefs::kGlicPreviousPositionX,
@@ -1085,7 +1089,7 @@
   }
 }
 
-void GlicWindowController::ShowTitleBarContextMenuAt(gfx::Point event_loc) {
+void GlicWindowControllerImpl::ShowTitleBarContextMenuAt(gfx::Point event_loc) {
 #if BUILDFLAG(IS_WIN)
   views::View::ConvertPointToScreen(GetGlicView(), &event_loc);
   event_loc = display::win::GetScreenWin()->DIPToScreenPoint(event_loc);
@@ -1094,8 +1098,9 @@
 #endif  // BUILDFLAG(IS_WIN)
 }
 
-bool GlicWindowController::ShouldStartDrag(const gfx::Point& initial_press_loc,
-                                           const gfx::Point& mouse_location) {
+bool GlicWindowControllerImpl::ShouldStartDrag(
+    const gfx::Point& initial_press_loc,
+    const gfx::Point& mouse_location) {
   // Determine if the mouse has moved beyond a minimum elasticity distance
   // in any direction from the starting point.
   static const int kMinimumDragDistance = 10;
@@ -1105,7 +1110,7 @@
               pow(static_cast<float>(y_offset), 2)) > kMinimumDragDistance;
 }
 
-void GlicWindowController::HandleWindowDragWithOffset(
+void GlicWindowControllerImpl::HandleWindowDragWithOffset(
     gfx::Vector2d mouse_offset) {
   // This code isn't set up to handle nested run loops. Nested run loops will
   // lead to crashes.
@@ -1140,11 +1145,11 @@
   }
 }
 
-const mojom::PanelState& GlicWindowController::GetPanelState() const {
+const mojom::PanelState& GlicWindowControllerImpl::GetPanelState() const {
   return panel_state_;
 }
 
-void GlicWindowController::AdjustPositionIfNeeded() {
+void GlicWindowControllerImpl::AdjustPositionIfNeeded() {
   // Always have at least `kMinimumVisible` px visible from glic window in
   // both vertical and horizontal directions.
   constexpr int kMinimumVisible = 40;
@@ -1162,7 +1167,7 @@
   GetGlicWidget()->SetBounds(rect);
 }
 
-void GlicWindowController::OnDragComplete() {
+void GlicWindowControllerImpl::OnDragComplete() {
   Browser* browser = FindBrowserForAttachment();
   // No browser within attachment range.
   if (!browser) {
@@ -1173,7 +1178,7 @@
   AttachToBrowser(*browser, AttachChangeReason::kDrag);
 }
 
-void GlicWindowController::HandleGlicButtonIndicator() {
+void GlicWindowControllerImpl::HandleGlicButtonIndicator() {
   Browser* browser = FindBrowserForAttachment();
   // No browser within attachment range so reset indicators
   if (!browser) {
@@ -1191,7 +1196,7 @@
   }
 }
 
-Browser* GlicWindowController::FindBrowserForAttachment() {
+Browser* GlicWindowControllerImpl::FindBrowserForAttachment() {
   // The profile must have started off as Glic enabled since a Glic widget is
   // open but it may have been disabled at runtime by policy. In this edge-case,
   // prevent reattaching back to a window (as it no longer has a GlicButton).
@@ -1241,7 +1246,7 @@
   return nullptr;
 }
 
-void GlicWindowController::MovePositionToBrowserGlicButton(
+void GlicWindowControllerImpl::MovePositionToBrowserGlicButton(
     const Browser& browser,
     bool animate) {
   if (!GetGlicWidget()) {
@@ -1290,15 +1295,15 @@
   NotifyIfPanelStateChanged();
 }
 
-void GlicWindowController::AddStateObserver(StateObserver* observer) {
+void GlicWindowControllerImpl::AddStateObserver(StateObserver* observer) {
   state_observers_.AddObserver(observer);
 }
 
-void GlicWindowController::RemoveStateObserver(StateObserver* observer) {
+void GlicWindowControllerImpl::RemoveStateObserver(StateObserver* observer) {
   state_observers_.RemoveObserver(observer);
 }
 
-void GlicWindowController::NotifyIfPanelStateChanged() {
+void GlicWindowControllerImpl::NotifyIfPanelStateChanged() {
   auto new_state = ComputePanelState();
   if (new_state != panel_state_) {
     panel_state_ = new_state;
@@ -1307,50 +1312,50 @@
   }
 }
 
-mojom::PanelState GlicWindowController::ComputePanelState() const {
+mojom::PanelState GlicWindowControllerImpl::ComputePanelState() const {
   return CreatePanelState(IsShowing(), attached_browser_);
 }
 
-bool GlicWindowController::IsActive() {
+bool GlicWindowControllerImpl::IsActive() {
   return IsShowing() && GetGlicWidget() && GetGlicWidget()->IsActive();
 }
 
-bool GlicWindowController::IsShowing() const {
+bool GlicWindowControllerImpl::IsShowing() const {
   return !(state_ == State::kClosed || state_ == State::kCloseAnimation);
 }
 
-bool GlicWindowController::IsPanelOrFreShowing() const {
+bool GlicWindowControllerImpl::IsPanelOrFreShowing() const {
   return IsShowing() || fre_controller_->IsShowingDialog();
 }
 
-bool GlicWindowController::IsAttached() const {
+bool GlicWindowControllerImpl::IsAttached() const {
   return attached_browser_ != nullptr;
 }
 
-bool GlicWindowController::IsDetached() const {
+bool GlicWindowControllerImpl::IsDetached() const {
   return IsShowing() && !IsAttached();
 }
 
 base::CallbackListSubscription
-GlicWindowController::AddWindowActivationChangedCallback(
+GlicWindowControllerImpl::AddWindowActivationChangedCallback(
     WindowActivationChangedCallback callback) {
   return window_activation_callback_list_.Add(std::move(callback));
 }
 
-void GlicWindowController::Preload() {
+void GlicWindowControllerImpl::Preload() {
   if (!host().contents_container()) {
     host().CreateContents();
     host().webui_contents()->Resize(GetInitialBounds(nullptr));
   }
 }
 
-void GlicWindowController::PreloadFre() {
+void GlicWindowControllerImpl::PreloadFre() {
   if (fre_controller_->ShouldShowFreDialog()) {
     fre_controller_->TryPreload();
   }
 }
 
-void GlicWindowController::Reload() {
+void GlicWindowControllerImpl::Reload() {
   if (GetFreWebContents()) {
     GetFreWebContents()->ReloadFocusedFrame();
   }
@@ -1360,23 +1365,23 @@
   }
 }
 
-bool GlicWindowController::IsWarmed() const {
+bool GlicWindowControllerImpl::IsWarmed() const {
   return !!host().contents_container();
 }
 
-base::WeakPtr<GlicWindowController> GlicWindowController::GetWeakPtr() {
+base::WeakPtr<GlicWindowController> GlicWindowControllerImpl::GetWeakPtr() {
   return weak_ptr_factory_.GetWeakPtr();
 }
 
-void GlicWindowController::Shutdown() {
+void GlicWindowControllerImpl::Shutdown() {
   // Hide first, then clean up (but do not animate).
   ForceClose();
   fre_controller_->Shutdown();
   window_activation_callback_list_.Notify(false);
 }
 
-bool GlicWindowController::IsBrowserOccludedAtPoint(Browser* browser,
-                                                    gfx::Point point) {
+bool GlicWindowControllerImpl::IsBrowserOccludedAtPoint(Browser* browser,
+                                                        gfx::Point point) {
   std::set<gfx::NativeWindow> exclude = {
       GetGlicView()->GetWidget()->GetNativeWindow()};
   gfx::NativeWindow window =
@@ -1387,7 +1392,7 @@
   return false;
 }
 
-gfx::Size GlicWindowController::GetLastRequestedSizeClamped() const {
+gfx::Size GlicWindowControllerImpl::GetLastRequestedSizeClamped() const {
   gfx::Size min = GlicWidget::GetInitialSize();
   if (glic_widget_) {
     gfx::Size widget_min = glic_widget_->GetMinimumSize();
@@ -1404,7 +1409,7 @@
   return result;
 }
 
-void GlicWindowController::MaybeAdjustSizeForDisplay(bool animate) {
+void GlicWindowControllerImpl::MaybeAdjustSizeForDisplay(bool animate) {
   if (state_ == State::kOpen || state_ == State::kWaitingForGlicToLoad ||
       state_ == State::kDetaching) {
     const auto target_size = GetLastRequestedSizeClamped();
@@ -1416,7 +1421,7 @@
   }
 }
 
-void GlicWindowController::SetWindowState(State new_state) {
+void GlicWindowControllerImpl::SetWindowState(State new_state) {
   if (state_ == new_state) {
     return;
   }
@@ -1427,8 +1432,33 @@
   }
 }
 
-bool GlicWindowController::IsWindowOpenAndReady() {
+bool GlicWindowControllerImpl::IsWindowOpenAndReady() {
   return host().IsReady() && state_ == State::kOpen;
 }
 
+GlicWindowController::State GlicWindowControllerImpl::state() const {
+  return state_;
+}
+
+bool GlicWindowControllerImpl::IsDragging() {
+  return in_move_loop_;
+}
+
+Profile* GlicWindowControllerImpl::profile() {
+  return profile_;
+}
+
+GlicWindowAnimator* GlicWindowControllerImpl::window_animator() {
+  return glic_window_animator_.get();
+}
+
+GlicFreController* GlicWindowControllerImpl::fre_controller() {
+  return fre_controller_.get();
+}
+
+// Return the Browser to which the panel is attached, or null if detached.
+Browser* GlicWindowControllerImpl::attached_browser() {
+  return attached_browser_;
+}
+
 }  // namespace glic
diff --git a/chrome/browser/glic/widget/glic_window_controller_impl.h b/chrome/browser/glic/widget/glic_window_controller_impl.h
new file mode 100644
index 0000000..4084eb296
--- /dev/null
+++ b/chrome/browser/glic/widget/glic_window_controller_impl.h
@@ -0,0 +1,351 @@
+// 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_GLIC_WIDGET_GLIC_WINDOW_CONTROLLER_IMPL_H_
+#define CHROME_BROWSER_GLIC_WIDGET_GLIC_WINDOW_CONTROLLER_IMPL_H_
+
+#include <optional>
+#include <vector>
+
+#include "base/callback_list.h"
+#include "base/functional/callback_forward.h"
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "base/observer_list_types.h"
+#include "base/scoped_observation.h"
+#include "base/scoped_observation_traits.h"
+#include "chrome/browser/glic/glic_enabling.h"
+#include "chrome/browser/glic/host/glic.mojom.h"
+#include "chrome/browser/glic/host/glic_web_client_access.h"
+#include "chrome/browser/glic/host/host.h"
+#include "chrome/browser/glic/widget/application_hotkey_delegate.h"
+#include "chrome/browser/glic/widget/glic_modal_manager.h"
+#include "chrome/browser/glic/widget/glic_window_controller.h"
+#include "chrome/browser/glic/widget/glic_window_hotkey_delegate.h"
+#include "chrome/browser/glic/widget/local_hotkey_manager.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
+#include "content/public/browser/web_contents.h"
+#include "ui/base/interaction/element_tracker.h"
+#include "ui/views/widget/widget.h"
+#include "ui/views/widget/widget_observer.h"
+
+class Browser;
+class WindowFinder;
+namespace gfx {
+class Size;
+class Point;
+}  // namespace gfx
+
+namespace glic {
+
+class GlicEnabling;
+class ScopedGlicButtonIndicator;
+class GlicButton;
+class GlicModalManager;
+
+// This class owns and manages the glic window. This class has the same lifetime
+// as the GlicKeyedService, so it exists if and only if the profile exists.
+//
+// See the |State| enum below for the lifecycle of the window. When the glic
+// window is open |attached_browser_| indicates if the window is attached or
+// standalone. See |IsAttached|
+class GlicWindowControllerImpl : public GlicWindowController,
+                                 public views::WidgetObserver,
+                                 public Host::Observer {
+ public:
+  GlicWindowControllerImpl(const GlicWindowControllerImpl&) = delete;
+  GlicWindowControllerImpl& operator=(const GlicWindowControllerImpl&) = delete;
+
+  GlicWindowControllerImpl(Profile* profile,
+                           signin::IdentityManager* identity_manager,
+                           GlicKeyedService* service,
+                           GlicEnabling* enabling);
+  ~GlicWindowControllerImpl() override;
+
+  // GlicWindowController implementation
+  void Toggle(BrowserWindowInterface* browser,
+              bool prevent_close,
+              mojom::InvocationSource source) override;
+  void ShowAfterSignIn(base::WeakPtr<Browser> browser) override;
+  void ToggleWhenNotAlwaysDetached(Browser* new_attached_browser,
+                                   bool prevent_close,
+                                   mojom::InvocationSource source) override;
+  void FocusIfOpen() override;
+  void Attach() override;
+  void Detach() override;
+  void Shutdown() override;
+  void Resize(const gfx::Size& size,
+              base::TimeDelta duration,
+              base::OnceClosure callback) override;
+  void EnableDragResize(bool enabled) override;
+  gfx::Size GetSize() override;
+  void SetDraggableAreas(
+      const std::vector<gfx::Rect>& draggable_areas) override;
+  void SetMinimumWidgetSize(const gfx::Size& size) override;
+  void Close() override;
+  void CloseWithReason(views::Widget::ClosedReason reason) override;
+  void ShowTitleBarContextMenuAt(gfx::Point event_loc) override;
+  bool ShouldStartDrag(const gfx::Point& initial_press_loc,
+                       const gfx::Point& mouse_location) override;
+  void HandleWindowDragWithOffset(gfx::Vector2d mouse_offset) override;
+  const mojom::PanelState& GetPanelState() const override;
+
+  void AddStateObserver(StateObserver* observer) override;
+  void RemoveStateObserver(StateObserver* observer) override;
+
+  bool IsActive() override;
+  bool IsShowing() const override;
+  bool IsPanelOrFreShowing() const override;
+  bool IsAttached() const override;
+  bool IsDetached() const override;
+  base::CallbackListSubscription AddWindowActivationChangedCallback(
+      WindowActivationChangedCallback callback) override;
+  void Preload() override;
+  void PreloadFre() override;
+  void Reload() override;
+  bool IsWarmed() const override;
+  base::WeakPtr<GlicWindowController> GetWeakPtr() override;
+
+  GlicView* GetGlicView() override;
+  GlicWidget* GetGlicWidget() override;
+  content::WebContents* GetFreWebContents() override;
+
+  Browser* attached_browser() override;
+  State state() const override;
+  GlicFreController* fre_controller() override;
+  GlicWindowAnimator* window_animator() override;
+  Profile* profile() override;
+  bool IsDragging() override;
+  void ShowGlicModal(std::u16string label) override;
+  gfx::Rect GetInitialBounds(Browser* browser) override;
+  void ShowDetachedForTesting() override;
+  void SetPreviousPositionForTesting(gfx::Point position) override;
+
+  // views::WidgetObserver implementation, monitoring the glic window widget.
+  void OnWidgetActivationChanged(views::Widget* widget, bool active) override;
+  void OnWidgetDestroyed(views::Widget* widget) override;
+  void OnWidgetBoundsChanged(views::Widget* widget,
+                             const gfx::Rect& new_bounds) override;
+  void OnWidgetUserResizeStarted() override;
+  void OnWidgetUserResizeEnded() override;
+
+ private:
+  Host& host() const;
+
+  // Sets the floating attributes of the glic window.
+  //
+  // When set to true, the glic window is set to have a `kFloatingWindow`
+  // z-order, and on the Mac is set to be "activation independent" (to allow the
+  // user to interact with it without causing Chromium to be activated), and
+  // visible on every space (including fullscreen ones).
+  //
+  // When set to false, the glic window is set to have a `kNormal` z-order, and
+  // on the Mac, all special activation and visibility properties are cleared.
+  void SetGlicWindowToFloatingMode(bool floating);
+
+  // Return the default detached bounds which are just below the tab strip
+  // button on the active browser.
+  std::optional<gfx::Rect> GetInitialDetachedBoundsFromBrowser(
+      Browser* browser,
+      const gfx::Size& target_size);
+
+  // Return the default detached bounds when there is no active browser. The
+  // position is relative to the top right of the current display.
+  gfx::Rect GetInitialDetachedBoundsNoBrowser(const gfx::Size& target_size);
+
+  // Return the default bounds when attached to the browser which cover the tab
+  // strip button on the active browser.
+  gfx::Rect GetInitialAttachedBounds(Browser& browser);
+
+  // Creates the glic view, waits for the web client to initialize, and then
+  // shows the glic window. If `browser` is non-nullptr then glic will be
+  // attached to the browser. Otherwise glic will be detached.
+  void Show(Browser* browser, mojom::InvocationSource source);
+
+  // Close the widget and reopen in detached mode.
+  void CloseAndReopenDetached(mojom::InvocationSource source);
+
+  void SetupGlicWidget(Browser* browser);
+  void SetupGlicWidgetAccessibilityText();
+
+  // Host::Observer implementation.
+  void WebClientInitializeFailed() override;
+  void LoginPageCommitted() override;
+  void ClientReadyToShow(const mojom::OpenPanelInfo& open_info) override;
+
+  // Called once glic is completely loaded and any animations have finished.
+  // This is the end of the opening process and |state_| will be set to kOpen.
+  void GlicLoadedAndReadyToDisplay();
+
+  void SetDraggingAreasAndWatchForMouseEvents();
+
+  // Internal closing implementation. reopen_detached_source must be set
+  // if and only if the internal state is kClosingToReopenDetached.
+  void CloseInternal(
+      std::optional<mojom::InvocationSource> reopen_detached_source);
+
+  // Finishes closing off the widget after running the closing animation.
+  void CloseFinish(
+      bool reopen_detached,
+      std::optional<mojom::InvocationSource> reopen_detached_source);
+
+  // Called when the Detach() animation ends.
+  void DetachFinished();
+
+  // Causes an immediate close (eg, for during shutdown).
+  void ForceClose();
+
+  // Save the top-right corner position for re-opening.
+  void SaveWidgetPosition();
+
+  // Clear the previous position if the widget would not be on an existing
+  // display when shown.
+  void MaybeResetPreviousPosition(const gfx::Size& target_size);
+
+  // Determines the correct position for the glic window when attached to a
+  // browser window. The top right of the widget should be placed here.
+  gfx::Point GetTopRightPositionForAttachedGlicWindow(GlicButton* glic_button);
+
+  // Runs an animation to move glic to its target position.
+  // TODO(crbug.com/410629338): Reimplement attachment.
+  void AttachToBrowser(Browser& browser, AttachChangeReason reason);
+
+  // Keep part of glic window within the visible region.
+  void AdjustPositionIfNeeded();
+
+  // Handles end-of-drag:
+  //  - If glic is within attachment distance of a browser window's glic button,
+  //    attach the glic window to the button's position.
+  //  - If glic is still detached and has moved to a display with a different
+  //    work area size, possibly resize the window.
+  void OnDragComplete();
+
+  // Finds a browser within attachment distance of glic to toggle the attachment
+  // indicator.
+  void HandleGlicButtonIndicator();
+
+  // Find and return a browser within attachment distance. Returns nullptr if no
+  // browsers are within attachment distance.
+  Browser* FindBrowserForAttachment();
+
+  // Updates the position of the glic window to that of the glic button of
+  // `browser`'s window. This position change is animated if `animate` is true.
+  void MovePositionToBrowserGlicButton(const Browser& browser, bool animate);
+
+  // Called when the move animation finishes when attaching.
+  void AttachAnimationFinished();
+
+  // This method should be called anytime:
+  //  * state_ transitions to or from kClosed.
+  //  * attached_browser_ changes.
+  void NotifyIfPanelStateChanged();
+  mojom::PanelState ComputePanelState() const;
+
+  // When the attached browser is closed, this is invoked so we can clean up.
+  void AttachedBrowserDidClose(BrowserWindowInterface* browser);
+
+  // Returns true if a browser is occluded at point in screen coordinates.
+  bool IsBrowserOccludedAtPoint(Browser* browser, gfx::Point point);
+
+  // Return the last size Resize() was called with, or the default initial size
+  // if Resize() hasn't been called. The return value is clamped to fit between
+  // the minimum and maximum sizes.
+  gfx::Size GetLastRequestedSizeClamped() const;
+
+  // Possibly adjusts the size of the window appropriate for the current
+  // display workspace, but only if it's different than the current target size.
+  void MaybeAdjustSizeForDisplay(bool animate);
+
+  // Modifies `state_` to the given new state.
+  void SetWindowState(State new_state);
+
+  // Returns true of the window is showing and the content is loaded.
+  bool IsWindowOpenAndReady();
+
+  // Observes the glic widget.
+  base::ScopedObservation<views::Widget, views::WidgetObserver>
+      glic_widget_observation_{this};
+
+  // Used for observing closing of the pinned browser.
+  std::optional<base::CallbackListSubscription> browser_close_subscription_;
+
+  // List of callbacks to be notified when window activation has changed.
+  base::RepeatingCallbackList<void(bool)> window_activation_callback_list_;
+
+  const raw_ptr<Profile> profile_;
+
+  // Contains the glic webview.
+  std::unique_ptr<GlicWidget> glic_widget_;
+
+  std::unique_ptr<GlicWindowAnimator> glic_window_animator_;
+
+  // True if we've hit a login page (and have not yet shown).
+  bool login_page_committed_ = false;
+
+  // This member contains the last size that glic requested. This should be
+  // reset every time glic is closed but is currently cached.
+  std::optional<gfx::Size> glic_size_;
+
+  // Whether the widget should be user resizable, kept here in case it's
+  // specified before the widget is created.
+  bool user_resizable_ = true;
+
+  // Used to monitor key and mouse events from native window.
+  class WindowEventObserver;
+  std::unique_ptr<WindowEventObserver> window_event_observer_;
+
+  // True while RunMoveLoop() has been called on a widget.
+  bool in_move_loop_ = false;
+
+  // This is the last panel state sent to observers. It should only be updated
+  // in `NotifyIfPanelStateChanged`.
+  mojom::PanelState panel_state_;
+
+  raw_ptr<GlicWebClientAccess> web_client_;
+
+  // Modified only by calling `SetWindowState`.
+  State state_ = State::kClosed;
+
+  // If State != kClosed, then the UI must either be associated with a browser
+  // window, or standalone. That is tracked by this member.
+  raw_ptr<Browser> attached_browser_ = nullptr;
+
+  base::ObserverList<StateObserver> state_observers_;
+
+  // The announcement should happen the first time focus is lost after the FRE.
+  bool do_focus_loss_announcement_ = false;
+
+  // Whether the user is currently drag-resizing the widget.
+  bool user_resizing_ = false;
+
+  // The invocation source requesting the opening of the web client. Note that
+  // this value is retained until it is consumed by the web client. Because
+  // opening the glic window may not actually load the client, there's no
+  // guarantee that this value is sent to the web client.
+  std::optional<mojom::InvocationSource> opening_source_;
+
+  std::optional<gfx::Point> previous_position_ = std::nullopt;
+
+  std::unique_ptr<ScopedGlicButtonIndicator> scoped_glic_button_indicator_;
+
+  std::unique_ptr<GlicFreController> fre_controller_;
+
+  std::unique_ptr<WindowFinder> window_finder_;
+
+  std::unique_ptr<GlicModalManager> glic_modal_manager_;
+
+  std::unique_ptr<LocalHotkeyManager> application_hotkey_manager_;
+  std::unique_ptr<LocalHotkeyManager> glic_window_hotkey_manager_;
+
+  raw_ptr<GlicKeyedService> glic_service_;  // Owns this.
+  raw_ptr<GlicEnabling> enabling_;
+  base::ScopedObservation<Host, Host::Observer> host_observation_{this};
+
+  base::WeakPtrFactory<GlicWindowControllerImpl> weak_ptr_factory_{this};
+};
+
+}  // namespace glic
+
+#endif  // CHROME_BROWSER_GLIC_WIDGET_GLIC_WINDOW_CONTROLLER_IMPL_H_
diff --git a/chrome/browser/glic/widget/glic_window_controller_interactive_uitest.cc b/chrome/browser/glic/widget/glic_window_controller_interactive_uitest.cc
index ee3f8c41..b3f3b66 100644
--- a/chrome/browser/glic/widget/glic_window_controller_interactive_uitest.cc
+++ b/chrome/browser/glic/widget/glic_window_controller_interactive_uitest.cc
@@ -566,7 +566,7 @@
   };
 
   for (auto& t : test_points) {
-    window_controller().previous_position_ = t.test;
+    window_controller().SetPreviousPositionForTesting(t.test);
     initial_bounds = window_controller().GetInitialBounds(nullptr);
     EXPECT_EQ(initial_bounds.origin(), t.expected) << t.msg;
   }
diff --git a/chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.cc b/chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.cc
index 617b84c1..bfa5420 100644
--- a/chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.cc
+++ b/chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.cc
@@ -459,12 +459,13 @@
   const url::Origin& origin =
       web_view_guest()->owner_rfh()->GetLastCommittedOrigin();
   // chrome://glic requires additional permissions, and webview's
-  // permissionrequest API does not handle clipboard access.
+  // permissionrequest API does not handle clipboard access or screen wake lock.
   if (origin.scheme() == content::kChromeUIScheme &&
       origin.host() == chrome::kChromeUIGlicHost) {
     switch (type) {
       case ContentSettingsType::CLIPBOARD_READ_WRITE:
       case ContentSettingsType::CLIPBOARD_SANITIZED_WRITE:
+      case ContentSettingsType::WAKE_LOCK_SCREEN:
         return content::PermissionResult(
             content::PermissionStatus::GRANTED,
             content::PermissionStatusSource::UNSPECIFIED);
diff --git a/chrome/browser/headless/headless_mode_protocol_browsertest.cc b/chrome/browser/headless/headless_mode_protocol_browsertest.cc
index b8ef6dd..2ccaec9 100644
--- a/chrome/browser/headless/headless_mode_protocol_browsertest.cc
+++ b/chrome/browser/headless/headless_mode_protocol_browsertest.cc
@@ -348,6 +348,8 @@
     "--ozone-override-screen-size=1234,5678")
 #endif
 
+// --screen-info switch is only supported on Linux and Windows at this time.
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN)
 // This currently results in an unexpected screen orientation type,
 // see http://crbug.com/398150465.
 HEADLESS_MODE_PROTOCOL_TEST_WITH_COMMAND_LINE_EXTRAS(
@@ -370,4 +372,6 @@
     "sanity/create-target-secondary-screen.js",
     "--screen-info={label='#1'}{label='#2'}")
 
+#endif
+
 }  // namespace headless
diff --git a/chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/settings/AlwaysTranslateListFragment.java b/chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/settings/AlwaysTranslateListFragment.java
index d2a8332..42d6cda 100644
--- a/chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/settings/AlwaysTranslateListFragment.java
+++ b/chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/settings/AlwaysTranslateListFragment.java
@@ -69,6 +69,11 @@
         TranslateBridge.setLanguageAlwaysTranslateState(getProfile(), code, false);
     }
 
+    @Override
+    public @AnimationType int getAnimationType() {
+        return AnimationType.PROPERTY;
+    }
+
     /**
      * Helper class to populate the LanguageItem list and used by {@link LanguageItemListPreference}
      * to make the summary text and launch an Intent to this Fragment.
diff --git a/chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/settings/LanguageSettings.java b/chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/settings/LanguageSettings.java
index c376401b..374bd14 100644
--- a/chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/settings/LanguageSettings.java
+++ b/chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/settings/LanguageSettings.java
@@ -398,4 +398,9 @@
     PrefService getPrefService() {
         return UserPrefs.get(getProfile());
     }
+
+    @Override
+    public @AnimationType int getAnimationType() {
+        return AnimationType.PROPERTY;
+    }
 }
diff --git a/chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/settings/NeverTranslateListFragment.java b/chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/settings/NeverTranslateListFragment.java
index c2267bc7..4d422b6 100644
--- a/chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/settings/NeverTranslateListFragment.java
+++ b/chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/settings/NeverTranslateListFragment.java
@@ -70,6 +70,11 @@
         TranslateBridge.setLanguageBlockedState(getProfile(), code, false);
     }
 
+    @Override
+    public @AnimationType int getAnimationType() {
+        return AnimationType.PROPERTY;
+    }
+
     /**
      * Helper class to populate the LanguageItem list and used by {@link LanguageItemListPreference}
      * to make the summary text and launch an Intent to this Fragment.
diff --git a/chrome/browser/lens/core/mojom/lens_side_panel.mojom b/chrome/browser/lens/core/mojom/lens_side_panel.mojom
index 56b9e39..7fcc165 100644
--- a/chrome/browser/lens/core/mojom/lens_side_panel.mojom
+++ b/chrome/browser/lens/core/mojom/lens_side_panel.mojom
@@ -30,6 +30,9 @@
   // file:// URL being tracked on the browser. If the latest contextualized
   // local file:// URL is not open on the main tab, then it opens in a new tab.
   OnScrollToMessage(array<string> text_fragments, uint32 pdf_page_number);
+
+  // Request the browser to open the feedback dialog.
+  RequestSendFeedback();
 };
 
 // Enumerates the semantic events that can be logged by the Lens Overlay.
diff --git a/chrome/browser/metrics/k12_age_classification_metrics_provider_browsertest.cc b/chrome/browser/metrics/k12_age_classification_metrics_provider_browsertest.cc
index 56adac5f..462ebea 100644
--- a/chrome/browser/metrics/k12_age_classification_metrics_provider_browsertest.cc
+++ b/chrome/browser/metrics/k12_age_classification_metrics_provider_browsertest.cc
@@ -22,11 +22,11 @@
 #include "chrome/common/chrome_features.h"
 #include "chrome/test/base/fake_gaia_mixin.h"
 #include "chromeos/ash/components/dbus/session_manager/fake_session_manager_client.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "components/metrics/metrics_service.h"
 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
 #include "components/policy/core/common/cloud/mock_cloud_policy_store.h"
 #include "components/policy/core/common/cloud/test/policy_builder.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "components/policy/proto/device_management_backend.pb.h"
 #include "content/public/test/browser_test.h"
 
diff --git a/chrome/browser/metrics/structured/metadata_processor_ash_browsertest.cc b/chrome/browser/metrics/structured/metadata_processor_ash_browsertest.cc
index eb03ffcf..81c20744 100644
--- a/chrome/browser/metrics/structured/metadata_processor_ash_browsertest.cc
+++ b/chrome/browser/metrics/structured/metadata_processor_ash_browsertest.cc
@@ -34,6 +34,7 @@
 #include "chrome/browser/metrics/structured/test/structured_metrics_mixin.h"
 #include "chrome/test/base/fake_gaia_mixin.h"
 #include "chromeos/ash/components/dbus/session_manager/fake_session_manager_client.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "components/metrics/metrics_service.h"
 #include "components/metrics/structured/structured_events.h"
 #include "components/metrics/structured/structured_metrics_client.h"
@@ -42,7 +43,6 @@
 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
 #include "components/policy/core/common/cloud/mock_cloud_policy_store.h"
 #include "components/policy/core/common/cloud/test/policy_builder.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "components/policy/proto/device_management_backend.pb.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
diff --git a/chrome/browser/metrics/usertype_by_devicetype_metrics_provider_browsertest.cc b/chrome/browser/metrics/usertype_by_devicetype_metrics_provider_browsertest.cc
index f9dde2e9..1e863915 100644
--- a/chrome/browser/metrics/usertype_by_devicetype_metrics_provider_browsertest.cc
+++ b/chrome/browser/metrics/usertype_by_devicetype_metrics_provider_browsertest.cc
@@ -31,11 +31,11 @@
 #include "chrome/browser/browser_process_platform_part_ash.h"
 #include "chrome/test/base/fake_gaia_mixin.h"
 #include "chromeos/ash/components/dbus/session_manager/fake_session_manager_client.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "components/metrics/metrics_service.h"
 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
 #include "components/policy/core/common/cloud/mock_cloud_policy_store.h"
 #include "components/policy/core/common/cloud/test/policy_builder.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "components/policy/proto/device_management_backend.pb.h"
 #include "content/public/test/browser_test.h"
 
diff --git a/chrome/browser/ntp_customization/java/src/org/chromium/chrome/browser/ntp_customization/NtpCustomizationCoordinator.java b/chrome/browser/ntp_customization/java/src/org/chromium/chrome/browser/ntp_customization/NtpCustomizationCoordinator.java
index 5fabde8..30da11e 100644
--- a/chrome/browser/ntp_customization/java/src/org/chromium/chrome/browser/ntp_customization/NtpCustomizationCoordinator.java
+++ b/chrome/browser/ntp_customization/java/src/org/chromium/chrome/browser/ntp_customization/NtpCustomizationCoordinator.java
@@ -25,7 +25,7 @@
 import org.chromium.build.annotations.Nullable;
 import org.chromium.chrome.browser.ntp_customization.feed.FeedSettingsCoordinator;
 import org.chromium.chrome.browser.ntp_customization.ntp_cards.NtpCardsCoordinator;
-import org.chromium.chrome.browser.profiles.ProfileProvider;
+import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
@@ -44,7 +44,7 @@
     private final BottomSheetDelegate mDelegate;
 
     private final Context mContext;
-    private final Supplier<ProfileProvider> mProfileSupplier;
+    private final Supplier<Profile> mProfileSupplier;
     private NtpCustomizationMediator mMediator;
     private @MonotonicNonNull NtpCardsCoordinator mNtpCardsCoordinator;
     private @Nullable FeedSettingsCoordinator mFeedSettingsCoordinator;
@@ -70,7 +70,7 @@
     public NtpCustomizationCoordinator(
             Context context,
             BottomSheetController bottomSheetController,
-            Supplier<ProfileProvider> profileSupplier) {
+            Supplier<Profile> profileSupplier) {
         mContext = context;
         mProfileSupplier = profileSupplier;
         View contentView =
diff --git a/chrome/browser/ntp_customization/java/src/org/chromium/chrome/browser/ntp_customization/NtpCustomizationMediator.java b/chrome/browser/ntp_customization/java/src/org/chromium/chrome/browser/ntp_customization/NtpCustomizationMediator.java
index a1bf46b..969b387 100644
--- a/chrome/browser/ntp_customization/java/src/org/chromium/chrome/browser/ntp_customization/NtpCustomizationMediator.java
+++ b/chrome/browser/ntp_customization/java/src/org/chromium/chrome/browser/ntp_customization/NtpCustomizationMediator.java
@@ -24,7 +24,6 @@
 import org.chromium.chrome.browser.feed.FeedFeatures;
 import org.chromium.chrome.browser.preferences.Pref;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.profiles.ProfileProvider;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetObserver;
@@ -57,7 +56,7 @@
     private final PropertyModel mViewFlipperPropertyModel;
     private List<Integer> mListContent;
     private final PropertyModel mContainerPropertyModel;
-    private final Supplier<ProfileProvider> mProfileSupplier;
+    private final Supplier<Profile> mProfileSupplier;
     private @Nullable Profile mProfile;
     private @Nullable Integer mCurrentBottomSheet;
     private static @Nullable PrefService sPrefServiceForTest;
@@ -67,7 +66,7 @@
             NtpCustomizationBottomSheetContent bottomSheetContent,
             PropertyModel viewFlipperPropertyModel,
             PropertyModel containerPropertyModel,
-            Supplier<ProfileProvider> profileSupplier) {
+            Supplier<Profile> profileSupplier) {
         mBottomSheetController = bottomSheetController;
         mBottomSheetContent = bottomSheetContent;
         mViewFlipperPropertyModel = viewFlipperPropertyModel;
diff --git a/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/NtpCustomizationMediatorUnitTest.java b/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/NtpCustomizationMediatorUnitTest.java
index 364d25b..9a04b8f 100644
--- a/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/NtpCustomizationMediatorUnitTest.java
+++ b/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/NtpCustomizationMediatorUnitTest.java
@@ -39,7 +39,7 @@
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
-import org.chromium.base.supplier.OneshotSupplierImpl;
+import org.chromium.base.supplier.Supplier;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.HistogramWatcher;
 import org.chromium.chrome.browser.feed.FeedFeatures;
@@ -48,7 +48,6 @@
 import org.chromium.chrome.browser.ntp_customization.NtpCustomizationCoordinator.BottomSheetType;
 import org.chromium.chrome.browser.preferences.Pref;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.profiles.ProfileProvider;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetObserver;
@@ -70,20 +69,17 @@
     @Mock private PrefService mPrefService;
     @Mock private FeedServiceBridge.Natives mFeedServiceBridgeJniMock;
     @Mock private Profile mProfile;
-    @Mock private ProfileProvider mProfileProvider;
 
     private NtpCustomizationMediator mMediator;
     private Map<Integer, Integer> mViewFlipperMap;
     private ListContainerViewDelegate mListDelegate;
     private Context mContext;
-    private OneshotSupplierImpl<ProfileProvider> mSupplier;
+    private Supplier<Profile> mProfileSupplier;
 
     @Before
     public void setUp() {
         mContext = ApplicationProvider.getApplicationContext();
-        mSupplier = new OneshotSupplierImpl<>();
-        mSupplier.set(mProfileProvider);
-        when(mProfileProvider.getOriginalProfile()).thenReturn(mProfile);
+        mProfileSupplier = () -> mProfile;
         NtpCustomizationMediator.setPrefForTesting(mPrefService);
         FeedServiceBridgeJni.setInstanceForTesting(mFeedServiceBridgeJniMock);
         FeedFeatures.setFakePrefsForTest(mPrefService);
@@ -93,7 +89,7 @@
                         mBottomSheetContent,
                         mViewFlipperPropertyModel,
                         mContainerPropertyModel,
-                        mSupplier);
+                        mProfileSupplier);
         mViewFlipperMap = mMediator.getViewFlipperMapForTesting();
         mListDelegate = mMediator.createListDelegate();
     }
diff --git a/chrome/browser/page_load_metrics/observers/lcp_critical_path_predictor_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/lcp_critical_path_predictor_page_load_metrics_observer.cc
index 00b11d0..ef6237697 100644
--- a/chrome/browser/page_load_metrics/observers/lcp_critical_path_predictor_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/lcp_critical_path_predictor_page_load_metrics_observer.cc
@@ -624,6 +624,7 @@
       navigation_handle->GetLCPPNavigationHint();
   if (hint) {
     if (!hint->lcp_element_locators.empty() ||
+        !hint->lcp_element_locators_all.empty() ||
         !hint->lcp_influencer_scripts.empty() ||
         !hint->preconnect_origins.empty()) {
       is_lcpp_hinted_navigation_ = true;
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 30d3c0b..93edf4b 100644
--- a/chrome/browser/policy/system_features_disable_list_policy_handler.cc
+++ b/chrome/browser/policy/system_features_disable_list_policy_handler.cc
@@ -12,7 +12,6 @@
 #include "base/values.h"
 #include "build/build_config.h"
 #include "components/policy/core/common/policy_pref_names.h"
-#include "components/policy/core/common/system_features_disable_list_constants.h"
 #include "components/policy/policy_constants.h"
 #include "components/prefs/pref_registry_simple.h"
 
@@ -56,13 +55,6 @@
 SystemFeaturesDisableListPolicyHandler::
     ~SystemFeaturesDisableListPolicyHandler() = default;
 
-void SystemFeaturesDisableListPolicyHandler::RegisterPrefs(
-    PrefRegistrySimple* registry) {
-  registry->RegisterListPref(policy_prefs::kSystemFeaturesDisableList);
-  registry->RegisterStringPref(policy_prefs::kSystemFeaturesDisableMode,
-                               kSystemFeaturesDisableModeBlocked);
-}
-
 SystemFeature SystemFeaturesDisableListPolicyHandler::GetSystemFeatureFromAppId(
     const std::string& app_id) {
   if (app_id == ash::kCanvasAppId) {
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 34f195f..b9792f1 100644
--- a/chrome/browser/policy/system_features_disable_list_policy_handler.h
+++ b/chrome/browser/policy/system_features_disable_list_policy_handler.h
@@ -12,7 +12,6 @@
 #include "components/prefs/pref_service.h"
 
 class PrefValueMap;
-class PrefRegistrySimple;
 
 namespace policy {
 
@@ -96,7 +95,6 @@
   SystemFeaturesDisableListPolicyHandler();
   ~SystemFeaturesDisableListPolicyHandler() override;
 
-  static void RegisterPrefs(PrefRegistrySimple* registry);
   static SystemFeature GetSystemFeatureFromAppId(const std::string& app_id);
   static bool IsSystemFeatureDisabled(SystemFeature feature,
                                       PrefService* const pref_service);
diff --git a/chrome/browser/predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_util.cc b/chrome/browser/predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_util.cc
index ad692e2fa..fe1cb448 100644
--- a/chrome/browser/predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_util.cc
+++ b/chrome/browser/predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_util.cc
@@ -816,6 +816,8 @@
   std::vector<std::string> lcp_element_locators =
       PredictLcpElementLocators(lcpp_stat.lcp_element_locator_stat(),
                                 kConfidenceThreshold, kTotalFrequencyThreshold);
+  std::vector<std::string> lcp_element_locators_all =
+      PredictLcpElementLocators(lcpp_stat.lcp_element_locator_stat_all());
   std::vector<GURL> lcp_influencer_scripts =
       PredictLcpInfluencerScripts(lcpp_stat);
   std::vector<GURL> fetched_fonts = PredictFetchedFontUrls(lcpp_stat);
@@ -823,13 +825,13 @@
       PredictPreconnectableOrigins(lcpp_stat);
   std::vector<GURL> unused_preloads = PredictUnusedPreloads(lcpp_stat);
 
-  if (!lcp_element_locators.empty() || !lcp_influencer_scripts.empty() ||
-      !fetched_fonts.empty() || !preconnect_origins.empty() ||
-      !unused_preloads.empty()) {
+  if (!lcp_element_locators.empty() || !lcp_element_locators_all.empty() ||
+      !lcp_influencer_scripts.empty() || !fetched_fonts.empty() ||
+      !preconnect_origins.empty() || !unused_preloads.empty()) {
     return blink::mojom::LCPCriticalPathPredictorNavigationTimeHint(
-        std::move(lcp_element_locators), std::move(lcp_influencer_scripts),
-        std::move(fetched_fonts), std::move(preconnect_origins),
-        std::move(unused_preloads), false);
+        std::move(lcp_element_locators), std::move(lcp_element_locators_all),
+        std::move(lcp_influencer_scripts), std::move(fetched_fonts),
+        std::move(preconnect_origins), std::move(unused_preloads), false);
   }
   return std::nullopt;
 }
@@ -850,9 +852,10 @@
 
 std::vector<std::string> PredictLcpElementLocators(
     const predictors::LcpElementLocatorStat& stat,
-    const double confidence_threshold,
-    const double total_frequency_threshold) {
-  if (SumOfFrequency(stat) < total_frequency_threshold) {
+    const std::optional<double>& confidence_threshold,
+    const std::optional<double>& total_frequency_threshold) {
+  if (total_frequency_threshold &&
+      SumOfFrequency(stat) < *total_frequency_threshold) {
     return {};
   }
   std::vector<std::pair<double, std::string>>
@@ -862,7 +865,7 @@
   lcp_element_locators.reserve(lcp_element_locators_with_confidence.size());
   for (auto& [confidence, lcp_element_locator] :
        lcp_element_locators_with_confidence) {
-    if (confidence < confidence_threshold) {
+    if (confidence_threshold && confidence < *confidence_threshold) {
       break;
     }
     lcp_element_locators.push_back(std::move(lcp_element_locator));
diff --git a/chrome/browser/predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_util.h b/chrome/browser/predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_util.h
index 3063f44..9bd39946 100644
--- a/chrome/browser/predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_util.h
+++ b/chrome/browser/predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_util.h
@@ -62,8 +62,8 @@
 // data, it returns an empty vector.
 std::vector<std::string> PredictLcpElementLocators(
     const predictors::LcpElementLocatorStat& stat,
-    const double confidence_threshold,
-    const double total_frequency_threshold);
+    const std::optional<double>& confidence_threshold = std::nullopt,
+    const std::optional<double>& total_frequency_threshold = std::nullopt);
 
 // Returns possible fonts from past loads for a given `stat`.
 // The returned urls are ordered by descending frequency (the most
diff --git a/chrome/browser/predictors/loading_predictor_browsertest.cc b/chrome/browser/predictors/loading_predictor_browsertest.cc
index 82bad5d94..10e776e0 100644
--- a/chrome/browser/predictors/loading_predictor_browsertest.cc
+++ b/chrome/browser/predictors/loading_predictor_browsertest.cc
@@ -1339,7 +1339,7 @@
     NavigateAndWaitForLcpElement(kUrl,
                                  /*expected_events=*/"LCP@IMG, LCP@DIV, Onload",
                                  /*expected_lcp_count=*/2u,
-                                 /*expected_lcp_index=*/0u,  // =IMG
+                                 /*expected_lcp_index=*/1u,  // =DIV
                                  from_here);
   }
 
@@ -1359,6 +1359,40 @@
   TestPrediction();
 }
 
+class LCPPTimingPredictorBrowserFlagTest
+    : public LCPPTimingPredictorBrowserTest,
+      public ::testing::WithParamInterface<std::string> {
+ public:
+  LCPPTimingPredictorBrowserFlagTest() {
+    scoped_feature_list_.InitWithFeaturesAndParameters(
+        /*enabled_features=*/
+        {{blink::features::kLCPCriticalPathPredictor,
+          {{blink::features::kLCPCriticalPathPredictorRecordedLcpElementTypes
+                .name,
+            GetParam()}}}},
+        /*disabled_features=*/{});
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+// kLCPCriticalPathPredictorRecordedLcpElementTypes should not affect.
+// TODO(crbug.com/413192370): Flaky on win-rel.
+#if BUILDFLAG(IS_WIN)
+#define MAYBE_WithKCriticalPathFlag DISABLED_WithKCriticalPathFlag
+#else
+#define MAYBE_WithKCriticalPathFlag WithKCriticalPathFlag
+#endif
+IN_PROC_BROWSER_TEST_P(LCPPTimingPredictorBrowserFlagTest,
+                       MAYBE_WithKCriticalPathFlag) {
+  TestPrediction();
+}
+
+INSTANTIATE_TEST_SUITE_P(Flags,
+                         LCPPTimingPredictorBrowserFlagTest,
+                         ::testing::Values("all", "image_only"));
+
 class SuppressesLoadingPredictorOnSlowNetworkBrowserTest
     : public LoadingPredictorBrowserTest {
  public:
diff --git a/chrome/browser/predictors/loading_predictor_tab_helper.cc b/chrome/browser/predictors/loading_predictor_tab_helper.cc
index e18fbad..b2ee3909 100644
--- a/chrome/browser/predictors/loading_predictor_tab_helper.cc
+++ b/chrome/browser/predictors/loading_predictor_tab_helper.cc
@@ -215,7 +215,7 @@
     CHECK_IS_TEST();
     if (!hint) {
       hint = blink::mojom::LCPCriticalPathPredictorNavigationTimeHint(
-          {}, {}, {}, {}, {}, /*for_testing=*/false);
+          {}, {}, {}, {}, {}, {}, /*for_testing=*/false);
     }
     hint->for_testing = true;
   }
diff --git a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
index f61908d..dee2ddb4 100644
--- a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
+++ b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
@@ -873,12 +873,12 @@
             "org.chromium.chrome.browser.tab.TabIdManager.NEXT_ID";
 
     // Start timestamp of 1-day period for measuring the number of times the max-instance toast is
-    // shown when tab tearing fails.
-    public static final String TAB_TEARING_MAX_INSTANCES_FAILURE_START_TIME_MS =
-            "Chrome.TabTearing.MaxInstancesFailureStartTimeMs";
-    // Number of times in a day the max-instance toast is shown when tab tearing fails.
-    public static final String TAB_TEARING_MAX_INSTANCES_FAILURE_COUNT =
-            "Chrome.TabTearing.MaxInstancesFailureCount";
+    // shown when tab or group tearing fails.
+    public static final String TAB_OR_GROUP_TEARING_MAX_INSTANCES_FAILURE_START_TIME_MS =
+            "Chrome.TabOrGroupTearing.MaxInstancesFailureStartTimeMs";
+    // Number of times in a day the max-instance toast is shown when tab or group tearing fails.
+    public static final String TAB_OR_GROUP_TEARING_MAX_INSTANCES_FAILURE_COUNT =
+            "Chrome.TabOrGroupTearing.MaxInstancesFailureCount";
 
     // If the toolbar should be shown on top.
     public static final String TOOLBAR_TOP_ANCHORED = "Chrome.Toolbar.TopAnchored";
@@ -1130,8 +1130,8 @@
                 TAB_DECLUTTER_AUTO_DELETE_ENABLED,
                 TAB_DECLUTTER_AUTO_DELETE_TIME_DELTA_HOURS,
                 TAB_DECLUTTER_DIALOG_IPH_DISMISS_COUNT,
-                TAB_TEARING_MAX_INSTANCES_FAILURE_START_TIME_MS,
-                TAB_TEARING_MAX_INSTANCES_FAILURE_COUNT,
+                TAB_OR_GROUP_TEARING_MAX_INSTANCES_FAILURE_START_TIME_MS,
+                TAB_OR_GROUP_TEARING_MAX_INSTANCES_FAILURE_COUNT,
                 TOOLBAR_TOP_ANCHORED,
                 TWA_DISCLOSURE_SEEN_PACKAGES,
                 UMA_ON_POSTCREATE_COUNTER,
diff --git a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/DeprecatedChromePreferenceKeys.java b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/DeprecatedChromePreferenceKeys.java
index 67702b8..dfcdd21 100644
--- a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/DeprecatedChromePreferenceKeys.java
+++ b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/DeprecatedChromePreferenceKeys.java
@@ -70,6 +70,8 @@
                 "Chrome.Tab.ArchiveIphShowing",
                 "Chrome.Tab.ShowArchiveTabDialogIPH",
                 "Chrome.Tab.ShowTabGroupCreationDialog",
+                "Chrome.TabTearing.MaxInstancesFailureCount",
+                "Chrome.TabTearing.MaxInstancesFailureStartTimeMs",
                 "Chrome.UMA.OnPreCreateCounter",
                 "Chrome.UMA.OnResumeCounter",
                 "Chrome.VideoTutorials.ShareUrls",
diff --git a/chrome/browser/prefetch/android/java/src/org/chromium/chrome/browser/prefetch/settings/ExtendedPreloadingSettingsFragment.java b/chrome/browser/prefetch/android/java/src/org/chromium/chrome/browser/prefetch/settings/ExtendedPreloadingSettingsFragment.java
index 0fc13087..033a860 100644
--- a/chrome/browser/prefetch/android/java/src/org/chromium/chrome/browser/prefetch/settings/ExtendedPreloadingSettingsFragment.java
+++ b/chrome/browser/prefetch/android/java/src/org/chromium/chrome/browser/prefetch/settings/ExtendedPreloadingSettingsFragment.java
@@ -13,4 +13,9 @@
     protected int getPreferenceResource() {
         return R.xml.extended_preloading_preferences;
     }
+
+    @Override
+    public @AnimationType int getAnimationType() {
+        return AnimationType.PROPERTY;
+    }
 }
diff --git a/chrome/browser/prefetch/android/java/src/org/chromium/chrome/browser/prefetch/settings/PreloadPagesSettingsFragment.java b/chrome/browser/prefetch/android/java/src/org/chromium/chrome/browser/prefetch/settings/PreloadPagesSettingsFragment.java
index ad1478f..76335bf 100644
--- a/chrome/browser/prefetch/android/java/src/org/chromium/chrome/browser/prefetch/settings/PreloadPagesSettingsFragment.java
+++ b/chrome/browser/prefetch/android/java/src/org/chromium/chrome/browser/prefetch/settings/PreloadPagesSettingsFragment.java
@@ -101,4 +101,9 @@
         PreloadPagesSettingsBridge.setState(getProfile(), newState);
         return true;
     }
+
+    @Override
+    public @AnimationType int getAnimationType() {
+        return AnimationType.PROPERTY;
+    }
 }
diff --git a/chrome/browser/prefetch/android/java/src/org/chromium/chrome/browser/prefetch/settings/StandardPreloadingSettingsFragment.java b/chrome/browser/prefetch/android/java/src/org/chromium/chrome/browser/prefetch/settings/StandardPreloadingSettingsFragment.java
index cc8abd8..df675e166 100644
--- a/chrome/browser/prefetch/android/java/src/org/chromium/chrome/browser/prefetch/settings/StandardPreloadingSettingsFragment.java
+++ b/chrome/browser/prefetch/android/java/src/org/chromium/chrome/browser/prefetch/settings/StandardPreloadingSettingsFragment.java
@@ -13,4 +13,9 @@
     protected int getPreferenceResource() {
         return R.xml.standard_preloading_preferences;
     }
+
+    @Override
+    public @AnimationType int getAnimationType() {
+        return AnimationType.PROPERTY;
+    }
 }
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 93d3420d..8a39797e 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -428,7 +428,6 @@
 #include "chrome/browser/memory/oom_kills_monitor.h"
 #include "chrome/browser/metrics/chromeos_metrics_provider.h"
 #include "chrome/browser/policy/annotations/blocklist_handler.h"
-#include "chrome/browser/policy/system_features_disable_list_policy_handler.h"
 #include "chrome/browser/ui/ash/shelf/chrome_shelf_prefs.h"
 #include "chrome/browser/ui/webui/ash/login/enable_debugging_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/settings/os_settings_ui.h"
@@ -447,6 +446,7 @@
 #include "chromeos/ash/components/network/network_metadata_store.h"
 #include "chromeos/ash/components/network/proxy/proxy_config_handler.h"
 #include "chromeos/ash/components/policy/restriction_schedule/device_restriction_schedule_controller.h"
+#include "chromeos/ash/components/policy/system_features_disable_list/system_features_disable_list_policy_utils.h"
 #include "chromeos/ash/components/quickoffice/quickoffice_prefs.h"
 #include "chromeos/ash/components/report/report_controller.h"
 #include "chromeos/ash/components/scheduler_config/scheduler_configuration_manager.h"
@@ -1769,7 +1769,7 @@
   RegisterNearbySharingLocalPrefs(registry);
   chromeos::echo_offer::RegisterPrefs(registry);
   memory::OOMKillsMonitor::RegisterPrefs(registry);
-  policy::SystemFeaturesDisableListPolicyHandler::RegisterPrefs(registry);
+  policy::RegisterDisabledSystemFeaturesPrefs(registry);
   policy::DlpRulesManagerImpl::RegisterPrefs(registry);
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
diff --git a/chrome/browser/preloading/chrome_preloading.cc b/chrome/browser/preloading/chrome_preloading.cc
index 5d8645a7..a18a1bc 100644
--- a/chrome/browser/preloading/chrome_preloading.cc
+++ b/chrome/browser/preloading/chrome_preloading.cc
@@ -36,7 +36,8 @@
 bool HasCanonicalPreloadingOmniboxSearchURL(
     const GURL& preloading_url,
     content::BrowserContext* browser_context,
-    GURL* canonical_url) {
+    GURL* canonical_url,
+    std::u16string* search_terms) {
   const TemplateURLService* const template_url_service =
       GetTemplateURLServiceFromBrowserContext(browser_context);
   if (!template_url_service) {
@@ -52,7 +53,7 @@
   return default_search_provider->KeepSearchTermsInURL(
       preloading_url, template_url_service->search_terms_data(),
       /*keep_search_intent_params=*/true,
-      /*normalize_search_terms=*/true, canonical_url);
+      /*normalize_search_terms=*/true, canonical_url, search_terms);
 }
 
 bool IsSearchDestinationMatch(const GURL& canonical_preloading_search_url,
diff --git a/chrome/browser/preloading/chrome_preloading.h b/chrome/browser/preloading/chrome_preloading.h
index d882b649..155e458 100644
--- a/chrome/browser/preloading/chrome_preloading.h
+++ b/chrome/browser/preloading/chrome_preloading.h
@@ -216,13 +216,15 @@
     content::BrowserContext* browser_context,
     const GURL& url);
 
-// Returns true if a canonical URL representation of a |preloading_url| can be
-// generated. |canonical_url| is set to the canonical URL representation when
-// this method returns |true|.
+// Returns true if a canonical URL representation of a `preloading_url` can be
+// generated. `canonical_url` is set to the canonical URL representation when
+// this method returns `true`. The search query is returned in `search_terms` if
+// the passing `search_terms` is not nullptr.
 bool HasCanonicalPreloadingOmniboxSearchURL(
     const GURL& preloading_url,
     content::BrowserContext* browser_context,
-    GURL* canonical_url);
+    GURL* canonical_url,
+    std::u16string* search_terms = nullptr);
 
 // Returns true when |navigation_url| is considered as navigating to the same
 // omnibox search results page as |canonical_preloading_search_url|.
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 26a550e5..1bd7e64 100644
--- a/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_service.cc
+++ b/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_service.cc
@@ -238,18 +238,29 @@
 
 struct SearchPrefetchService::SearchPrefetchServingReasonRecorder {
  public:
-  explicit SearchPrefetchServingReasonRecorder(bool for_prerender)
-      : for_prerender_(for_prerender) {}
+  // Passing the SearchPrefetchService pointer is optional. If it is passed,
+  // the recorder will ask the service to track the search terms it in its dtor.
+  explicit SearchPrefetchServingReasonRecorder(
+      bool for_prerender,
+      SearchPrefetchService* service = nullptr)
+      : for_prerender_(for_prerender), service_(service) {}
   ~SearchPrefetchServingReasonRecorder() {
     base::UmaHistogramEnumeration(
         for_prerender_
             ? "Omnibox.SearchPrefetch.PrefetchServingReason2.Prerender"
             : "Omnibox.SearchPrefetch.PrefetchServingReason2",
         reason_);
+    if (service_) {
+      service_->RecordInterceptionMetrics(search_terms_, reason_);
+    }
   }
 
-  SearchPrefetchServingReason reason_ = SearchPrefetchServingReason::kServed;
   const bool for_prerender_ = false;
+  // A method of SearchPrefetchService holds this instance, so it is safe to
+  // refer to it with pointer.
+  raw_ptr<SearchPrefetchService> service_;
+  SearchPrefetchServingReason reason_ = SearchPrefetchServingReason::kServed;
+  std::u16string search_terms_;
 };
 
 // static
@@ -542,7 +553,12 @@
 SearchPrefetchService::TakePrefetchResponseFromMemoryCache(
     const network::ResourceRequest& tentative_resource_request) {
   const GURL& navigation_url = tentative_resource_request.url;
-  SearchPrefetchServingReasonRecorder recorder(/*for_prerender=*/false);
+  SearchPrefetchServingReasonRecorder recorder(
+      /*for_prerender=*/false,
+      // Not to track back/forward style navigation.
+      tentative_resource_request.load_flags & net::LOAD_SKIP_CACHE_VALIDATION
+          ? nullptr
+          : this);
 
   auto iter =
       RetrieveSearchTermsInMemoryCache(tentative_resource_request, recorder);
@@ -756,20 +772,13 @@
   }
 
   GURL canonical_search_url;
+  std::u16string search_terms;
   if (!HasCanonicalPreloadingOmniboxSearchURL(match.destination_url, profile_,
-                                              &canonical_search_url)) {
+                                              &canonical_search_url,
+                                              &search_terms)) {
     return false;
   }
-
-  // Parse the search terms from the match URL to verify this is a valid search
-  // query.
-  std::u16string search_terms;
-  template_url_service->GetDefaultSearchProvider()->ExtractSearchTermsFromURL(
-      match.destination_url, template_url_service->search_terms_data(),
-      &search_terms);
-
-  if (search_terms.size() == 0)
-    return false;
+  CHECK(!search_terms.empty());
 
   // Search history suggestions (those that are not also server suggestions)
   // don't have search term args. If search history suggestions are enabled,
@@ -1083,14 +1092,16 @@
   }
 
   GURL canonical_search_url;
-  if (!HasCanonicalPreloadingOmniboxSearchURL(navigation_url, profile_,
-                                              &canonical_search_url) ||
-      !IsSearchDestinationMatch(canonical_search_url, profile_,
-                                navigation_url)) {
+  std::u16string search_terms;
+  if (!HasCanonicalPreloadingOmniboxSearchURL(
+          navigation_url, profile_, &canonical_search_url, &search_terms)) {
     recorder.reason_ = SearchPrefetchServingReason::kNotDefaultSearchWithTerms;
     return prefetches_.end();
   }
-
+  // `HasCanonicalPreloadingOmniboxSearchURL` should return false if
+  // search_terms is empty.
+  CHECK(!search_terms.empty());
+  recorder.search_terms_ = search_terms;
   const auto& iter = prefetches_.find(canonical_search_url);
 
   // Return early if there is no prefetch found before checking for other
@@ -1248,3 +1259,36 @@
 void SearchPrefetchService::DeletePreloadedDictionaries() {
   preloaded_shared_dictionaries_handle_.reset();
 }
+
+void SearchPrefetchService::RecordInterceptionMetrics(
+    const std::u16string& search_terms,
+    SearchPrefetchServingReason serving_status) {
+  // Do not track empty search terms.
+  if (search_terms.empty()) {
+    return;
+  }
+  switch (serving_status) {
+    // Do not track non-DSE navigations.
+    case SearchPrefetchServingReason::kSearchEngineNotValid:
+    case SearchPrefetchServingReason::kJavascriptDisabled:
+    case SearchPrefetchServingReason::kNotDefaultSearchWithTerms:
+      return;
+    case SearchPrefetchServingReason::kServed:
+    case SearchPrefetchServingReason::kNoPrefetch:
+    case SearchPrefetchServingReason::kPrefetchWasForDifferentOrigin:
+    case SearchPrefetchServingReason::kRequestFailed:
+    case SearchPrefetchServingReason::kNotServedOtherReason:
+    case SearchPrefetchServingReason::kPostReloadFormOrLink:
+      break;
+    case SearchPrefetchServingReason::kRequestInFlightNotReady:
+      NOTREACHED();
+  }
+  auto iter = search_terms_cache_.Get(search_terms);
+  if (iter != search_terms_cache_.end()) {
+    base::UmaHistogramCustomTimes(
+        "Omnibox.SearchPrefetch.DuplicateSearchTermsAge",
+        base::Time::Now() - iter->second, base::Milliseconds(1),
+        base::Hours(10), 100);
+  }
+  search_terms_cache_.Put(search_terms, base::Time::Now());
+}
diff --git a/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_service.h b/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_service.h
index 9bb7faae..6f2ca1aa 100644
--- a/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_service.h
+++ b/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_service.h
@@ -10,6 +10,7 @@
 #include <optional>
 #include <utility>
 
+#include "base/containers/lru_cache.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/scoped_observation.h"
@@ -271,6 +272,9 @@
   void MaybePreloadDictionary(const AutocompleteResult& result);
   void DeletePreloadedDictionaries();
 
+  void RecordInterceptionMetrics(const std::u16string& search_terms,
+                                 SearchPrefetchServingReason serving_status);
+
   // Prefetches that are started are stored using search terms as a key. Only
   // one prefetch should be started for a given search term until the old
   // prefetch expires.
@@ -295,6 +299,8 @@
   // serving time of the response.
   std::map<GURL, std::pair<GURL, base::Time>> prefetch_cache_;
 
+  base::LRUCache<std::u16string, base::Time> search_terms_cache_{50};
+
   mojo::PendingRemote<network::mojom::PreloadedSharedDictionaryInfoHandle>
       preloaded_shared_dictionaries_handle_;
   base::OneShotTimer preloaded_shared_dictionaries_expiry_timer_;
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 21b7a2b..23cef01 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
@@ -1327,6 +1327,35 @@
   }
 }
 
+IN_PROC_BROWSER_TEST_F(SearchPrefetchServiceEnabledBrowserTest,
+                       DuplicateSearchTermMetricsAreRecorded) {
+  base::HistogramTester histogram_tester;
+  auto* search_prefetch_service =
+      SearchPrefetchServiceFactory::GetForProfile(browser()->profile());
+  EXPECT_NE(nullptr, search_prefetch_service);
+
+  std::string search_terms = "prefetch_content";
+
+  auto [prefetch_url, search_url] =
+      GetSearchPrefetchAndNonPrefetch(search_terms);
+  GURL canonical_search_url = GetCanonicalSearchURL(prefetch_url);
+
+  EXPECT_TRUE(search_prefetch_service->MaybePrefetchURL(prefetch_url,
+                                                        GetWebContents()));
+  histogram_tester.ExpectUniqueSample(
+      "Omnibox.SearchPrefetch.PrefetchEligibilityReason2.SuggestionPrefetch",
+      SearchPrefetchEligibilityReason::kPrefetchStarted, 1);
+  WaitUntilStatusChangesTo(canonical_search_url,
+                           SearchPrefetchStatus::kComplete);
+
+  ASSERT_TRUE(content::NavigateToURL(GetWebContents(), search_url));
+  histogram_tester.ExpectTotalCount(
+      "Omnibox.SearchPrefetch.DuplicateSearchTermsAge", 0);
+  ASSERT_TRUE(content::NavigateToURL(GetWebContents(), search_url));
+  histogram_tester.ExpectTotalCount(
+      "Omnibox.SearchPrefetch.DuplicateSearchTermsAge", 1);
+}
+
 // Tests used for integrating to No-Vary-Search Disk Cache.
 class SearchPrefetchServiceEnabledWithNVSBrowserTest
     : public testing::WithParamInterface<bool>,
@@ -1456,6 +1485,8 @@
   histogram_tester.ExpectTotalCount(
       "Omnibox.SearchPrefetch.NavigationInterceptedToForwardingComplete",
       expected_count);
+  histogram_tester.ExpectTotalCount(
+        "Omnibox.SearchPrefetch.DuplicateSearchTermsAge", 0);
 }
 
 IN_PROC_BROWSER_TEST_P(SearchPrefetchServiceEnabledWithNVSBrowserTest,
diff --git a/chrome/browser/preloading/search_preload/search_preload_browsertest.cc b/chrome/browser/preloading/search_preload/search_preload_browsertest.cc
index ffcfd9d..8f48a1f0 100644
--- a/chrome/browser/preloading/search_preload/search_preload_browsertest.cc
+++ b/chrome/browser/preloading/search_preload/search_preload_browsertest.cc
@@ -9,6 +9,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.h"
+#include "chrome/browser/preloading/chrome_preloading.h"
 #include "chrome/browser/preloading/prefetch/search_prefetch/search_preload_test_response_utils.h"
 #include "chrome/browser/preloading/search_preload/search_preload_features.h"
 #include "chrome/browser/preloading/search_preload/search_preload_service.h"
@@ -19,8 +20,10 @@
 #include "chrome/test/base/search_test_utils.h"
 #include "components/omnibox/browser/autocomplete_input.h"
 #include "components/omnibox/browser/autocomplete_result.h"
+#include "components/omnibox/browser/omnibox.mojom.h"
 #include "components/search_engines/template_url_data.h"
 #include "components/search_engines/template_url_service.h"
+#include "content/public/browser/preload_pipeline_info.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/content_features.h"
 #include "content/public/test/browser_test.h"
@@ -100,7 +103,14 @@
     scoped_feature_list_.InitWithFeaturesAndParameters(
         {
             {features::kPrefetchPrerenderIntegration, {}},
-            {features::kDsePreload2, {}},
+            {
+                features::kDsePreload2,
+                {
+                    {"kDsePreload2PredictorMouseDown", "true"},
+                    {"kDsePreload2PredictorUpOrDownArrowButton", "true"},
+                    {"kDsePreload2PredictorTouchDown", "true"},
+                },
+            },
         },
         /*disabled_features=*/{});
 
@@ -136,13 +146,15 @@
         base::BindRepeating(&SearchPreloadBrowserTest::HandleSearchRequest,
                             base::Unretained(this)));
     ASSERT_TRUE(https_server_->Start());
+  }
 
-    // Set up `TemplateURLService`.
+  void SetUpTemplateURLService(bool prefetch_likely_navigations = true) {
     TemplateURLService* model =
         TemplateURLServiceFactory::GetForProfile(&GetProfile());
     ASSERT_TRUE(model);
     search_test_utils::WaitForTemplateURLServiceToLoad(model);
     ASSERT_TRUE(model->loaded());
+
     TemplateURLData data;
     data.SetShortName(kSearchDomain16);
     data.SetKeyword(data.short_name());
@@ -154,6 +166,8 @@
                     .spec());
     data.suggestions_url =
         https_server_->GetURL(kSearchDomain, "/?q={searchTerms}").spec();
+    data.prefetch_likely_navigations = prefetch_likely_navigations;
+
     TemplateURL* template_url = model->Add(std::make_unique<TemplateURL>(data));
     ASSERT_TRUE(template_url);
     model->SetUserSelectedDefaultSearchProvider(template_url);
@@ -184,31 +198,35 @@
     kReal,
     // For URLs that will be used for prefetch requests.
     kPrefetch,
+    // For URLs that will be used for prefetch requests for
+    // `OnNavigationLikely()`.
+    kPrefetchOnNavigationLikely,
     // For URLs that will be used for prerender requests.
     kPrerender
   };
 
   GURL GetSearchUrl(const std::string& search_terms, UrlType url_type) {
-    // $1: the search terms that will be retrieved.
-    // $2: parameter for prefetch request. Should be &pf=cs if the url is
-    // expected to declare itself as a prefetch request. Otherwise it should be
-    // an empty string.
-    std::string url_template = "/search_page.html?q=$1$2&type=test";
-    bool attach_prefetch_flag;
+    const char* pf;
     switch (url_type) {
       case UrlType::kReal:
       case UrlType::kPrerender:
-        attach_prefetch_flag = false;
+        pf = "";
         break;
       case UrlType::kPrefetch:
-        attach_prefetch_flag = true;
+        pf = "&pf=cs";
+        break;
+      case UrlType::kPrefetchOnNavigationLikely:
+        pf = "&pf=op";
         break;
     }
-    return https_server_->GetURL(
-        kSearchDomain,
-        base::ReplaceStringPlaceholders(
-            url_template, {search_terms, attach_prefetch_flag ? "&pf=cs" : ""},
-            nullptr));
+    std::string path = base::StrCat({
+        "/search_page.html",
+        "?q=",
+        search_terms,
+        pf,
+        "&type=test",
+    });
+    return https_server_->GetURL(kSearchDomain, path);
   }
 
   void ChangeAutocompleteResult(const std::string& original_query,
@@ -309,6 +327,8 @@
 // - Prefetch is used.
 IN_PROC_BROWSER_TEST_F(SearchPreloadBrowserTest,
                        OnAutocompleteResultChanged_TriggersPrefetch) {
+  SetUpTemplateURLService();
+
   ASSERT_TRUE(content::NavigateToURL(
       &GetWebContents(), embedded_test_server()->GetURL("/empty.html")));
 
@@ -358,6 +378,8 @@
 // - Prefetch is used.
 IN_PROC_BROWSER_TEST_F(SearchPreloadBrowserTest,
                        OnAutocompleteResultChanged_TriggeredPrefetchIsHeld) {
+  SetUpTemplateURLService();
+
   ASSERT_TRUE(content::NavigateToURL(
       &GetWebContents(), embedded_test_server()->GetURL("/empty.html")));
 
@@ -412,6 +434,8 @@
 IN_PROC_BROWSER_TEST_F(
     SearchPreloadBrowserTest,
     OnAutocompleteResultChanged_TriggersPrefetchAndPrerender) {
+  SetUpTemplateURLService();
+
   ASSERT_TRUE(content::NavigateToURL(
       &GetWebContents(), embedded_test_server()->GetURL("/empty.html")));
 
@@ -472,6 +496,8 @@
 IN_PROC_BROWSER_TEST_F(
     SearchPreloadBrowserTest,
     OnAutocompleteResultChanged_TriggersPrefetchThenPrerender) {
+  SetUpTemplateURLService();
+
   ASSERT_TRUE(content::NavigateToURL(
       &GetWebContents(), embedded_test_server()->GetURL("/empty.html")));
 
@@ -525,4 +551,132 @@
       alternative_content::PrerenderFinalStatus::kActivated, 1);
 }
 
+// Scenario:
+//
+// - A user inputs "he".
+// - A user clicks a suggestion "hello".
+// - `SearchPreloadService` starts prefetch with query "?q=hello&pf=op...".
+// - A user navigates to a page with query "?q=hello&..."
+// - Prefetch is used.
+IN_PROC_BROWSER_TEST_F(SearchPreloadBrowserTest,
+                       OnNavigationLikely_TriggersPrefetch) {
+  SetUpTemplateURLService(/*prefetch_likely_navigations=*/true);
+
+  ASSERT_TRUE(content::NavigateToURL(
+      &GetWebContents(), embedded_test_server()->GetURL("/empty.html")));
+
+  std::string original_query = "he";
+  std::string search_terms = "hello";
+  GURL prefetch_url_on_navigation_likely =
+      GetSearchUrl(search_terms, UrlType::kPrefetchOnNavigationLikely);
+  GURL navigation_url = GetSearchUrl(search_terms, UrlType::kReal);
+
+  {
+    content::test::TestPrefetchWatcher watcher;
+
+    AutocompleteMatch autocomplete_match = CreateSearchSuggestionMatch(
+        original_query, search_terms, PrefetchHint::kEnabled,
+        PrerenderHint::kDisabled);
+
+    const bool is_triggered_prefetch =
+        GetSearchPreloadService().OnNavigationLikely(
+            1, autocomplete_match,
+            omnibox::mojom::NavigationPredictor::kMouseDown, &GetWebContents());
+    ASSERT_TRUE(is_triggered_prefetch);
+
+    watcher.WaitUntilPrefetchResponseCompleted(
+        std::nullopt, prefetch_url_on_navigation_likely);
+  }
+
+  EXPECT_EQ(1,
+            request_collector().CountByPath(prefetch_url_on_navigation_likely));
+
+  // Navigate.
+  ASSERT_TRUE(content::NavigateToURL(&GetWebContents(), navigation_url));
+
+  // Prefetch is used.
+  EXPECT_EQ(1,
+            request_collector().CountByPath(prefetch_url_on_navigation_likely));
+  EXPECT_EQ(0, request_collector().CountByPath(navigation_url));
+}
+
+// `OnNavigationLikely()` doesn't trigger prefetch if default search provider
+// doesn't opt in.
+IN_PROC_BROWSER_TEST_F(
+    SearchPreloadBrowserTest,
+    OnNavigationLikely_DoesntTriggerPrefetchIfDefaultSearchProviderDoesntOptIn) {
+  SetUpTemplateURLService(/*prefetch_likely_navigations=*/false);
+
+  ASSERT_TRUE(content::NavigateToURL(
+      &GetWebContents(), embedded_test_server()->GetURL("/empty.html")));
+
+  std::string original_query = "he";
+  std::string search_terms = "hello";
+
+  AutocompleteMatch autocomplete_match = CreateSearchSuggestionMatch(
+      original_query, search_terms, PrefetchHint::kEnabled,
+      PrerenderHint::kDisabled);
+
+  const bool is_triggered_prefetch =
+      GetSearchPreloadService().OnNavigationLikely(
+          1, autocomplete_match,
+          omnibox::mojom::NavigationPredictor::kMouseDown, &GetWebContents());
+  ASSERT_FALSE(is_triggered_prefetch);
+}
+
+// Scenario:
+//
+// - A user inputs "he".
+// - Autocomplete suggests to prefetch "hello".
+// - `SearchPreloadService` starts prefetch with query "?q=hello&pf=cs...".
+// - A user clicks a suggestion "hello".
+// - Prefetch is not triggered with query "?q=hello&pf=op..." as prefetch is
+// already triggered.
+// - A user navigates to a page with query "?q=hello&..."
+// - Prefetch is used.
+IN_PROC_BROWSER_TEST_F(SearchPreloadBrowserTest,
+                       OnAutocompleteResultChanged_Then_OnNavigationLikely) {
+  SetUpTemplateURLService(/*prefetch_likely_navigations=*/true);
+
+  ASSERT_TRUE(content::NavigateToURL(
+      &GetWebContents(), embedded_test_server()->GetURL("/empty.html")));
+
+  std::string original_query = "he";
+  std::string search_terms = "hello";
+  GURL prefetch_url = GetSearchUrl(search_terms, UrlType::kPrefetch);
+  GURL prefetch_url_on_navigation_likely =
+      GetSearchUrl(search_terms, UrlType::kPrefetchOnNavigationLikely);
+  GURL navigation_url = GetSearchUrl(search_terms, UrlType::kReal);
+
+  {
+    content::test::TestPrefetchWatcher watcher;
+
+    ChangeAutocompleteResult(original_query, search_terms,
+                             PrefetchHint::kEnabled, PrerenderHint::kDisabled);
+
+    watcher.WaitUntilPrefetchResponseCompleted(std::nullopt, prefetch_url);
+  }
+
+  EXPECT_EQ(1, request_collector().CountByPath(prefetch_url));
+
+  AutocompleteMatch autocomplete_match = CreateSearchSuggestionMatch(
+      original_query, search_terms, PrefetchHint::kEnabled,
+      PrerenderHint::kDisabled);
+
+  const bool is_triggered_prefetch =
+      GetSearchPreloadService().OnNavigationLikely(
+          1, autocomplete_match,
+          omnibox::mojom::NavigationPredictor::kMouseDown, &GetWebContents());
+  ASSERT_FALSE(is_triggered_prefetch);
+
+  // Navigate.
+  ASSERT_TRUE(content::NavigateToURL(&GetWebContents(), navigation_url));
+
+  // Prefetch is used.
+  EXPECT_EQ(1, request_collector().CountByPath(prefetch_url));
+  EXPECT_EQ(0,
+            request_collector().CountByPath(prefetch_url_on_navigation_likely));
+  EXPECT_EQ(0, request_collector().CountByPath(navigation_url));
+}
+
 }  // namespace
diff --git a/chrome/browser/preloading/search_preload/search_preload_pipeline.cc b/chrome/browser/preloading/search_preload/search_preload_pipeline.cc
index 24c698f..e72a9c7 100644
--- a/chrome/browser/preloading/search_preload/search_preload_pipeline.cc
+++ b/chrome/browser/preloading/search_preload/search_preload_pipeline.cc
@@ -55,13 +55,19 @@
       /*vary_on_key_order=*/true);
 }
 
-void SearchPreloadPipeline::StartPrefetch(
+bool SearchPreloadPipeline::StartPrefetch(
     content::WebContents& web_contents,
     const GURL& prefetch_url,
     content::PreloadingPredictor predictor) {
-  // Don't trigger prefetch if already triggered.
+  // Don't trigger prefetch if already triggered and is alive.
+  //
+  // TODO(crbug.com/394213503): Reconsider the behavior when prefetch is already
+  // triggered but not alive. Currently, the main reason that a triggered
+  // prefetch fails for DSE (embedder trigger, no TTL) is the failure of the
+  // load of the prefetch. (There should be no other timeouts nor expiration.)
+  // In general, retriggering may be useful.
   if (prefetch_handle_) {
-    return;
+    return false;
   }
 
   auto* preloading_data =
@@ -93,6 +99,8 @@
       /*referring_origin=*/std::nullopt, std::move(no_vary_search_hint),
       pipeline_info_, attempt->GetWeakPtr(),
       /*holdback_status_override=*/std::nullopt);
+
+  return true;
 }
 
 void SearchPreloadPipeline::StartPrerender(
diff --git a/chrome/browser/preloading/search_preload/search_preload_pipeline.h b/chrome/browser/preloading/search_preload/search_preload_pipeline.h
index 10667927..10980b51 100644
--- a/chrome/browser/preloading/search_preload/search_preload_pipeline.h
+++ b/chrome/browser/preloading/search_preload/search_preload_pipeline.h
@@ -29,7 +29,10 @@
   void UpdateConfidence(content::WebContents& web_contents, int confidence);
 
   // Starts prefetch if not triggered yet.
-  void StartPrefetch(content::WebContents& web_contents,
+  //
+  // Returns true iff prefetch is triggered, i.e. `WebContents::StartPrefetch()`
+  // is called.
+  bool StartPrefetch(content::WebContents& web_contents,
                      const GURL& prefetch_url,
                      content::PreloadingPredictor predictor);
   // Starts prerender if not triggered yet and prefetch is alive.
diff --git a/chrome/browser/preloading/search_preload/search_preload_pipeline_manager.cc b/chrome/browser/preloading/search_preload/search_preload_pipeline_manager.cc
index 0b3c7d8..8844593c 100644
--- a/chrome/browser/preloading/search_preload/search_preload_pipeline_manager.cc
+++ b/chrome/browser/preloading/search_preload/search_preload_pipeline_manager.cc
@@ -33,6 +33,20 @@
   return std::nullopt;
 }
 
+// Ergonomic wrapper of `ExtractSearchTermsFromURL()`
+std::optional<std::u16string> ExtractSearchTermsFromUrl(
+    TemplateURLService& template_url_service,
+    const AutocompleteMatch& match) {
+  std::u16string search_terms;
+  if (template_url_service.GetDefaultSearchProvider()
+          ->ExtractSearchTermsFromURL(match.destination_url,
+                                      template_url_service.search_terms_data(),
+                                      &search_terms)) {
+    return search_terms;
+  }
+  return std::nullopt;
+}
+
 }  // namespace
 
 WEB_CONTENTS_USER_DATA_KEY_IMPL(SearchPreloadPipelineManager);
@@ -120,3 +134,81 @@
         chrome_preloading_predictor::kDefaultSearchEngine);
   }
 }
+
+bool SearchPreloadPipelineManager::OnNavigationLikely(
+    Profile& profile,
+    const AutocompleteMatch& match,
+    omnibox::mojom::NavigationPredictor navigation_predictor) {
+  if (!features::DsePreload2IsPredictorEnabled(navigation_predictor)) {
+    return false;
+  }
+
+  if (!AutocompleteMatch::IsSearchType(match.type)) {
+    return false;
+  }
+
+  auto* template_url_service =
+      TemplateURLServiceFactory::GetForProfile(&profile);
+  CHECK(template_url_service);
+  bool does_search_provider_opt_in =
+      template_url_service->GetDefaultSearchProvider() &&
+      template_url_service->GetDefaultSearchProvider()
+          ->data()
+          .prefetch_likely_navigations;
+  if (!does_search_provider_opt_in) {
+    return false;
+  }
+
+  const std::optional<GURL> maybe_canonical_url =
+      GetCanonicalUrlForSearchPreload(profile, match.destination_url);
+  if (!maybe_canonical_url.has_value()) {
+    return false;
+  }
+  const GURL& canonical_url = maybe_canonical_url.value();
+
+  const std::optional<std::u16string> maybe_search_terms =
+      ExtractSearchTermsFromUrl(*template_url_service, match);
+  if (!maybe_search_terms.has_value()) {
+    return false;
+  }
+  const std::u16string& search_terms = maybe_search_terms.value();
+
+  GURL prefetch_url;
+  if (match.search_terms_args) {
+    auto& search_terms_args = *match.search_terms_args.get();
+    prefetch_url =
+        GetPrefetchUrlFromMatch(search_terms_args, *template_url_service,
+                                /*is_navigation_likely=*/true);
+  } else {
+    // Search history suggestions (those that are not also server suggestions)
+    // don't have search term args. Generate search term args instead.
+
+    auto search_terms_args_for_history_suggestion =
+        std::make_unique<TemplateURLRef::SearchTermsArgs>(search_terms);
+    auto& search_terms_args = *search_terms_args_for_history_suggestion.get();
+    prefetch_url =
+        GetPrefetchUrlFromMatch(search_terms_args, *template_url_service,
+                                /*is_navigation_likely=*/true);
+  }
+
+  auto predictor =
+      [](omnibox::mojom::NavigationPredictor navigation_predictor) {
+        switch (navigation_predictor) {
+          case omnibox::mojom::NavigationPredictor::kMouseDown:
+            return chrome_preloading_predictor::kOmniboxMousePredictor;
+          case omnibox::mojom::NavigationPredictor::kUpOrDownArrowButton:
+            return chrome_preloading_predictor::kOmniboxSearchPredictor;
+          case omnibox::mojom::NavigationPredictor::kTouchDown:
+            return chrome_preloading_predictor::kOmniboxTouchDownPredictor;
+        }
+      }(navigation_predictor);
+
+  // TODO(crbug.com/403198750): Limit the number of active pipelines.
+  if (!pipelines_.contains(canonical_url)) {
+    pipelines_.insert_or_assign(
+        canonical_url, std::make_unique<SearchPreloadPipeline>(canonical_url));
+  }
+  pipelines_[canonical_url]->UpdateConfidence(GetWebContents(), 100);
+  return pipelines_[canonical_url]->StartPrefetch(GetWebContents(),
+                                                  prefetch_url, predictor);
+}
diff --git a/chrome/browser/preloading/search_preload/search_preload_pipeline_manager.h b/chrome/browser/preloading/search_preload/search_preload_pipeline_manager.h
index cf0bf44..612f7d1 100644
--- a/chrome/browser/preloading/search_preload/search_preload_pipeline_manager.h
+++ b/chrome/browser/preloading/search_preload/search_preload_pipeline_manager.h
@@ -13,6 +13,7 @@
 
 class Profile;
 class AutocompleteResult;
+struct AutocompleteMatch;
 
 namespace content {
 class WebContents;
@@ -50,6 +51,16 @@
   void OnAutocompleteResultChanged(Profile& profile,
                                    const AutocompleteResult& result);
 
+  // Called when a user is likely to navigate to the match.
+  //
+  // Returns true iff a new prefetch is triggered by this call. Note that it
+  // returns false if a prefetch for the same canonical URL has already
+  // triggered.
+  bool OnNavigationLikely(
+      Profile& profile,
+      const AutocompleteMatch& match,
+      omnibox::mojom::NavigationPredictor navigation_predictor);
+
  private:
   friend content::WebContentsUserData<SearchPreloadPipelineManager>;
   WEB_CONTENTS_USER_DATA_KEY_DECL();
diff --git a/chrome/browser/preloading/search_preload/search_preload_service.cc b/chrome/browser/preloading/search_preload/search_preload_service.cc
index 5b039fe..955b7dc7 100644
--- a/chrome/browser/preloading/search_preload/search_preload_service.cc
+++ b/chrome/browser/preloading/search_preload/search_preload_service.cc
@@ -87,5 +87,10 @@
     const AutocompleteMatch& match,
     omnibox::mojom::NavigationPredictor navigation_predictor,
     content::WebContents* web_contents) {
-  NOTREACHED();
+  if (!web_contents) {
+    return false;
+  }
+
+  return GetOrCreatePipelineManagerWithLimit(*web_contents)
+      .OnNavigationLikely(*profile_, match, navigation_predictor);
 }
diff --git a/chrome/browser/printing/print_backend_service_manager.cc b/chrome/browser/printing/print_backend_service_manager.cc
index 8e75f8b..43dadbf 100644
--- a/chrome/browser/printing/print_backend_service_manager.cc
+++ b/chrome/browser/printing/print_backend_service_manager.cc
@@ -38,6 +38,7 @@
 
 #if BUILDFLAG(IS_LINUX)
 #include "content/public/common/content_switches.h"
+#include "ui/linux/linux_ui.h"
 #endif
 
 #if BUILDFLAG(IS_WIN)
@@ -878,14 +879,19 @@
             << (sandboxed ? "sandboxed" : "unsandboxed") << " for `"
             << remote_id << "`";
 
+    std::vector<std::string> extra_switches;
+#if BUILDFLAG(IS_LINUX)
+    if (auto* linux_ui = ui::LinuxUi::instance()) {
+      extra_switches = linux_ui->GetCmdLineFlagsForCopy();
+    }
+    extra_switches.push_back(switches::kMessageLoopTypeUi);
+#endif
     mojo::Remote<T>& host = bundle->host;
     content::ServiceProcessHost::Launch(
         host.BindNewPipeAndPassReceiver(),
         content::ServiceProcessHost::Options()
             .WithDisplayName(IDS_UTILITY_PROCESS_PRINT_BACKEND_SERVICE_NAME)
-#if BUILDFLAG(IS_LINUX)
-            .WithExtraCommandLineSwitches({switches::kMessageLoopTypeUi})
-#endif
+            .WithExtraCommandLineSwitches(std::move(extra_switches))
             .Pass());
     host->BindBackend(service.BindNewPipeAndPassReceiver());
 
diff --git a/chrome/browser/privacy_guide/android/java/res/layout/privacy_guide_welcome.xml b/chrome/browser/privacy_guide/android/java/res/layout/privacy_guide_welcome.xml
index 1effa2d..eb72fa1 100644
--- a/chrome/browser/privacy_guide/android/java/res/layout/privacy_guide_welcome.xml
+++ b/chrome/browser/privacy_guide/android/java/res/layout/privacy_guide_welcome.xml
@@ -8,7 +8,8 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
-    android:layout_height="match_parent">
+    android:layout_height="match_parent"
+    android:id="@+id/privacy_guide_welcome_scrollview">
 
     <LinearLayout
         android:layout_width="match_parent"
diff --git a/chrome/browser/privacy_guide/android/java/src/org/chromium/chrome/browser/privacy_guide/PrivacyGuideFragment.java b/chrome/browser/privacy_guide/android/java/src/org/chromium/chrome/browser/privacy_guide/PrivacyGuideFragment.java
index 716ded7..e60f2e4 100644
--- a/chrome/browser/privacy_guide/android/java/src/org/chromium/chrome/browser/privacy_guide/PrivacyGuideFragment.java
+++ b/chrome/browser/privacy_guide/android/java/src/org/chromium/chrome/browser/privacy_guide/PrivacyGuideFragment.java
@@ -19,6 +19,7 @@
 import androidx.annotation.IntDef;
 import androidx.appcompat.app.AppCompatActivity;
 import androidx.fragment.app.Fragment;
+import androidx.recyclerview.widget.RecyclerView;
 import androidx.viewpager2.widget.ViewPager2;
 
 import com.google.android.material.tabs.TabLayout;
@@ -129,6 +130,14 @@
         mViewPager.setPageTransformer(new PrivacyGuidePageTransformer());
         mViewPager.setUserInputEnabled(false);
 
+        // Workaround for ViewPager2 bug: https://issuetracker.google.com/issues/284429851.
+        for (int i = 0; i < mViewPager.getChildCount(); i++) {
+            var child = mViewPager.getChildAt(i);
+            if (child instanceof RecyclerView) {
+                child.setFocusable(false);
+            }
+        }
+
         mOnPageChangeCallback =
                 new ViewPager2.OnPageChangeCallback() {
                     @Override
@@ -154,10 +163,12 @@
         mViewPager.registerOnPageChangeCallback(mOnPageChangeCallback);
 
         mTabLayout = mView.findViewById(R.id.tab_layout);
+        mTabLayout.setFocusable(false);
         new TabLayoutMediator(
                         mTabLayout,
                         mViewPager,
                         (tab, position) -> {
+                            tab.view.setFocusable(false);
                             tab.view.setClickable(false);
                             tab.view.setImportantForAccessibility(
                                     View.IMPORTANT_FOR_ACCESSIBILITY_NO);
diff --git a/chrome/browser/privacy_guide/android/java/src/org/chromium/chrome/browser/privacy_guide/WelcomeFragment.java b/chrome/browser/privacy_guide/android/java/src/org/chromium/chrome/browser/privacy_guide/WelcomeFragment.java
index cd8913b..706e0f5 100644
--- a/chrome/browser/privacy_guide/android/java/src/org/chromium/chrome/browser/privacy_guide/WelcomeFragment.java
+++ b/chrome/browser/privacy_guide/android/java/src/org/chromium/chrome/browser/privacy_guide/WelcomeFragment.java
@@ -22,4 +22,12 @@
             @Nullable Bundle savedInstanceState) {
         return inflater.inflate(R.layout.privacy_guide_welcome, container, false);
     }
+
+    @Override
+    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+        // ScrollView sets focusable to true during construction. So setting focusable to false in
+        // xml file doesn't work. It has to be set after the construction of ScrollView.
+        view.findViewById(R.id.privacy_guide_welcome_scrollview).setFocusable(false);
+    }
 }
diff --git a/chrome/browser/privacy_sandbox/BUILD.gn b/chrome/browser/privacy_sandbox/BUILD.gn
index 79998f3..ed2b0215 100644
--- a/chrome/browser/privacy_sandbox/BUILD.gn
+++ b/chrome/browser/privacy_sandbox/BUILD.gn
@@ -288,13 +288,58 @@
   ]
 }
 
+source_set("survey_desktop_controller_unittest") {
+  testonly = true
+  if (!is_android) {
+    sources = [ "privacy_sandbox_survey_desktop_controller_unittest.cc" ]
+    deps = [
+      "//base/test:test_support",
+      "//base/version_info:channel",
+      "//chrome/browser/profiles:profile",
+      "//chrome/browser/ui/hats",
+      "//chrome/browser/ui/hats:test_support",
+      "//chrome/common:channel_info",
+      "//chrome/test:test_support",
+      "//components/prefs",
+      "//components/prefs:test_support",
+      "//components/privacy_sandbox:features",
+      "//components/privacy_sandbox:privacy_sandbox_prefs",
+      "//components/privacy_sandbox:privacy_sandbox_survey_service",
+      "//components/signin/public/base",
+      "//components/signin/public/identity_manager:test_support",
+      "//components/version_info",
+      "//content/test:test_support",
+    ]
+  }
+}
+
+source_set("activity_types_service_unittest") {
+  testonly = true
+  if (is_android) {
+    sources = [ "privacy_sandbox_activity_types_service_unittest.cc" ]
+    deps = [
+      "//base/test:test_support",
+      "//chrome/browser/preferences:java_pref_names_srcjar",
+      "//chrome/browser/privacy_sandbox/android:privacy_sandbox_enums",
+      "//chrome/test:test_support",
+      "//components/metrics:metrics_pref_names",
+      "//components/prefs:test_support",
+      "//components/privacy_sandbox:features",
+      "//components/privacy_sandbox:privacy_sandbox_prefs",
+      "//content/test:test_support",
+    ]
+  }
+}
+
 group("unit_tests") {
   testonly = true
   deps = [
+    ":activity_types_service_unittest",
     ":policy_handler_unittest",
     ":profile_bucket_metrics_unittest",
     ":service_impl_unittest",
     ":settings_delegate_unittest",
+    ":survey_desktop_controller_unittest",
     ":utils_unittest",
     "notice:unit_tests",
   ]
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 f86596f..fdbca07 100644
--- a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
+++ b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
@@ -231,6 +231,7 @@
 #include "chrome/browser/visited_url_ranking/group_suggestions_service_factory.h"
 #include "chrome/browser/visited_url_ranking/visited_url_ranking_service_factory.h"
 #include "chrome/browser/webauthn/enclave_manager_factory.h"
+#include "chrome/browser/webauthn/immediate_request_rate_limiter_factory.h"
 #include "chrome/browser/webdata_services/web_data_service_factory.h"
 #include "chrome/browser/webid/federated_identity_api_permission_context_factory.h"
 #include "chrome/browser/webid/federated_identity_auto_reauthn_permission_context_factory.h"
@@ -956,6 +957,9 @@
   HttpsEngagementServiceFactory::GetInstance();
   HttpsFirstModeServiceFactory::GetInstance();
   IdentityManagerFactory::EnsureFactoryAndDependeeFactoriesBuilt();
+#if !BUILDFLAG(IS_ANDROID)
+  ImmediateRequestRateLimiterFactory::GetInstance();
+#endif  // !BUILDFLAG(IS_ANDROID)
   InMemoryURLIndexFactory::GetInstance();
   visited_url_ranking::VisitedURLRankingServiceFactory::GetInstance();
 #if !BUILDFLAG(IS_ANDROID)
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv2/autoclick/autoclick_test.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv2/autoclick/autoclick_test.js
index 02398a807..90f2167 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv2/autoclick/autoclick_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv2/autoclick/autoclick_test.js
@@ -38,6 +38,7 @@
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
 #include "chrome/browser/ash/accessibility/accessibility_manager.h"
+#include "ui/accessibility/accessibility_features.h"
     `);
   }
 
@@ -52,6 +53,13 @@
     super.testGenPreambleCommon('kAccessibilityCommonExtensionId');
   }
 
+  /** @override */
+  get featureList() {
+    return {
+      disabled: ['features::kAccessibilityManifestV3AccessibilityCommon']
+    };
+  }
+
   /**
    * Asserts that two rects are the same.
    * @param {!chrome.accessibilityPrivate.ScreenRect} first
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv2/dictation/dictation_test_base.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv2/dictation/dictation_test_base.js
index 3150eff..e336897 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv2/dictation/dictation_test_base.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv2/dictation/dictation_test_base.js
@@ -159,6 +159,13 @@
         ]);
   }
 
+  /** @override */
+  get featureList() {
+    return {
+      disabled: ['features::kAccessibilityManifestV3AccessibilityCommon']
+    };
+  }
+
   /** Turns on Dictation and checks IME and Speech Recognition state. */
   toggleDictationOn() {
     this.mockAccessibilityPrivate.callOnToggleDictation(true);
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv2/facegaze/facegaze_test_base.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv2/facegaze/facegaze_test_base.js
index 23b47d7..6aae0f3 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv2/facegaze/facegaze_test_base.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv2/facegaze/facegaze_test_base.js
@@ -284,7 +284,10 @@
 
   /** @override */
   get featureList() {
-    return {enabled: ['features::kAccessibilityFaceGaze']};
+    return {
+      enabled: ['features::kAccessibilityFaceGaze'],
+      disabled: ['features::kAccessibilityManifestV3AccessibilityCommon']
+    };
   }
 
   /** @return {!FaceGaze} */
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv2/magnifier/magnifier_test.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv2/magnifier/magnifier_test.js
index 305e712..10f8cf7 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv2/magnifier/magnifier_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv2/magnifier/magnifier_test.js
@@ -53,6 +53,7 @@
       enabled: [
         'features::kAccessibilityMagnifierFollowsChromeVox',
       ],
+      disabled: ['features::kAccessibilityManifestV3AccessibilityCommon']
     };
   }
 };
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv3/BUILD.gn b/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv3/BUILD.gn
index ad0fec9..8a1b6f0c 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv3/BUILD.gn
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv3/BUILD.gn
@@ -53,6 +53,7 @@
   "dictation/ui_controller.ts",
   "dictation/macros/list_commands_macro.ts",
   "dictation/offscreen_audio.ts",
+  "dictation/offscreen_pumpkin_worker.ts",
   "dictation/parse/input_text_strategy.ts",
   "dictation/parse/parse_strategy.ts",
   "dictation/parse/pumpkin_parse_strategy.ts",
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv3/dictation/offscreen.html b/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv3/dictation/offscreen.html
index cb6ee3a8..ef0b9824 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv3/dictation/offscreen.html
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv3/dictation/offscreen.html
@@ -6,4 +6,5 @@
 
 <body>
   <script type="module" src="offscreen_audio.js"></script>
+  <script type="module" src="offscreen_pumpkin_worker.js"></script>
 </body>
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv3/dictation/offscreen_pumpkin_worker.ts b/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv3/dictation/offscreen_pumpkin_worker.ts
new file mode 100644
index 0000000..85be4581
--- /dev/null
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv3/dictation/offscreen_pumpkin_worker.ts
@@ -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.
+
+import {OffscreenCommandType} from '../offscreen_command_type.js';
+
+import * as PumpkinConstants from './parse/pumpkin/pumpkin_constants.js';
+
+const SANDBOXED_PUMPKIN_TAGGER_JS_FILE = 'parse/sandboxed_pumpkin_tagger.js';
+
+/**
+ * Offscreen way to communicate to pumpkin via worker.
+ */
+class OffscreenPumpkinWorker {
+  private worker_: Worker|null = null;
+
+  static instance?: OffscreenPumpkinWorker;
+
+  static init(): void {
+    if (OffscreenPumpkinWorker.instance) {
+      throw 'Error: trying to create two instances of singleton ' +
+          'OffscreenPumpkinWorker.';
+    }
+    OffscreenPumpkinWorker.instance = new OffscreenPumpkinWorker();
+  }
+
+  constructor() {
+    chrome.runtime.onMessage.addListener(
+        (message: any|undefined, _sender: chrome.runtime.MessageSender) =>
+            this.handleMessageFromServiceWorker_(message));
+  }
+
+  private handleMessageFromServiceWorker_(message: any|undefined): boolean {
+    switch (message['command']) {
+      case OffscreenCommandType.DICTATION_PUMPKIN_INSTALL:
+        this.worker_ =
+            new Worker(SANDBOXED_PUMPKIN_TAGGER_JS_FILE, {type: 'module'});
+        this.worker_.onmessage = (message) =>
+            chrome.runtime.sendMessage(undefined, {
+              command: OffscreenCommandType.DICTATION_PUMPKIN_RECEIVE,
+              fromPumpkinTagger: message.data
+            });
+        break;
+      case OffscreenCommandType.DICTATION_PUMPKIN_SEND:
+        this.sendToSandboxedPumpkinTagger_(message['toPumpkinTagger'])
+        break;
+    }
+    return false;
+  }
+
+  private sendToSandboxedPumpkinTagger_(
+      command: PumpkinConstants.ToPumpkinTagger): void {
+    if (!this.worker_) {
+      throw new Error(
+          `Worker not ready, cannot send command to SandboxedPumpkinTagger: ${
+              command.type}`);
+    }
+    this.worker_.postMessage(command);
+  }
+}
+
+OffscreenPumpkinWorker.init();
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv3/dictation/parse/pumpkin/pumpkin_constants.ts b/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv3/dictation/parse/pumpkin/pumpkin_constants.ts
index be65875..aff3fe36 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv3/dictation/parse/pumpkin/pumpkin_constants.ts
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv3/dictation/parse/pumpkin/pumpkin_constants.ts
@@ -141,7 +141,4 @@
   END_PHRASE = 'END_PHRASE',
 }
 
-export const SANDBOXED_PUMPKIN_TAGGER_JS_FILE =
-    'dictation/parse/sandboxed_pumpkin_tagger.js';
-
 TestImportManager.exportForTesting(['SUPPORTED_LOCALES', SUPPORTED_LOCALES]);
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv3/dictation/parse/pumpkin_parse_strategy.ts b/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv3/dictation/parse/pumpkin_parse_strategy.ts
index f5e07dd..bc8ac70 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv3/dictation/parse/pumpkin_parse_strategy.ts
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv3/dictation/parse/pumpkin_parse_strategy.ts
@@ -21,6 +21,7 @@
 import {SmartSelectBetweenMacro} from '/common/action_fulfillment/macros/smart_select_between_macro.js';
 import {ToggleDictationMacro} from '/common/action_fulfillment/macros/toggle_dictation_macro.js';
 
+import {OffscreenCommandType} from '../../offscreen_command_type.js';
 import {LocaleInfo} from '../locale_info.js';
 import {ListCommandsMacro} from '../macros/list_commands_macro.js';
 
@@ -39,7 +40,6 @@
   private pumpkinTaggerReady_ = false;
   private tagResolver_: (
       (results: proto.speech.pumpkin.PumpkinTaggerResults) => void)|null = null;
-  private worker_: Worker|null = null;
   private locale_: PumpkinConstants.PumpkinLocale|null = null;
   private requestedPumpkinInstall_ = false;
   private onPumpkinTaggerReadyChangedForTesting_: VoidFunction|null = null;
@@ -50,6 +50,11 @@
       return;
     }
 
+    chrome.runtime.onMessage.addListener(
+        (message: any|undefined, _sender: chrome.runtime.MessageSender,
+         _sendResponse: (value: any) => void) =>
+            this.handleMessageFromOffscreen_(message));
+
     this.requestedPumpkinInstall_ = true;
     chrome.accessibilityPrivate.installPumpkinForDictation(data => {
       // TODO(crbug.com/259352407): Consider retrying installation at a later
@@ -79,17 +84,23 @@
     this.setPumpkinTaggerReady_(false);
     this.pumpkinData_ = data;
 
-    this.worker_ = new Worker(
-        PumpkinConstants.SANDBOXED_PUMPKIN_TAGGER_JS_FILE, {type: 'module'});
-    this.worker_.onmessage = (message) => this.onMessage_(message);
+    this.sendToOffscreen_(OffscreenCommandType.DICTATION_PUMPKIN_INSTALL);
+  }
+
+  private handleMessageFromOffscreen_(message: any|undefined) {
+    switch (message['command']) {
+      case OffscreenCommandType.DICTATION_PUMPKIN_RECEIVE:
+        this.onMessage_(message['fromPumpkinTagger']);
+        break;
+    }
+    return false;
   }
 
   /**
    * Called when the SandboxedPumpkinTagger posts a message to the background
    * context.
    */
-  private onMessage_(message: MessageEvent): void {
-    const command: PumpkinConstants.FromPumpkinTagger = message.data;
+  private onMessage_(command: PumpkinConstants.FromPumpkinTagger): void {
     switch (command.type) {
       case PumpkinConstants.FromPumpkinTaggerCommand.READY:
         this.refreshLocale_();
@@ -128,14 +139,9 @@
   }
 
   private sendToSandboxedPumpkinTagger_(
-      command: PumpkinConstants.ToPumpkinTagger): void {
-    if (!this.worker_) {
-      throw new Error(
-          `Worker not ready, cannot send command to SandboxedPumpkinTagger: ${
-              command.type}`);
-    }
-
-    this.worker_.postMessage(command);
+      toPumpkinTagger: PumpkinConstants.ToPumpkinTagger): void {
+    this.sendToOffscreen_(
+        OffscreenCommandType.DICTATION_PUMPKIN_SEND, {toPumpkinTagger});
   }
 
   /**
@@ -356,4 +362,8 @@
       this.onPumpkinTaggerReadyChangedForTesting_();
     }
   }
+
+  private sendToOffscreen_(command: OffscreenCommandType, data = {}): void {
+    chrome.runtime.sendMessage(undefined, Object.assign({command}, data));
+  }
 }
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv3/offscreen_command_type.ts b/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv3/offscreen_command_type.ts
index 415eaa2..232f942 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv3/offscreen_command_type.ts
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv3/offscreen_command_type.ts
@@ -10,4 +10,7 @@
   DICTATION_PLAY_CANCEL = 'DictationPlayCancel',
   DICTATION_PLAY_START = 'DictationPlayStart',
   DICTATION_PLAY_END = 'DictationPlayEnd',
+  DICTATION_PUMPKIN_INSTALL = 'DictationPumpkinInstall',
+  DICTATION_PUMPKIN_RECEIVE = 'DictationPumpkinReceive',
+  DICTATION_PUMPKIN_SEND = 'DictationPumpkinSend',
 }
diff --git a/chrome/browser/resources/glic/webview.ts b/chrome/browser/resources/glic/webview.ts
index ba735d8..b68fcf55 100644
--- a/chrome/browser/resources/glic/webview.ts
+++ b/chrome/browser/resources/glic/webview.ts
@@ -222,6 +222,7 @@
         return;
       }
     }
+    console.warn(`Webview permission request was denied: ${e.permission}`);
     e.request.deny();
   }
 
diff --git a/chrome/browser/resources/lens/overlay/BUILD.gn b/chrome/browser/resources/lens/overlay/BUILD.gn
index fd1b5c3..34314cd 100644
--- a/chrome/browser/resources/lens/overlay/BUILD.gn
+++ b/chrome/browser/resources/lens/overlay/BUILD.gn
@@ -77,6 +77,8 @@
     "selection_utils.ts",
     "side_panel/side_panel_browser_proxy.ts",
     "side_panel/side_panel_error_page.ts",
+    "side_panel/feedback_toast.ts",
+    "side_panel/feedback_toast.html.ts",
     "side_panel/side_panel_error_page.html.ts",
     "side_panel/post_message_communication.ts",
     "simplified_text_layer.html.ts",
@@ -91,6 +93,7 @@
   css_files = [
     "simplified_text_layer.css",
     "side_panel/side_panel_error_page.css",
+    "side_panel/feedback_toast.css",
   ]
 
   ts_deps = [
diff --git a/chrome/browser/resources/lens/overlay/side_panel/feedback_toast.css b/chrome/browser/resources/lens/overlay/side_panel/feedback_toast.css
new file mode 100644
index 0000000..1cc113c
--- /dev/null
+++ b/chrome/browser/resources/lens/overlay/side_panel/feedback_toast.css
@@ -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. */
+
+/* #css_wrapper_metadata_start
+ * #type=style-lit
+ * #scheme=relative
+ * #css_wrapper_metadata_end */
+
+#closeFeedbackToastButton {
+  margin-block-end: 8px;
+  margin-block-start: 8px;
+  margin-inline-end: 0;
+  margin-inline-start: 0;
+}
+
+#feedbackButtonContainer {
+  align-items: center;
+  display: flex;
+  justify-content: center;
+  margin-inline-start: auto;
+}
+
+#feedbackToast {
+  --color-toast-background: white;
+  --color-toast-button: #1558D6;
+  --color-toast-foreground: #474747;
+  --cr-icon-button-fill-color: #5E5E5E;
+  box-shadow: 0px 2px 6px 0px rgba(23, 23, 23, 0.32);
+  display: flex;
+  height: 48px;
+  left: 0;
+  margin: auto auto 16px;
+  min-height: 48px;
+  padding: 0;
+  right: 0;
+  width: 336px;
+  white-space: nowrap;
+}
+
+:host([dark-mode]) #feedbackToast {
+  --color-toast-background: #303134;
+  --color-toast-button: #99C3FF6;
+  --color-toast-foreground: #BFBFBF;
+  --cr-icon-button-fill-color: #9E9E9E;
+}
+
+#feedbackToastMessage {
+  margin-inline-start: 16px;
+}
+
+#sendFeedbackButton {
+  background-color: transparent;
+  border: none;
+  margin-block-end: 6px;
+  margin-block-start: 6px;
+  padding: 0;
+}
\ No newline at end of file
diff --git a/chrome/browser/resources/lens/overlay/side_panel/feedback_toast.html.ts b/chrome/browser/resources/lens/overlay/side_panel/feedback_toast.html.ts
new file mode 100644
index 0000000..1815e107
--- /dev/null
+++ b/chrome/browser/resources/lens/overlay/side_panel/feedback_toast.html.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 '//resources/cr_elements/cr_button/cr_button.js';
+import '//resources/cr_elements/cr_icon_button/cr_icon_button.js';
+import '//resources/cr_elements/cr_toast/cr_toast.js';
+
+import {loadTimeData} from '//resources/js/load_time_data.js';
+import {html} from '//resources/lit/v3_0/lit.rollup.js';
+
+import type {FeedbackToastElement} from './feedback_toast.js';
+
+export function getHtml(this: FeedbackToastElement) {
+  return html`
+<cr-toast id="feedbackToast" duration="0">
+  <div id="feedbackToastMessage">${
+      loadTimeData.getString('feedbackToastMessage')}</div>
+  <div id="feedbackButtonContainer">
+    <cr-button id="sendFeedbackButton" @click="${this.onSendFeedbackClick}">
+      ${loadTimeData.getString('sendFeedbackButtonText')}
+    </cr-button>
+    <cr-icon-button id="closeFeedbackToastButton" iron-icon="cr:close"
+                    @click="${this.onHideFeedbackToastClick}">
+    </cr-icon-button>
+  </div>
+</cr-toast>`;
+}
diff --git a/chrome/browser/resources/lens/overlay/side_panel/feedback_toast.ts b/chrome/browser/resources/lens/overlay/side_panel/feedback_toast.ts
new file mode 100644
index 0000000..0346fd3
--- /dev/null
+++ b/chrome/browser/resources/lens/overlay/side_panel/feedback_toast.ts
@@ -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.
+
+import '//resources/cr_elements/cr_button/cr_button.js';
+import '//resources/cr_elements/cr_icon_button/cr_icon_button.js';
+import '//resources/cr_elements/cr_toast/cr_toast.js';
+import '/strings.m.js';
+
+import type {CrButtonElement} from '//resources/cr_elements/cr_button/cr_button.js';
+import type {CrToastElement} from '//resources/cr_elements/cr_toast/cr_toast.js';
+import {loadTimeData} from '//resources/js/load_time_data.js';
+import {CrLitElement} from '//resources/lit/v3_0/lit.rollup.js';
+
+import {getCss} from './feedback_toast.css.js';
+import {getHtml} from './feedback_toast.html.js';
+import {SidePanelBrowserProxyImpl} from './side_panel_browser_proxy.js';
+import type {SidePanelBrowserProxy} from './side_panel_browser_proxy.js';
+
+export interface FeedbackToastElement {
+  $: {
+    closeFeedbackToastButton: CrButtonElement,
+    feedbackToast: CrToastElement,
+  };
+}
+
+export class FeedbackToastElement extends CrLitElement {
+  static get is() {
+    return 'feedback-toast';
+  }
+
+  static override get styles() {
+    return getCss();
+  }
+
+  override render() {
+    return getHtml.bind(this)();
+  }
+
+  static override get properties() {
+    return {
+      darkMode: {
+        type: Boolean,
+        reflect: true,
+      },
+    };
+  }
+
+  // Whether to render the feedback toast in dark mode.
+  protected accessor darkMode: boolean = loadTimeData.getBoolean('darkMode');
+
+  private browserProxy: SidePanelBrowserProxy =
+      SidePanelBrowserProxyImpl.getInstance();
+
+  show() {
+    if (this.$.feedbackToast.open) {
+      // If toast already open, wait after hiding so that animation is
+      // smoother.
+      this.hide();
+      setTimeout(() => {
+        this.$.feedbackToast.show();
+      }, 100);
+      return;
+    }
+    this.$.feedbackToast.show();
+  }
+
+  hide() {
+    this.$.feedbackToast.hide();
+  }
+
+  protected onSendFeedbackClick() {
+    this.browserProxy.handler.requestSendFeedback();
+    this.hide();
+  }
+
+  protected onHideFeedbackToastClick() {
+    this.hide();
+  }
+}
+
+declare global {
+  interface HTMLElementTagNameMap {
+    'feedback-toast': FeedbackToastElement;
+  }
+}
+
+customElements.define(FeedbackToastElement.is, FeedbackToastElement);
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 ca52348..2865f6e9 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
@@ -28,48 +28,6 @@
     background-color: #101218;
   }
 
-  #closeFeedbackToastButton {
-    margin-block-end: 8px;
-    margin-block-start: 8px;
-    margin-inline-end: 0;
-    margin-inline-start: 0;
-  }
-
-  #feedbackButtonContainer {
-    align-items: center;
-    display: flex;
-    justify-content: center;
-    margin-inline-start: auto;
-  }
-
-  #feedbackToast {
-    --color-toast-background: white;
-    --color-toast-button: #1558D6;
-    --color-toast-foreground: #474747;
-    --cr-icon-button-fill-color: #5E5E5E;
-    box-shadow: 0px 2px 6px 0px rgba(23, 23, 23, 0.32);
-    display: flex;
-    height: 48px;
-    left: 0;
-    margin: auto auto 16px;
-    min-height: 48px;
-    padding: 0;
-    right: 0;
-    width: 336px;
-    white-space: nowrap;
-  }
-
-  :host([dark-mode]) #feedbackToast {
-    --color-toast-background: #303134;
-    --color-toast-button: #99C3FF6;
-    --color-toast-foreground: #BFBFBF;
-    --cr-icon-button-fill-color: #9E9E9E;
-  }
-
-  #feedbackToastMessage {
-    margin-inline-start: 16px;
-  }
-
   /* Set z-index to hide results to prevent ghost frames when reshowing. */
   :host([is-loading-results]) #results {
     z-index: 0;
@@ -154,14 +112,6 @@
     --color-searchbox-background: var(--color-searchbox-results-background);
   }
 
-  #sendFeedbackButton {
-    background-color: transparent;
-    border: none;
-    margin-block-end: 6px;
-    margin-block-start: 6px;
-    padding: 0;
-  }
-
   #toast {
     justify-content: space-between;
   }
@@ -268,17 +218,7 @@
     allow="cross-origin-isolated; display-capture; geolocation; microphone;"
     frameBorder="0">
 </iframe>
-<cr-toast id="feedbackToast" duration="0">
-  <div id="feedbackToastMessage">$i18n{feedbackToastMessage}</div>
-  <div id="feedbackButtonContainer">
-    <cr-button id="sendFeedbackButton" on-click="onSendFeedbackClick">
-      $i18n{sendFeedbackButtonText}
-    </cr-button>
-    <cr-icon-button id="closeFeedbackToastButton" iron-icon="cr:close"
-                    on-click="onHideFeedbackToastClick">
-    </cr-icon-button>
-  </div>
-</cr-toast>
+<feedback-toast id="feedbackToast"></feedback-toast>
 
 <cr-toast id="messageToast" duration="4000">
   <div>[[toastMessage]]</div>
diff --git a/chrome/browser/resources/lens/overlay/side_panel/side_panel_app.ts b/chrome/browser/resources/lens/overlay/side_panel/side_panel_app.ts
index 6a02e0e..69ad781 100644
--- a/chrome/browser/resources/lens/overlay/side_panel/side_panel_app.ts
+++ b/chrome/browser/resources/lens/overlay/side_panel/side_panel_app.ts
@@ -4,6 +4,7 @@
 
 import './side_panel_ghost_loader.js';
 import './side_panel_error_page.js';
+import './feedback_toast.js';
 import '/strings.m.js';
 import '/lens/shared/searchbox_ghost_loader.js';
 import '/lens/shared/searchbox_shared_style.css.js';
@@ -13,7 +14,6 @@
 import {ColorChangeUpdater} from '//resources/cr_components/color_change_listener/colors_css_updater.js';
 import {HelpBubbleMixin} from '//resources/cr_components/help_bubble/help_bubble_mixin.js';
 import type {SearchboxElement} from '//resources/cr_components/searchbox/searchbox.js';
-import type {CrButtonElement} from '//resources/cr_elements/cr_button/cr_button.js';
 import type {CrToastElement} from '//resources/cr_elements/cr_toast/cr_toast.js';
 import {I18nMixin} from '//resources/cr_elements/i18n_mixin.js';
 import {assert} from '//resources/js/assert.js';
@@ -28,6 +28,8 @@
 import {PageContentType} from '../page_content_type.mojom-webui.js';
 import {handleEscapeSearchbox} from '../searchbox_utils.js';
 
+import type {FeedbackToastElement} from './feedback_toast.js';
+
 import {PostMessageReceiver} from './post_message_communication.js';
 import {getTemplate} from './side_panel_app.html.js';
 import {SidePanelBrowserProxyImpl} from './side_panel_browser_proxy.js';
@@ -41,8 +43,7 @@
 
 export interface LensSidePanelAppElement {
   $: {
-    closeFeedbackToastButton: CrButtonElement,
-    feedbackToast: CrToastElement,
+    feedbackToast: FeedbackToastElement,
     ghostLoader: SidePanelGhostLoaderElement,
     messageToast: CrToastElement,
     errorPage: SidePanelErrorPageElement,
@@ -304,6 +305,8 @@
         this.uploadProgressPercentage = 0;
       };
 
+      // Show the feedback on every result load by showing it as soon as the
+      // result load animation is complete.
       this.showFeedbackToast();
     }
   }
@@ -465,11 +468,11 @@
     }
 
     await this.$.messageToast.hide();
-    await this.showToast(this.$.feedbackToast);
+    this.$.feedbackToast.show();
   }
 
   private async showMessageToast(message: string) {
-    await this.$.feedbackToast.hide();
+    this.$.feedbackToast.hide();
     await this.showToast(this.$.messageToast, message);
   }
 
@@ -489,14 +492,6 @@
     toast.show();
   }
 
-  private onSendFeedbackClick() {
-    // TODO(crbug.com/408057740): Clicking button should open form.
-  }
-
-  private onHideFeedbackToastClick() {
-    this.$.feedbackToast.hide();
-  }
-
   private onHideMessageToastClick() {
     this.$.messageToast.hide();
   }
diff --git a/chrome/browser/resources/on_device_internals/tools.ts b/chrome/browser/resources/on_device_internals/tools.ts
index 9ba1cfe8..ee5f33f0 100644
--- a/chrome/browser/resources/on_device_internals/tools.ts
+++ b/chrome/browser/resources/on_device_internals/tools.ts
@@ -289,6 +289,7 @@
     this.session_.append(
         {
           maxTokens: 0,
+          // TODO(crbug.com/416009528): This field is deprecated. Remove it.
           tokenOffset: 0,
           input: {pieces: textToInputPieces(this.contextText_)},
         },
@@ -407,6 +408,7 @@
     clonedSession.append(
         {
           maxTokens: 0,
+          // TODO(crbug.com/416009528): This field is deprecated. Remove it.
           tokenOffset: 0,
           input: {pieces: pieces},
         },
diff --git a/chrome/browser/resources/settings/glic_page/glic_page.html b/chrome/browser/resources/settings/glic_page/glic_page.html
index 09ec2fb..a535dc0 100644
--- a/chrome/browser/resources/settings/glic_page/glic_page.html
+++ b/chrome/browser/resources/settings/glic_page/glic_page.html
@@ -139,6 +139,13 @@
                 on-shortcut-updated="onFocusToggleShortcutUpdated_">
             </cr-shortcut-input>
           </div>
+          <settings-toggle-button
+              id="closedCaptionsToggle"
+              pref="{{prefs.glic.closed_captioning_enabled}}"
+              label="$i18n{glicClosedCaptionsToggle}"
+              sub-label="$i18n{glicClosedCaptionsToggleSublabel}"
+              on-settings-boolean-control-change="onClosedCaptionsToggleChange_">
+          </settings-toggle-button>
         </template>
       </div>
       <div class="section">
diff --git a/chrome/browser/resources/settings/glic_page/glic_page.ts b/chrome/browser/resources/settings/glic_page/glic_page.ts
index 8367233..355029dd 100644
--- a/chrome/browser/resources/settings/glic_page/glic_page.ts
+++ b/chrome/browser/resources/settings/glic_page/glic_page.ts
@@ -33,6 +33,7 @@
 import {getTemplate} from './glic_page.html.js';
 
 export enum SettingsGlicPageFeaturePrefName {
+  CLOSED_CAPTIONS_ENABLED = 'glic.closed_captioning_enabled',
   GEOLOCATION_ENABLED = 'glic.geolocation_enabled',
   LAUNCHER_ENABLED = 'glic.launcher_enabled',
   MICROPHONE_ENABLED = 'glic.microphone_enabled',
@@ -265,6 +266,12 @@
   private disallowedByAdminChanged_(disallowed: boolean) {
     this.disallowedByAdmin_ = disallowed;
   }
+
+  private onClosedCaptionsToggleChange_(event: Event) {
+    const enabled = (event.target as SettingsToggleButtonElement).checked;
+    this.metricsBrowserProxy_.recordAction(
+        'Glic.Settings.ClosedCaptions.' + (enabled ? 'Enabled' : 'Disabled'));
+  }
 }
 
 declare global {
diff --git a/chrome/browser/resources/side_panel/read_anything/app.ts b/chrome/browser/resources/side_panel/read_anything/app.ts
index beddbd29..d2f4dd3 100644
--- a/chrome/browser/resources/side_panel/read_anything/app.ts
+++ b/chrome/browser/resources/side_panel/read_anything/app.ts
@@ -28,6 +28,7 @@
 import {PauseActionSource, SpeechEngineState} from './read_aloud/speech_model.js';
 import type {SpeechPlayingState} from './read_aloud/speech_model.js';
 import {VoicePackController} from './read_aloud/voice_pack_controller.js';
+import type {VoiceLanguageListener} from './read_aloud/voice_pack_controller.js';
 import {WordBoundaries} from './read_aloud/word_boundaries.js';
 import type {WordBoundaryState} from './read_aloud/word_boundaries.js';
 import {ReadAnythingLogger, TimeFrom} from './read_anything_logger.js';
@@ -56,7 +57,8 @@
   };
 }
 
-export class AppElement extends AppElementBase implements SpeechListener {
+export class AppElement extends AppElementBase implements
+    SpeechListener, VoiceLanguageListener {
   static get is() {
     return 'read-anything-app';
   }
@@ -213,6 +215,7 @@
 
     if (this.isReadAloudEnabled_) {
       this.speechController_.addListener(this);
+      this.voicePackController_.addListener(this);
       this.notificationManager_.addListener(this.$.languageToast);
 
       // Clear state. We don't do this in disconnectedCallback because that's
@@ -816,9 +819,8 @@
     // Update application state
     this.updateApplicationState(lang, newVoicePackStatus);
 
-    if (isVoicePackStatusError(newVoicePackStatus) &&
-        this.voicePackController_.disableLangIfNoVoices(lang)) {
-      this.enabledLangs_ = this.voicePackController_.getEnabledLangs();
+    if (isVoicePackStatusError(newVoicePackStatus)) {
+      this.voicePackController_.disableLangIfNoVoices(lang);
     }
   }
 
@@ -840,7 +842,8 @@
         case VoicePackServerStatusSuccessCode.INSTALLED:
           // Force a refresh of the voices list since we might not get an update
           // the voices have changed.
-          this.getVoices_(/*forceRefresh=*/ true);
+          this.voicePackController_.refreshAvailableVoices(
+              /*forceRefresh=*/ true);
           this.autoSwitchVoice_(lang);
 
           // Some languages may require a download from the voice pack
@@ -906,7 +909,7 @@
 
   onVoicesChanged() {
     if (this.waitingForNewEngine_) {
-      this.enabledLangs_.forEach(lang => {
+      this.voicePackController_.getEnabledLangs().forEach(lang => {
         this.installVoicePackIfPossible(
             lang,
             /* onlyInstallExactGoogleLocaleMatch=*/ true,
@@ -919,15 +922,13 @@
     const hadAvailableVoices = this.voicePackController_.hasAvailableVoices();
     // Get a new list of voices. This should be done before we call
     // refreshVoicePackStatuses();
-    this.getVoices_(/*forceRefresh=*/ true);
+    this.voicePackController_.refreshAvailableVoices(/*forceRefresh=*/ true);
 
     // TODO: crbug.com/390435037 - Simplify logic around loading voices and
     // language availability, especially around the new TTS engine.
 
     // <if expr="not is_chromeos">
-    if (this.voicePackController_.enableNowAvailableLangs()) {
-      this.enabledLangs_ = this.voicePackController_.getEnabledLangs();
-    }
+    this.voicePackController_.enableNowAvailableLangs();
     // </if>
 
     if (!hadAvailableVoices && this.voicePackController_.hasAvailableVoices()) {
@@ -1060,12 +1061,8 @@
   }
 
   private getVoices_(forceRefresh: boolean = false): SpeechSynthesisVoice[] {
-    if (this.voicePackController_.refreshAvailableVoices(forceRefresh)) {
-      this.availableVoices_ = this.voicePackController_.getAvailableVoices();
-      this.localeToDisplayName_ =
-          this.voicePackController_.getDisplayNamesForLocaleCodes();
-    }
-    return this.availableVoices_;
+    this.voicePackController_.refreshAvailableVoices(forceRefresh);
+    return this.voicePackController_.getAvailableVoices();
   }
 
   protected onPreviewVoice_(
@@ -1126,6 +1123,16 @@
     }
   }
 
+  onEnabledLangsChange(): void {
+    this.enabledLangs_ = this.voicePackController_.getEnabledLangs();
+  }
+
+  onAvailableVoicesChange(): void {
+    this.availableVoices_ = this.voicePackController_.getAvailableVoices();
+    this.localeToDisplayName_ =
+        this.voicePackController_.getDisplayNamesForLocaleCodes();
+  }
+
   private logSpeechPlaySession_() {
     // Don't log a playback session just in case something has gotten out of
     // sync and we call stopSpeech before playSpeech.
@@ -1683,15 +1690,10 @@
       this.installVoicePackIfPossible(
           toggledLanguage, /* onlyInstallExactGoogleLocaleMatch=*/ true,
           /* retryIfPreviousInstallFailed= */ true);
+      this.voicePackController_.enableLang(toggledLanguage);
     } else {
       this.voicePackController_.uninstall(toggledLanguage);
-    }
-
-    const updateEnabledLangs = currentlyEnabled ?
-        this.voicePackController_.disableLang(toggledLanguage) :
-        this.voicePackController_.enableLang(toggledLanguage);
-    if (updateEnabledLangs) {
-      this.enabledLangs_ = this.voicePackController_.getEnabledLangs();
+      this.voicePackController_.disableLang(toggledLanguage);
     }
 
     chrome.readingMode.onLanguagePrefChange(toggledLanguage, !currentlyEnabled);
@@ -1746,17 +1748,10 @@
   }
 
   restoreEnabledLanguagesFromPref() {
-    // We need to make sure the languages we choose correspond to voices, so
-    // refresh the list of voices and available langs
-    this.getVoices_();
+    this.voicePackController_.restoreEnabledLanguagesFromPref(
+        this.defaultVoice()?.lang);
 
-    this.voicePackController_.setCurrentLanguage(
-        chrome.readingMode.baseLanguageForSpeech);
-    this.enabledLangs_ =
-        this.voicePackController_.getInitialListOfEnabledLanguages(
-            this.defaultVoice()?.lang);
-
-    for (const lang of this.enabledLangs_) {
+    for (const lang of this.voicePackController_.getEnabledLangs()) {
       this.installVoicePackIfPossible(
           lang, /* onlyInstallExactGoogleLocaleMatch=*/ true,
           /* retryIfPreviousInstallFailed= */ false);
@@ -1798,9 +1793,7 @@
         this.defaultVoice();
 
     // Enable the locale for the preferred voice for this language.
-    if (this.voicePackController_.enableLang(this.selectedVoice_?.lang)) {
-      this.enabledLangs_ = this.voicePackController_.getEnabledLangs();
-    }
+    this.voicePackController_.enableLang(this.selectedVoice_?.lang);
   }
 
   protected onLineSpacingChange_() {
@@ -1922,9 +1915,7 @@
 
     // Enable the locales so we can select a voice for the given language and
     // show it in the voice menu.
-    if (this.voicePackController_.enableLang(localeToEnable)) {
-      this.enabledLangs_ = this.voicePackController_.getEnabledLangs();
-    }
+    this.voicePackController_.enableLang(localeToEnable);
     this.selectPreferredVoice();
   }
 
diff --git a/chrome/browser/resources/side_panel/read_anything/read_aloud/voice_pack_controller.ts b/chrome/browser/resources/side_panel/read_anything/read_aloud/voice_pack_controller.ts
index 7cff1b1..a16a2e7 100644
--- a/chrome/browser/resources/side_panel/read_anything/read_aloud/voice_pack_controller.ts
+++ b/chrome/browser/resources/side_panel/read_anything/read_aloud/voice_pack_controller.ts
@@ -16,12 +16,17 @@
 
 import {VoicePackModel} from './voice_pack_model.js';
 
+export interface VoiceLanguageListener {
+  onEnabledLangsChange(): void;
+  onAvailableVoicesChange(): void;
+}
 
 export class VoicePackController {
   private notificationManager_: VoiceNotificationManager =
       VoiceNotificationManager.getInstance();
   private model_: VoicePackModel = new VoicePackModel();
   private speech_: SpeechBrowserProxy = SpeechBrowserProxyImpl.getInstance();
+  private listeners_: VoiceLanguageListener[] = [];
 
   // The extension is responsible for installing the Natural voices. If the
   // extension is not being responsive, the extension is probably not
@@ -29,6 +34,10 @@
   // if the extension does not respond in a timely manner.
   private speechExtensionResponseCallbackHandle_?: number;
 
+  addListener(listener: VoiceLanguageListener) {
+    this.listeners_.push(listener);
+  }
+
   getCurrentLanguage(): string {
     return this.model_.getCurrentLanguage();
   }
@@ -45,6 +54,11 @@
     return [...this.model_.getAvailableLangs()];
   }
 
+  setAvailableVoices(voices: SpeechSynthesisVoice[]): void {
+    this.model_.setAvailableVoices(voices);
+    this.listeners_.forEach(l => l.onAvailableVoicesChange());
+  }
+
   getAvailableVoices(): SpeechSynthesisVoice[] {
     return this.model_.getAvailableVoices();
   }
@@ -67,7 +81,7 @@
     }
   }
 
-  disableLangIfNoVoices(lang: string): boolean {
+  disableLangIfNoVoices(lang: string): void {
     const lowerLang = lang.toLowerCase();
     this.refreshAvailableVoices();
     const availableVoicesForLang = this.getAvailableVoicesForLang_(lowerLang);
@@ -88,28 +102,26 @@
         }
       });
     }
-
-    return disableLang;
   }
 
-  // Returns whether lang was enabled before disabling it.
-  disableLang(lang?: string): boolean {
+  disableLang(lang?: string): void {
     if (!lang) {
-      return false;
+      return;
     }
-    return this.model_.disableLang(lang);
+    if (this.isLangEnabled(lang)) {
+      this.model_.disableLang(lang);
+      this.listeners_.forEach(l => l.onEnabledLangsChange());
+    }
   }
 
-  // Returns whether lang was disabled before enabling it.
-  enableLang(lang?: string): boolean {
+  enableLang(lang?: string): void {
     if (!lang) {
-      return false;
+      return;
     }
     if (!this.isLangEnabled(lang)) {
       this.model_.enableLang(lang.toLowerCase());
-      return true;
+      this.listeners_.forEach(l => l.onEnabledLangsChange());
     }
-    return false;
   }
 
   isLangEnabled(lang: string): boolean {
@@ -122,7 +134,7 @@
   // happen on non-ChromeOS, since we're only installing the new engine
   // outside of ChromeOS.
   // <if expr="not is_chromeos">
-  enableNowAvailableLangs(): boolean {
+  enableNowAvailableLangs(): void {
     const nowAvailableLangs =
         [...this.model_.getPossiblyDisabledLangs()].filter(
             (lang: string) => this.isLangAvailable_(lang));
@@ -132,7 +144,6 @@
       chrome.readingMode.onLanguagePrefChange(lowerLang, true);
       this.model_.removePossiblyDisabledLang(lowerLang);
     });
-    return nowAvailableLangs.length > 0;
   }
 
   private isLangAvailable_(lang: string) {
@@ -140,24 +151,25 @@
   }
   // </if>
 
-  getInitialListOfEnabledLanguages(langOfDefaultVoice?: string): string[] {
+  restoreEnabledLanguagesFromPref(langOfDefaultVoice?: string): void {
+    // We need to make sure the languages we choose correspond to voices, so
+    // refresh the list of voices and available langs
+    this.refreshAvailableVoices();
+    this.setCurrentLanguage(chrome.readingMode.baseLanguageForSpeech);
     const storedLanguagesPref = chrome.readingMode.getLanguagesEnabledInPref();
     const langs = createInitialListOfEnabledLanguages(
         chrome.readingMode.baseLanguageForSpeech, storedLanguagesPref,
         this.getAvailableLangs(), langOfDefaultVoice);
     this.alignPreferencesWithEnabledLangs_(storedLanguagesPref);
     langs.forEach((l: string) => this.enableLang(l));
-    return langs;
   }
 
-  refreshAvailableVoices(forceRefresh: boolean = false): boolean {
+  refreshAvailableVoices(forceRefresh: boolean = false): void {
     if (!this.hasAvailableVoices() || forceRefresh) {
       const availableVoices = getFilteredVoiceList(this.speech_.getVoices());
-      this.model_.setAvailableVoices(availableVoices);
+      this.setAvailableVoices(availableVoices);
       this.model_.setAvailableLangs(availableVoices.map(({lang}) => lang));
-      return true;
     }
-    return false;
   }
 
   getDisplayNamesForLocaleCodes(): {[locale: string]: string} {
diff --git a/chrome/browser/resources/side_panel/read_anything/read_aloud/voice_pack_model.ts b/chrome/browser/resources/side_panel/read_anything/read_aloud/voice_pack_model.ts
index c528013..c2f6656 100644
--- a/chrome/browser/resources/side_panel/read_anything/read_aloud/voice_pack_model.ts
+++ b/chrome/browser/resources/side_panel/read_anything/read_aloud/voice_pack_model.ts
@@ -63,8 +63,8 @@
     this.enabledLangs_.add(lang);
   }
 
-  disableLang(lang: string): boolean {
-    return this.enabledLangs_.delete(lang);
+  disableLang(lang: string): void {
+    this.enabledLangs_.delete(lang);
   }
 
   getAvailableLangs(): Set<string> {
diff --git a/chrome/browser/resources/side_panel/read_anything/read_anything.ts b/chrome/browser/resources/side_panel/read_anything/read_anything.ts
index f7a0fc5..152287ef 100644
--- a/chrome/browser/resources/side_panel/read_anything/read_anything.ts
+++ b/chrome/browser/resources/side_panel/read_anything/read_anything.ts
@@ -26,7 +26,7 @@
 export {currentReadHighlightClass, previousReadHighlightClass, ReadAloudHighlighter} from './read_aloud/highlighter.js';
 export {SpeechController, SpeechListener} from './read_aloud/speech_controller.js';
 export {PauseActionSource, SpeechEngineState, SpeechModel} from './read_aloud/speech_model.js';
-export {VoicePackController} from './read_aloud/voice_pack_controller.js';
+export {VoiceLanguageListener, VoicePackController} from './read_aloud/voice_pack_controller.js';
 export {VoicePackModel} from './read_aloud/voice_pack_model.js';
 export type {WordBoundaryState} from './read_aloud/word_boundaries.js';
 export {WordBoundaries} from './read_aloud/word_boundaries.js';
diff --git a/chrome/browser/safe_browsing/android/java/src/org/chromium/chrome/browser/safe_browsing/settings/EnhancedProtectionSettingsFragment.java b/chrome/browser/safe_browsing/android/java/src/org/chromium/chrome/browser/safe_browsing/settings/EnhancedProtectionSettingsFragment.java
index 672894e..efab943 100644
--- a/chrome/browser/safe_browsing/android/java/src/org/chromium/chrome/browser/safe_browsing/settings/EnhancedProtectionSettingsFragment.java
+++ b/chrome/browser/safe_browsing/android/java/src/org/chromium/chrome/browser/safe_browsing/settings/EnhancedProtectionSettingsFragment.java
@@ -46,4 +46,9 @@
     private void onLearnMoreClicked(View view) {
         getCustomTabLauncher().openUrlInCct(getContext(), SAFE_BROWSING_IN_CHROME_URL);
     }
+
+    @Override
+    public @AnimationType int getAnimationType() {
+        return AnimationType.PROPERTY;
+    }
 }
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 e5ec9c6..ec9611a 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
@@ -282,4 +282,9 @@
         }
         RecordUserAction.record("SafeBrowsing.Settings." + userActionSuffix);
     }
+
+    @Override
+    public @AnimationType int getAnimationType() {
+        return AnimationType.PROPERTY;
+    }
 }
diff --git a/chrome/browser/safe_browsing/android/java/src/org/chromium/chrome/browser/safe_browsing/settings/StandardProtectionSettingsFragment.java b/chrome/browser/safe_browsing/android/java/src/org/chromium/chrome/browser/safe_browsing/settings/StandardProtectionSettingsFragment.java
index 41e26f47..468e218 100644
--- a/chrome/browser/safe_browsing/android/java/src/org/chromium/chrome/browser/safe_browsing/settings/StandardProtectionSettingsFragment.java
+++ b/chrome/browser/safe_browsing/android/java/src/org/chromium/chrome/browser/safe_browsing/settings/StandardProtectionSettingsFragment.java
@@ -96,4 +96,9 @@
             }
         };
     }
+
+    @Override
+    public @AnimationType int getAnimationType() {
+        return AnimationType.PROPERTY;
+    }
 }
diff --git a/chrome/browser/safety_hub/android/BUILD.gn b/chrome/browser/safety_hub/android/BUILD.gn
index 542c338..709fa7d 100644
--- a/chrome/browser/safety_hub/android/BUILD.gn
+++ b/chrome/browser/safety_hub/android/BUILD.gn
@@ -77,6 +77,7 @@
     "java/src/org/chromium/chrome/browser/safety_hub/SafetyHubReusedPasswordsModuleHelper.java",
     "java/src/org/chromium/chrome/browser/safety_hub/SafetyHubSafeBrowsingModuleMediator.java",
     "java/src/org/chromium/chrome/browser/safety_hub/SafetyHubSubpageFragment.java",
+    "java/src/org/chromium/chrome/browser/safety_hub/SafetyHubUnavailablePasswordsModuleHelper.java",
     "java/src/org/chromium/chrome/browser/safety_hub/SafetyHubUpdateCheckModuleMediator.java",
     "java/src/org/chromium/chrome/browser/safety_hub/SafetyHubUtils.java",
     "java/src/org/chromium/chrome/browser/safety_hub/SafetyHubWeakPasswordsModuleHelper.java",
diff --git a/chrome/browser/safety_hub/android/java/res/layout/safety_hub_expandable_preference.xml b/chrome/browser/safety_hub/android/java/res/layout/safety_hub_expandable_preference.xml
index d74937d..0725cca 100644
--- a/chrome/browser/safety_hub/android/java/res/layout/safety_hub_expandable_preference.xml
+++ b/chrome/browser/safety_hub/android/java/res/layout/safety_hub_expandable_preference.xml
@@ -68,7 +68,7 @@
                 android:layout_marginTop="4dp"
                 android:layout_alignStart="@android:id/title"
                 android:scrollbars="none"
-                android:focusable="true"
+                android:screenReaderFocusable="true"
                 style="@style/PreferenceSummary" />
         </RelativeLayout>
 
diff --git a/chrome/browser/safety_hub/android/java/res/xml/safety_hub_preferences.xml b/chrome/browser/safety_hub/android/java/res/xml/safety_hub_preferences.xml
index 8be3af0..5f2cf7a 100644
--- a/chrome/browser/safety_hub/android/java/res/xml/safety_hub_preferences.xml
+++ b/chrome/browser/safety_hub/android/java/res/xml/safety_hub_preferences.xml
@@ -16,7 +16,8 @@
     <org.chromium.components.browser_ui.settings.TextMessagePreference
         android:key="safety_hub_description"
         android:summary="@string/safety_hub_description"
-        app:allowDividerAbove="false" />
+        app:allowDividerAbove="false"
+        android:screenReaderFocusable="true" />
 
     <PreferenceCategory
         android:key="safety_modules"
diff --git a/chrome/browser/safety_hub/android/java/src/org/chromium/chrome/browser/safety_hub/SafetyHubExpandablePreference.java b/chrome/browser/safety_hub/android/java/src/org/chromium/chrome/browser/safety_hub/SafetyHubExpandablePreference.java
index acafe8e..4edec3d6 100644
--- a/chrome/browser/safety_hub/android/java/src/org/chromium/chrome/browser/safety_hub/SafetyHubExpandablePreference.java
+++ b/chrome/browser/safety_hub/android/java/src/org/chromium/chrome/browser/safety_hub/SafetyHubExpandablePreference.java
@@ -13,6 +13,7 @@
 import android.view.View;
 import android.view.View.AccessibilityDelegate;
 import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.ProgressBar;
 import android.widget.TextView;
@@ -179,11 +180,14 @@
         String description =
                 getContext()
                         .getString(
-                                R.string.concat_two_strings_with_periods,
+                                R.string.concat_two_strings_with_comma,
                                 getTitle(),
                                 collapseOrExpandedText);
 
         view.setContentDescription(description);
+        if (view.isAccessibilityFocused()) {
+            view.sendAccessibilityEvent(AccessibilityEvent.CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION);
+        }
     }
 
     private AccessibilityDelegate createButtonAccessibilityDelegate(View labelView) {
diff --git a/chrome/browser/safety_hub/android/java/src/org/chromium/chrome/browser/safety_hub/SafetyHubPasswordsModuleMediator.java b/chrome/browser/safety_hub/android/java/src/org/chromium/chrome/browser/safety_hub/SafetyHubPasswordsModuleMediator.java
index 35dcebd..90db06e6 100644
--- a/chrome/browser/safety_hub/android/java/src/org/chromium/chrome/browser/safety_hub/SafetyHubPasswordsModuleMediator.java
+++ b/chrome/browser/safety_hub/android/java/src/org/chromium/chrome/browser/safety_hub/SafetyHubPasswordsModuleMediator.java
@@ -87,7 +87,6 @@
             @SafetyHubLocalPasswordsDataSource.ModuleType int localModuleType) {
         Context context = mPreference.getContext();
 
-        // TODO(crbug.com/407930886): Add all states for account and local passwords.
         if (accountModuleType
                         == SafetyHubAccountPasswordsDataSource.ModuleType.HAS_COMPROMISED_PASSWORDS
                 || localModuleType
@@ -100,6 +99,16 @@
                     /* unifiedModule= */ true);
         }
 
+        if (accountModuleType
+                        == SafetyHubAccountPasswordsDataSource.ModuleType.UNAVAILABLE_PASSWORDS
+                || accountModuleType
+                        == SafetyHubAccountPasswordsDataSource.ModuleType
+                                .UNAVAILABLE_COMPROMISED_NO_WEAK_REUSED_PASSWORDS
+                || localModuleType
+                        == SafetyHubLocalPasswordsDataSource.ModuleType.UNAVAILABLE_PASSWORDS) {
+            return new SafetyHubUnavailablePasswordsModuleHelper(context, mModuleDelegate);
+        }
+
         if (accountModuleType == SafetyHubAccountPasswordsDataSource.ModuleType.HAS_REUSED_PASSWORDS
                 || localModuleType
                         == SafetyHubLocalPasswordsDataSource.ModuleType.HAS_REUSED_PASSWORDS) {
@@ -133,6 +142,7 @@
                     /* unifiedModule= */ true);
         }
 
+        // TODO(crbug.com/407930886): Add no password state for account and local passwords.
         return new SafetyHubAccountPasswordsUnavailableAllPasswordsModuleHelper(
                 context, mModuleDelegate);
     }
diff --git a/chrome/browser/safety_hub/android/java/src/org/chromium/chrome/browser/safety_hub/SafetyHubUnavailablePasswordsModuleHelper.java b/chrome/browser/safety_hub/android/java/src/org/chromium/chrome/browser/safety_hub/SafetyHubUnavailablePasswordsModuleHelper.java
new file mode 100644
index 0000000..50efc94
--- /dev/null
+++ b/chrome/browser/safety_hub/android/java/src/org/chromium/chrome/browser/safety_hub/SafetyHubUnavailablePasswordsModuleHelper.java
@@ -0,0 +1,66 @@
+// 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.safety_hub;
+
+import android.content.Context;
+import android.view.View;
+
+import androidx.annotation.Nullable;
+
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.chrome.browser.safety_hub.SafetyHubModuleMediator.ModuleState;
+
+/**
+ * Helper for the {@link SafetyHubPasswordsModule} for when passwords counts are unavailable module.
+ */
+@NullMarked
+public class SafetyHubUnavailablePasswordsModuleHelper implements SafetyHubModuleHelper {
+    private final Context mContext;
+    private final SafetyHubModuleDelegate mModuleDelegate;
+
+    SafetyHubUnavailablePasswordsModuleHelper(
+            Context context, SafetyHubModuleDelegate moduleDelegate) {
+        mContext = context;
+        mModuleDelegate = moduleDelegate;
+    }
+
+    @Override
+    public String getTitle() {
+        return mContext.getString(R.string.safety_hub_password_check_unavailable_title);
+    }
+
+    @Override
+    public String getSummary() {
+        return mContext.getString(R.string.safety_hub_unavailable_summary);
+    }
+
+    @Override
+    public @Nullable String getPrimaryButtonText() {
+        return null;
+    }
+
+    @Override
+    public @Nullable View.OnClickListener getPrimaryButtonListener() {
+        return null;
+    }
+
+    @Override
+    public String getSecondaryButtonText() {
+        return mContext.getString(R.string.safety_hub_password_subpage_navigation_button);
+    }
+
+    @Override
+    public View.OnClickListener getSecondaryButtonListener() {
+        return v -> {
+            // TODO(crbug.com/407931779): Change to open the SH passwords page.
+            mModuleDelegate.showLocalPasswordCheckUi(mContext);
+        };
+    }
+
+    @Override
+    public @ModuleState int getModuleState() {
+        return ModuleState.UNAVAILABLE;
+    }
+}
diff --git a/chrome/browser/safety_hub/android/javatests/src/org/chromium/chrome/browser/safety_hub/SafetyHubTest.java b/chrome/browser/safety_hub/android/javatests/src/org/chromium/chrome/browser/safety_hub/SafetyHubTest.java
index d938f0f5..b11bbf0 100644
--- a/chrome/browser/safety_hub/android/javatests/src/org/chromium/chrome/browser/safety_hub/SafetyHubTest.java
+++ b/chrome/browser/safety_hub/android/javatests/src/org/chromium/chrome/browser/safety_hub/SafetyHubTest.java
@@ -1512,6 +1512,7 @@
         verifyButtonsNextToTextVisibility(safeBrowsingTitle, true);
 
         clearLocalCompromisedPasswordsCount();
+        clearAccountCompromisedPasswordsCount();
         setLocalPasswordCheckTimestamp(0);
     }
 
@@ -1553,6 +1554,7 @@
         verifyButtonsNextToTextVisibility(safeBrowsingTitle, true);
 
         clearLocalCompromisedPasswordsCount();
+        clearAccountCompromisedPasswordsCount();
         setLocalPasswordCheckTimestamp(0);
     }
 
@@ -1593,6 +1595,7 @@
         verifyButtonsNextToTextVisibility(safeBrowsingTitle, true);
 
         clearLocalCompromisedPasswordsCount();
+        clearAccountCompromisedPasswordsCount();
         setLocalPasswordCheckTimestamp(0);
     }
 
@@ -1605,7 +1608,7 @@
         ChromeFeatureList.SAFETY_HUB_LOCAL_PASSWORDS_MODULE,
         ChromeFeatureList.SAFETY_HUB_UNIFIED_PASSWORDS_MODULE
     })
-    public void testUnifiedPasswordsModule_NoCompromisedPasswords() {
+    public void testUnifiedPasswordsModule_NoAccountAndLocalCompromisedPasswords() {
         setAccountCompromisedPasswordsCount(0);
         setLocalCompromisedPasswordsCount(0);
         setAccountReusedPasswordsCount(0);
@@ -1619,8 +1622,7 @@
         mSafetyHubFragmentTestRule.startSettingsActivity();
         SafetyHubFragment safetyHubFragment = mSafetyHubFragmentTestRule.getFragment();
 
-        // Verify that unified passwords module which is in the safe state is collapsed by
-        // default.
+        // Verify that unified passwords module which is in the safe state is collapsed by default.
         String noCompromisedPasswordsTitle =
                 safetyHubFragment.getString(R.string.safety_hub_no_compromised_passwords_title);
         scrollToExpandedPreference(noCompromisedPasswordsTitle);
@@ -1633,6 +1635,42 @@
         verifyButtonsNextToTextVisibility(safeBrowsingTitle, true);
 
         clearLocalCompromisedPasswordsCount();
+        clearAccountCompromisedPasswordsCount();
+        setLocalPasswordCheckTimestamp(0);
+    }
+
+    @Test
+    @MediumTest
+    @Policies.Add({@Policies.Item(key = "SafeBrowsingEnabled", string = "false")})
+    @Restriction(GmsCoreVersionRestriction.RESTRICTION_TYPE_VERSION_GE_24W15)
+    @Features.EnableFeatures({
+        ChromeFeatureList.SAFETY_HUB_WEAK_AND_REUSED_PASSWORDS,
+        ChromeFeatureList.SAFETY_HUB_LOCAL_PASSWORDS_MODULE,
+        ChromeFeatureList.SAFETY_HUB_UNIFIED_PASSWORDS_MODULE
+    })
+    public void testUnifiedPasswordsModule_AccountAndLocalPasswordsUnavailable() {
+        clearLocalCompromisedPasswordsCount();
+        clearAccountCompromisedPasswordsCount();
+        addCredentialToProfileStore();
+        addCredentialToAccountStore();
+
+        mSafetyHubFragmentTestRule.startSettingsActivity();
+        SafetyHubFragment safetyHubFragment = mSafetyHubFragmentTestRule.getFragment();
+
+        // Verify that unified passwords module which is in the unavailable state is expanded by
+        // default.
+        String unavailablePasswordsTitle =
+                safetyHubFragment.getString(R.string.safety_hub_password_check_unavailable_title);
+        scrollToExpandedPreference(unavailablePasswordsTitle);
+        verifyButtonsNextToTextVisibility(unavailablePasswordsTitle, true);
+
+        // Verify the information module is expanded.
+        String safeBrowsingTitle =
+                safetyHubFragment.getString(R.string.prefs_safe_browsing_no_protection_summary);
+        scrollToPreference(withText(safeBrowsingTitle));
+        verifyButtonsNextToTextVisibility(safeBrowsingTitle, true);
+
+        clearLocalCompromisedPasswordsCount();
         setLocalPasswordCheckTimestamp(0);
     }
 
diff --git a/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubPasswordsModuleMediatorTest.java b/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubPasswordsModuleMediatorTest.java
index 7894f74b..fbb3a3a 100644
--- a/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubPasswordsModuleMediatorTest.java
+++ b/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubPasswordsModuleMediatorTest.java
@@ -143,7 +143,7 @@
     }
 
     @Test
-    public void compromisedAccountPasswordsExist() {
+    public void compromisedAccountPasswordsExist_weakAndReuseExist() {
         int compromisedAccountPasswordsCount = 5;
         mockAccountPasswordCounts(compromisedAccountPasswordsCount, /* reused= */ 2, /* weak= */ 1);
         mockLocalPasswordCounts(/* compromised= */ 0, /* reused= */ 2, /* weak= */ 1);
@@ -152,10 +152,9 @@
                 SafetyHubAccountPasswordsDataSource.ModuleType.HAS_COMPROMISED_PASSWORDS);
         mModuleMediator.accountPasswordsStateChanged(
                 SafetyHubAccountPasswordsDataSource.ModuleType.HAS_COMPROMISED_PASSWORDS);
-        mockLocalPasswordState(
-                SafetyHubLocalPasswordsDataSource.ModuleType.NO_COMPROMISED_PASSWORDS);
+        mockLocalPasswordState(SafetyHubLocalPasswordsDataSource.ModuleType.HAS_REUSED_PASSWORDS);
         mModuleMediator.localPasswordsStateChanged(
-                SafetyHubLocalPasswordsDataSource.ModuleType.NO_COMPROMISED_PASSWORDS);
+                SafetyHubLocalPasswordsDataSource.ModuleType.HAS_REUSED_PASSWORDS);
 
         verify(mMediatorDelegateMock, times(1)).onUpdateNeeded();
 
@@ -186,15 +185,100 @@
     }
 
     @Test
-    public void compromisedLocalPasswordsExist() {
+    public void compromisedAccountPasswordsExist_unavailableLocalPasswords() {
+        int compromisedAccountPasswordsCount = 5;
+        mockAccountPasswordCounts(compromisedAccountPasswordsCount, /* reused= */ 2, /* weak= */ 1);
+        mockLocalPasswordCounts(/* compromised= */ -1, /* reused= */ -1, /* weak= */ -1);
+
+        mockAccountPasswordState(
+                SafetyHubAccountPasswordsDataSource.ModuleType.HAS_COMPROMISED_PASSWORDS);
+        mModuleMediator.accountPasswordsStateChanged(
+                SafetyHubAccountPasswordsDataSource.ModuleType.HAS_COMPROMISED_PASSWORDS);
+        mockLocalPasswordState(SafetyHubLocalPasswordsDataSource.ModuleType.UNAVAILABLE_PASSWORDS);
+        mModuleMediator.localPasswordsStateChanged(
+                SafetyHubLocalPasswordsDataSource.ModuleType.UNAVAILABLE_PASSWORDS);
+
+        verify(mMediatorDelegateMock, times(1)).onUpdateNeeded();
+
+        String expectedTitle =
+                mActivity
+                        .getResources()
+                        .getQuantityString(
+                                R.plurals.safety_hub_account_passwords_compromised_exist,
+                                compromisedAccountPasswordsCount,
+                                compromisedAccountPasswordsCount);
+        String expectedSummary =
+                mActivity
+                        .getResources()
+                        .getQuantityString(
+                                R.plurals.safety_hub_compromised_passwords_summary,
+                                compromisedAccountPasswordsCount,
+                                compromisedAccountPasswordsCount);
+        String expectedPrimaryButtonText =
+                mActivity.getString(R.string.safety_hub_passwords_navigation_button);
+        String expectedSecondaryButtonText =
+                mActivity.getString(R.string.safety_hub_password_subpage_navigation_button);
+
+        assertEquals(expectedTitle, mPreference.getTitle().toString());
+        assertEquals(expectedSummary, mPreference.getSummary().toString());
+        assertEquals(WARNING_ICON, shadowOf(mPreference.getIcon()).getCreatedFromResId());
+        assertEquals(expectedPrimaryButtonText, mPreference.getPrimaryButtonText());
+        assertEquals(expectedSecondaryButtonText, mPreference.getSecondaryButtonText());
+    }
+
+    @Test
+    public void compromisedLocalPasswordsExist_weakAndReuseExist() {
         int compromisedLocalPasswordsCount = 5;
         mockAccountPasswordCounts(/* compromised= */ 0, /* reused= */ 2, /* weak= */ 1);
         mockLocalPasswordCounts(compromisedLocalPasswordsCount, /* reused= */ 2, /* weak= */ 1);
 
         mockAccountPasswordState(
-                SafetyHubAccountPasswordsDataSource.ModuleType.NO_COMPROMISED_PASSWORDS);
+                SafetyHubAccountPasswordsDataSource.ModuleType.HAS_REUSED_PASSWORDS);
         mModuleMediator.accountPasswordsStateChanged(
-                SafetyHubAccountPasswordsDataSource.ModuleType.NO_COMPROMISED_PASSWORDS);
+                SafetyHubAccountPasswordsDataSource.ModuleType.HAS_REUSED_PASSWORDS);
+        mockLocalPasswordState(
+                SafetyHubLocalPasswordsDataSource.ModuleType.HAS_COMPROMISED_PASSWORDS);
+        mModuleMediator.localPasswordsStateChanged(
+                SafetyHubLocalPasswordsDataSource.ModuleType.HAS_COMPROMISED_PASSWORDS);
+
+        verify(mMediatorDelegateMock, times(1)).onUpdateNeeded();
+
+        String expectedTitle =
+                mActivity
+                        .getResources()
+                        .getQuantityString(
+                                R.plurals.safety_hub_local_passwords_compromised_title,
+                                compromisedLocalPasswordsCount,
+                                compromisedLocalPasswordsCount);
+        String expectedSummary =
+                mActivity
+                        .getResources()
+                        .getQuantityString(
+                                R.plurals.safety_hub_compromised_passwords_summary,
+                                compromisedLocalPasswordsCount,
+                                compromisedLocalPasswordsCount);
+        String expectedPrimaryButtonText =
+                mActivity.getString(R.string.safety_hub_passwords_navigation_button);
+        String expectedSecondaryButtonText =
+                mActivity.getString(R.string.safety_hub_password_subpage_navigation_button);
+
+        assertEquals(expectedTitle, mPreference.getTitle().toString());
+        assertEquals(expectedSummary, mPreference.getSummary().toString());
+        assertEquals(WARNING_ICON, shadowOf(mPreference.getIcon()).getCreatedFromResId());
+        assertEquals(expectedPrimaryButtonText, mPreference.getPrimaryButtonText());
+        assertEquals(expectedSecondaryButtonText, mPreference.getSecondaryButtonText());
+    }
+
+    @Test
+    public void compromisedLocalPasswordsExist_unavailableAccountPasswords() {
+        int compromisedLocalPasswordsCount = 5;
+        mockAccountPasswordCounts(/* compromised= */ -1, /* reused= */ -1, /* weak= */ -1);
+        mockLocalPasswordCounts(compromisedLocalPasswordsCount, /* reused= */ 2, /* weak= */ 1);
+
+        mockAccountPasswordState(
+                SafetyHubAccountPasswordsDataSource.ModuleType.UNAVAILABLE_PASSWORDS);
+        mModuleMediator.accountPasswordsStateChanged(
+                SafetyHubAccountPasswordsDataSource.ModuleType.UNAVAILABLE_PASSWORDS);
         mockLocalPasswordState(
                 SafetyHubLocalPasswordsDataSource.ModuleType.HAS_COMPROMISED_PASSWORDS);
         mModuleMediator.localPasswordsStateChanged(
@@ -477,4 +561,79 @@
         assertEquals(expectedPrimaryButtonText, mPreference.getPrimaryButtonText());
         assertEquals(expectedSecondaryButtonText, mPreference.getSecondaryButtonText());
     }
+
+    @Test
+    public void unavailableLocalPasswords_reusedAccountPasswordsExists() {
+        mockAccountPasswordState(
+                SafetyHubAccountPasswordsDataSource.ModuleType.HAS_REUSED_PASSWORDS);
+        mockLocalPasswordState(SafetyHubLocalPasswordsDataSource.ModuleType.UNAVAILABLE_PASSWORDS);
+        mModuleMediator.accountPasswordsStateChanged(
+                SafetyHubAccountPasswordsDataSource.ModuleType.HAS_REUSED_PASSWORDS);
+        mModuleMediator.localPasswordsStateChanged(
+                SafetyHubLocalPasswordsDataSource.ModuleType.UNAVAILABLE_PASSWORDS);
+
+        verify(mMediatorDelegateMock, times(1)).onUpdateNeeded();
+
+        String expectedTitle =
+                mActivity.getString(R.string.safety_hub_password_check_unavailable_title);
+        String expectedSummary = mActivity.getString(R.string.safety_hub_unavailable_summary);
+        String expectedSecondaryButtonText =
+                mActivity.getString(R.string.safety_hub_password_subpage_navigation_button);
+
+        assertEquals(expectedTitle, mPreference.getTitle().toString());
+        assertEquals(expectedSummary, mPreference.getSummary().toString());
+        assertEquals(INFO_ICON, shadowOf(mPreference.getIcon()).getCreatedFromResId());
+        assertNull(mPreference.getPrimaryButtonText());
+        assertEquals(expectedSecondaryButtonText, mPreference.getSecondaryButtonText());
+    }
+
+    @Test
+    public void unavailableAccountPasswords_reusedLocalPasswordsExists() {
+        mockAccountPasswordState(
+                SafetyHubAccountPasswordsDataSource.ModuleType.UNAVAILABLE_PASSWORDS);
+        mockLocalPasswordState(SafetyHubLocalPasswordsDataSource.ModuleType.HAS_REUSED_PASSWORDS);
+        mModuleMediator.accountPasswordsStateChanged(
+                SafetyHubAccountPasswordsDataSource.ModuleType.UNAVAILABLE_PASSWORDS);
+        mModuleMediator.localPasswordsStateChanged(
+                SafetyHubLocalPasswordsDataSource.ModuleType.HAS_REUSED_PASSWORDS);
+
+        verify(mMediatorDelegateMock, times(1)).onUpdateNeeded();
+
+        String expectedTitle =
+                mActivity.getString(R.string.safety_hub_password_check_unavailable_title);
+        String expectedSummary = mActivity.getString(R.string.safety_hub_unavailable_summary);
+        String expectedSecondaryButtonText =
+                mActivity.getString(R.string.safety_hub_password_subpage_navigation_button);
+
+        assertEquals(expectedTitle, mPreference.getTitle().toString());
+        assertEquals(expectedSummary, mPreference.getSummary().toString());
+        assertEquals(INFO_ICON, shadowOf(mPreference.getIcon()).getCreatedFromResId());
+        assertNull(mPreference.getPrimaryButtonText());
+        assertEquals(expectedSecondaryButtonText, mPreference.getSecondaryButtonText());
+    }
+
+    @Test
+    public void unavailableAccountAndLocalPasswords() {
+        mockAccountPasswordState(
+                SafetyHubAccountPasswordsDataSource.ModuleType.UNAVAILABLE_PASSWORDS);
+        mockLocalPasswordState(SafetyHubLocalPasswordsDataSource.ModuleType.UNAVAILABLE_PASSWORDS);
+        mModuleMediator.accountPasswordsStateChanged(
+                SafetyHubAccountPasswordsDataSource.ModuleType.UNAVAILABLE_PASSWORDS);
+        mModuleMediator.localPasswordsStateChanged(
+                SafetyHubLocalPasswordsDataSource.ModuleType.UNAVAILABLE_PASSWORDS);
+
+        verify(mMediatorDelegateMock, times(1)).onUpdateNeeded();
+
+        String expectedTitle =
+                mActivity.getString(R.string.safety_hub_password_check_unavailable_title);
+        String expectedSummary = mActivity.getString(R.string.safety_hub_unavailable_summary);
+        String expectedSecondaryButtonText =
+                mActivity.getString(R.string.safety_hub_password_subpage_navigation_button);
+
+        assertEquals(expectedTitle, mPreference.getTitle().toString());
+        assertEquals(expectedSummary, mPreference.getSummary().toString());
+        assertEquals(INFO_ICON, shadowOf(mPreference.getIcon()).getCreatedFromResId());
+        assertNull(mPreference.getPrimaryButtonText());
+        assertEquals(expectedSecondaryButtonText, mPreference.getSecondaryButtonText());
+    }
 }
diff --git a/chrome/browser/screen_ai/optical_character_recognizer_browsertest.cc b/chrome/browser/screen_ai/optical_character_recognizer_browsertest.cc
index 38dd0d32..998cdd11 100644
--- a/chrome/browser/screen_ai/optical_character_recognizer_browsertest.cc
+++ b/chrome/browser/screen_ai/optical_character_recognizer_browsertest.cc
@@ -371,12 +371,6 @@
   histograms.ExpectBucketCount("Accessibility.ScreenAI.OCR.LinesCount",
                                expected_lines_count, expected_calls);
 
-  histograms.ExpectTotalCount("Accessibility.ScreenAI.OCR.ImageSize10M",
-                              expected_calls);
-  histograms.ExpectBucketCount("Accessibility.ScreenAI.OCR.ImageSize10M",
-                               bitmap.width() * bitmap.height(),
-                               expected_calls);
-
   // Expect measured latency, but we don't know how long it taskes to process.
   // So we just check the total count of the expected bucket determined by the
   // image dimensions, with threshold 2048 for each dimension.
@@ -386,6 +380,8 @@
       "Accessibility.ScreenAI.OCR.Latency.NotDownsampled", expected_calls);
   histograms.ExpectTotalCount("Accessibility.ScreenAI.OCR.Latency.Downsampled",
                               0);
+  histograms.ExpectTotalCount(
+      "Accessibility.ScreenAI.OCR.Downsampled.ClientType", 0);
 
   // PDF Specific metrics should not be recorded as the client type is test.
   histograms.ExpectTotalCount("Accessibility.ScreenAI.OCR.LinesCount.PDF", 0);
@@ -663,6 +659,8 @@
 #endif
 IN_PROC_BROWSER_TEST_F(OpticalCharacterRecognizerResultsTest,
                        MAYBE_PerformOCRLargeImage) {
+  base::HistogramTester histograms;
+
   base::ScopedAllowBlockingForTesting allow_blocking;
 
   ASSERT_TRUE(CreateAndInitOCR());
@@ -679,6 +677,10 @@
   // Since OCR downsamples large images, the content of this image becomes quite
   // small and unreadable, hence nothing is recognized.
   EXPECT_FALSE(results->lines.size());
+
+  metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
+  histograms.ExpectTotalCount(
+      "Accessibility.ScreenAI.OCR.Downsampled.ClientType", 1);
 }
 
 IN_PROC_BROWSER_TEST_F(OpticalCharacterRecognizerResultsTest,
diff --git a/chrome/browser/search_engines/template_url_service_sync_unittest.cc b/chrome/browser/search_engines/template_url_service_sync_unittest.cc
index 1815361..7b0e68b 100644
--- a/chrome/browser/search_engines/template_url_service_sync_unittest.cc
+++ b/chrome/browser/search_engines/template_url_service_sync_unittest.cc
@@ -1295,29 +1295,6 @@
             model()->GetTemplateURLForKeyword(kCommonKeyword));
 }
 
-TEST_F(TemplateURLServiceSyncTest, SyncMergeDeletesDefault) {
-  // If the value from Sync is a duplicate of the local default and is newer, it
-  // should safely replace the local value and set as the new default.
-  TemplateURL* default_turl = model()->Add(
-      CreateTestTemplateURL(u"key1", "http://key1.com/{searchTerms}",
-                            "whateverguid", base::Time::FromTimeT(10)));
-  model()->SetUserSelectedDefaultSearchProvider(default_turl);
-
-  syncer::SyncDataList initial_data = CreateInitialSyncData();
-  // The guid1 entry should be a duplicate of the default.
-  std::unique_ptr<TemplateURL> turl(
-      CreateTestTemplateURL(u"key1", "http://key1.com/{searchTerms}", "guid1",
-                            base::Time::FromTimeT(90)));
-  initial_data[0] =
-      TemplateURLService::CreateSyncDataFromTemplateURLData(turl->data());
-  MergeAndExpectNotify(initial_data, 1);
-
-  EXPECT_EQ(3U, model()->GetAllSyncData(syncer::SEARCH_ENGINES).size());
-  EXPECT_FALSE(model()->GetTemplateURLForGUID("whateverguid"));
-  EXPECT_EQ(model()->GetDefaultSearchProvider(),
-            model()->GetTemplateURLForGUID("guid1"));
-}
-
 TEST_F(TemplateURLServiceSyncTest, DeleteBogusData) {
   // Create a couple of bogus entries to sync.
   syncer::SyncDataList initial_data;
@@ -3605,6 +3582,30 @@
             "localkey3");
 }
 
+TEST_F(TemplateURLServiceSyncTestWithoutSeparateLocalAndAccountSearchEngines,
+       SyncMergeDeletesDefault) {
+  // If the value from Sync is a duplicate of the local default and is newer, it
+  // should safely replace the local value and set as the new default.
+  TemplateURL* default_turl = model()->Add(
+      CreateTestTemplateURL(u"key1", "http://key1.com/{searchTerms}",
+                            "whateverguid", base::Time::FromTimeT(10)));
+  model()->SetUserSelectedDefaultSearchProvider(default_turl);
+
+  syncer::SyncDataList initial_data = CreateInitialSyncData();
+  // The guid1 entry should be a duplicate of the default.
+  std::unique_ptr<TemplateURL> turl(
+      CreateTestTemplateURL(u"key1", "http://key1.com/{searchTerms}", "guid1",
+                            base::Time::FromTimeT(90)));
+  initial_data[0] =
+      TemplateURLService::CreateSyncDataFromTemplateURLData(turl->data());
+  MergeAndExpectNotify(initial_data, 1);
+
+  EXPECT_EQ(3U, model()->GetAllSyncData(syncer::SEARCH_ENGINES).size());
+  EXPECT_FALSE(model()->GetTemplateURLForGUID("whateverguid"));
+  EXPECT_EQ(model()->GetDefaultSearchProvider(),
+            model()->GetTemplateURLForGUID("guid1"));
+}
+
 class TemplateURLServiceSyncTestWithSeparateLocalAndAccountSearchEngines
     : public TemplateURLServiceSyncTest {
  public:
diff --git a/chrome/browser/sync_file_system/local/syncable_file_system_unittest.cc b/chrome/browser/sync_file_system/local/syncable_file_system_unittest.cc
index b43c217..997d9a6 100644
--- a/chrome/browser/sync_file_system/local/syncable_file_system_unittest.cc
+++ b/chrome/browser/sync_file_system/local/syncable_file_system_unittest.cc
@@ -28,7 +28,6 @@
 #include "third_party/blink/public/mojom/quota/quota_types.mojom.h"
 #include "third_party/leveldatabase/leveldb_chrome.h"
 
-using blink::mojom::StorageType;
 using storage::FileSystemContext;
 using storage::FileSystemOperationContext;
 using storage::FileSystemURL;
diff --git a/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/ActionConfirmationManager.java b/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/ActionConfirmationManager.java
index 0357f4d..af52d16a 100644
--- a/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/ActionConfirmationManager.java
+++ b/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/ActionConfirmationManager.java
@@ -80,7 +80,6 @@
             this.finishBlocking = finishBlocking;
         }
     }
-    ;
 
     /**
      * @param profile The profile to access shared services with.
diff --git a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelOrderControllerImpl.java b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelOrderControllerImpl.java
index ea051a4..aaf82baa 100644
--- a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelOrderControllerImpl.java
+++ b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelOrderControllerImpl.java
@@ -163,8 +163,13 @@
                         && type != TabLaunchType.FROM_BOOKMARK_BAR_BACKGROUND
                         && type != TabLaunchType.FROM_REPARENTING_BACKGROUND
                         && type != TabLaunchType.FROM_HISTORY_NAVIGATION_BACKGROUND)
-                || (!mTabModelSelector.isIncognitoBrandedModelSelected()
-                        && isNewTabIncognitoBranded);
+                || isDifferentModel(isNewTabIncognitoBranded);
+    }
+
+    private boolean isDifferentModel(boolean isNewTabIncognitoBranded) {
+        return mTabModelSelector.isIncognitoBrandedModelSelected()
+                ? !isNewTabIncognitoBranded
+                : isNewTabIncognitoBranded;
     }
 
     /**
diff --git a/chrome/browser/themes/theme_syncable_service_unittest.cc b/chrome/browser/themes/theme_syncable_service_unittest.cc
index 43f4f69a..935a85d 100644
--- a/chrome/browser/themes/theme_syncable_service_unittest.cc
+++ b/chrome/browser/themes/theme_syncable_service_unittest.cc
@@ -525,73 +525,6 @@
   EXPECT_FALSE(fake_theme_service_->is_dirty());
 }
 
-TEST_F(ThemeSyncableServiceTest, UpdateThemeSpecifics_CurrentTheme_Extension) {
-  // Set up theme service to use custom theme.
-  fake_theme_service_->SetTheme(theme_extension_.get());
-
-  std::optional<syncer::ModelError> error =
-      theme_sync_service_->MergeDataAndStartSyncing(
-          syncer::THEMES, syncer::SyncDataList(),
-          std::unique_ptr<syncer::SyncChangeProcessor>(
-              new syncer::SyncChangeProcessorWrapperForTest(
-                  fake_change_processor_.get())));
-  EXPECT_FALSE(error.has_value()) << error.value().message();
-  const syncer::SyncChangeList& changes = fake_change_processor_->changes();
-  ASSERT_EQ(1u, changes.size());
-  EXPECT_EQ(syncer::SyncChange::ACTION_ADD, changes[0].change_type());
-  EXPECT_EQ(syncer::THEMES, changes[0].sync_data().GetDataType());
-
-  const sync_pb::ThemeSpecifics& theme_specifics =
-      changes[0].sync_data().GetSpecifics().theme();
-  EXPECT_TRUE(theme_specifics.use_custom_theme());
-  EXPECT_EQ(theme_extension_->id(), theme_specifics.custom_theme_id());
-  EXPECT_EQ(theme_extension_->name(), theme_specifics.custom_theme_name());
-  EXPECT_EQ(
-      extensions::ManifestURL::GetUpdateURL(theme_extension_.get()).spec(),
-      theme_specifics.custom_theme_update_url());
-}
-
-TEST_F(ThemeSyncableServiceTest,
-       UpdateThemeSpecifics_CurrentTheme_Autogenerated) {
-  // Set up theme service to use autogenerated theme.
-  fake_theme_service_->BuildAutogeneratedThemeFromColor(
-      SkColorSetRGB(0, 0, 100));
-
-  std::optional<syncer::ModelError> error =
-      theme_sync_service_->MergeDataAndStartSyncing(
-          syncer::THEMES, syncer::SyncDataList(),
-          std::unique_ptr<syncer::SyncChangeProcessor>(
-              new syncer::SyncChangeProcessorWrapperForTest(
-                  fake_change_processor_.get())));
-  EXPECT_FALSE(error.has_value()) << error.value().message();
-  const syncer::SyncChangeList& changes = fake_change_processor_->changes();
-  ASSERT_EQ(1u, changes.size());
-  EXPECT_EQ(syncer::SyncChange::ACTION_ADD, changes[0].change_type());
-  EXPECT_EQ(syncer::THEMES, changes[0].sync_data().GetDataType());
-
-  const sync_pb::ThemeSpecifics& theme_specifics =
-      changes[0].sync_data().GetSpecifics().theme();
-  EXPECT_FALSE(theme_specifics.use_custom_theme());
-  EXPECT_EQ(fake_theme_service_->GetAutogeneratedThemeColor(),
-            theme_specifics.autogenerated_color_theme().color());
-}
-
-TEST_F(ThemeSyncableServiceTest, UpdateThemeSpecifics_CurrentTheme_Policy) {
-  // Set up theme service to use policy theme.
-  fake_theme_service_->BuildAutogeneratedPolicyTheme();
-
-  std::optional<syncer::ModelError> error =
-      theme_sync_service_->MergeDataAndStartSyncing(
-          syncer::THEMES, syncer::SyncDataList(),
-          std::unique_ptr<syncer::SyncChangeProcessor>(
-              new syncer::SyncChangeProcessorWrapperForTest(
-                  fake_change_processor_.get())));
-  EXPECT_FALSE(error.has_value()) << error.value().message();
-  // Applying policy theme doesn't trigger sync changes.
-  const syncer::SyncChangeList& changes = fake_change_processor_->changes();
-  ASSERT_EQ(0u, changes.size());
-}
-
 TEST_F(ThemeSyncableServiceTest, GetAllSyncDataForTesting_Extension) {
   // Set up theme service to use custom theme.
   fake_theme_service_->SetTheme(theme_extension_.get());
@@ -1034,6 +967,84 @@
   EXPECT_TRUE(registrar()->IsExtensionEnabled(theme_extension()->id()));
 }
 
+TEST_F(RealThemeSyncableServiceTest,
+       UpdateThemeSpecifics_CurrentTheme_Extension) {
+  std::optional<syncer::ModelError> error =
+      theme_sync_service()->MergeDataAndStartSyncing(
+          syncer::THEMES, syncer::SyncDataList(),
+          std::unique_ptr<syncer::SyncChangeProcessor>(
+              new syncer::SyncChangeProcessorWrapperForTest(
+                  fake_change_processor())));
+  EXPECT_FALSE(error.has_value()) << error.value().message();
+
+  fake_change_processor()->changes().clear();
+  // Set up theme service to use custom theme.
+  {
+    test::ThemeServiceChangedWaiter waiter(theme_service());
+    theme_service()->SetTheme(theme_extension());
+    waiter.WaitForThemeChanged();
+  }
+
+  const syncer::SyncChangeList& changes = fake_change_processor()->changes();
+  ASSERT_EQ(1u, changes.size());
+  EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, changes[0].change_type());
+  EXPECT_EQ(syncer::THEMES, changes[0].sync_data().GetDataType());
+
+  const sync_pb::ThemeSpecifics& theme_specifics =
+      changes[0].sync_data().GetSpecifics().theme();
+  EXPECT_TRUE(theme_specifics.use_custom_theme());
+  EXPECT_EQ(theme_extension()->id(), theme_specifics.custom_theme_id());
+  EXPECT_EQ(theme_extension()->name(), theme_specifics.custom_theme_name());
+  EXPECT_EQ(extensions::ManifestURL::GetUpdateURL(theme_extension()).spec(),
+            theme_specifics.custom_theme_update_url());
+}
+
+TEST_F(RealThemeSyncableServiceTest,
+       UpdateThemeSpecifics_CurrentTheme_Autogenerated) {
+  std::optional<syncer::ModelError> error =
+      theme_sync_service()->MergeDataAndStartSyncing(
+          syncer::THEMES, syncer::SyncDataList(),
+          std::unique_ptr<syncer::SyncChangeProcessor>(
+              new syncer::SyncChangeProcessorWrapperForTest(
+                  fake_change_processor())));
+  EXPECT_FALSE(error.has_value()) << error.value().message();
+
+  fake_change_processor()->changes().clear();
+  // Set up theme service to use autogenerated theme.
+  theme_service()->BuildAutogeneratedThemeFromColor(SkColorSetRGB(0, 0, 100));
+
+  const syncer::SyncChangeList& changes = fake_change_processor()->changes();
+  ASSERT_EQ(1u, changes.size());
+  EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, changes[0].change_type());
+  EXPECT_EQ(syncer::THEMES, changes[0].sync_data().GetDataType());
+
+  const sync_pb::ThemeSpecifics& theme_specifics =
+      changes[0].sync_data().GetSpecifics().theme();
+  EXPECT_FALSE(theme_specifics.use_custom_theme());
+  EXPECT_EQ(theme_service()->GetAutogeneratedThemeColor(),
+            theme_specifics.autogenerated_color_theme().color());
+}
+
+TEST_F(RealThemeSyncableServiceTest, UpdateThemeSpecifics_CurrentTheme_Policy) {
+  std::optional<syncer::ModelError> error =
+      theme_sync_service()->MergeDataAndStartSyncing(
+          syncer::THEMES, syncer::SyncDataList(),
+          std::unique_ptr<syncer::SyncChangeProcessor>(
+              new syncer::SyncChangeProcessorWrapperForTest(
+                  fake_change_processor())));
+  EXPECT_FALSE(error.has_value()) << error.value().message();
+
+  fake_change_processor()->changes().clear();
+  // Set up theme service to use policy theme.
+  profile_->GetTestingPrefService()->SetManagedPref(
+      prefs::kPolicyThemeColor, std::make_unique<base::Value>(100));
+
+  ASSERT_TRUE(theme_service()->UsingPolicyTheme());
+  // Applying policy theme doesn't trigger sync changes.
+  const syncer::SyncChangeList& changes = fake_change_processor()->changes();
+  ASSERT_EQ(0u, changes.size());
+}
+
 TEST_F(RealThemeSyncableServiceTest, ShouldDownloadUserColorTheme) {
   sync_pb::ThemeSpecifics theme_specifics;
   sync_pb::ThemeSpecifics::UserColorTheme* user_color_theme =
@@ -2191,14 +2202,11 @@
                 prefs::kNonSyncingNtpCustomBackgroundDictDoNotUse),
             new_value);
 
-  // The local theme should be committed to the server.
-  const syncer::SyncChangeList& changes = fake_change_processor()->changes();
-  ASSERT_EQ(changes.size(), 1u);
-  const sync_pb::ThemeSpecifics& change_specifics =
-      changes.back().sync_data().GetSpecifics().theme();
-  ASSERT_TRUE(change_specifics.has_browser_color_scheme());
-  ASSERT_TRUE(change_specifics.has_ntp_background());
-  EXPECT_EQ(change_specifics.ntp_background().url(), kTestUrl);
+  sync_pb::ThemeSpecifics current_specifics =
+      theme_sync_service()->GetThemeSpecificsFromCurrentThemeForTesting();
+  ASSERT_TRUE(current_specifics.has_browser_color_scheme());
+  ASSERT_TRUE(current_specifics.has_ntp_background());
+  EXPECT_EQ(current_specifics.ntp_background().url(), kTestUrl);
 }
 
 // Regression test for crbug.com/389026436.
@@ -2285,14 +2293,11 @@
                 prefs::kNonSyncingNtpCustomBackgroundDictDoNotUse),
             new_value);
 
-  // The local theme should be committed to the server.
-  const syncer::SyncChangeList& changes = fake_change_processor()->changes();
-  ASSERT_EQ(changes.size(), 1u);
-  const sync_pb::ThemeSpecifics& change_specifics =
-      changes.back().sync_data().GetSpecifics().theme();
-  ASSERT_TRUE(change_specifics.has_browser_color_scheme());
-  ASSERT_TRUE(change_specifics.has_ntp_background());
-  EXPECT_EQ(change_specifics.ntp_background().url(), kTestUrl);
+  sync_pb::ThemeSpecifics current_specifics =
+      theme_sync_service()->GetThemeSpecificsFromCurrentThemeForTesting();
+  ASSERT_TRUE(current_specifics.has_browser_color_scheme());
+  ASSERT_TRUE(current_specifics.has_ntp_background());
+  EXPECT_EQ(current_specifics.ntp_background().url(), kTestUrl);
 }
 
 TEST_F(RealThemeSyncableServiceTest,
@@ -2351,15 +2356,12 @@
   EXPECT_EQ(theme_service()->GetThemeID(), ThemeService::kUserColorThemeID);
   EXPECT_EQ(theme_service()->GetUserColor(), SK_ColorBLUE);
 
-  // The local theme should be committed to the server.
-  const syncer::SyncChangeList& changes = fake_change_processor()->changes();
-  ASSERT_EQ(changes.size(), 1u);
-  const sync_pb::ThemeSpecifics& change_specifics =
-      changes.back().sync_data().GetSpecifics().theme();
-  ASSERT_TRUE(change_specifics.has_browser_color_scheme());
-  ASSERT_TRUE(change_specifics.has_user_color_theme());
-  EXPECT_EQ(change_specifics.user_color_theme().color(), SK_ColorBLUE);
-  EXPECT_EQ(change_specifics.user_color_theme().browser_color_variant(),
+  sync_pb::ThemeSpecifics current_specifics =
+      theme_sync_service()->GetThemeSpecificsFromCurrentThemeForTesting();
+  ASSERT_TRUE(current_specifics.has_browser_color_scheme());
+  ASSERT_TRUE(current_specifics.has_user_color_theme());
+  EXPECT_EQ(current_specifics.user_color_theme().color(), SK_ColorBLUE);
+  EXPECT_EQ(current_specifics.user_color_theme().browser_color_variant(),
             sync_pb::ThemeSpecifics_UserColorTheme_BrowserColorVariant_NEUTRAL);
 }
 
@@ -2426,15 +2428,12 @@
   EXPECT_EQ(theme_service()->GetThemeID(), ThemeService::kUserColorThemeID);
   EXPECT_EQ(theme_service()->GetUserColor(), SK_ColorBLUE);
 
-  // The local theme should be committed to the server.
-  const syncer::SyncChangeList& changes = fake_change_processor()->changes();
-  ASSERT_EQ(changes.size(), 1u);
-  const sync_pb::ThemeSpecifics& change_specifics =
-      changes.back().sync_data().GetSpecifics().theme();
-  ASSERT_TRUE(change_specifics.has_browser_color_scheme());
-  ASSERT_TRUE(change_specifics.has_user_color_theme());
-  EXPECT_EQ(change_specifics.user_color_theme().color(), SK_ColorBLUE);
-  EXPECT_EQ(change_specifics.user_color_theme().browser_color_variant(),
+  sync_pb::ThemeSpecifics current_specifics =
+      theme_sync_service()->GetThemeSpecificsFromCurrentThemeForTesting();
+  ASSERT_TRUE(current_specifics.has_browser_color_scheme());
+  ASSERT_TRUE(current_specifics.has_user_color_theme());
+  EXPECT_EQ(current_specifics.user_color_theme().color(), SK_ColorBLUE);
+  EXPECT_EQ(current_specifics.user_color_theme().browser_color_variant(),
             sync_pb::ThemeSpecifics_UserColorTheme_BrowserColorVariant_NEUTRAL);
 }
 
@@ -2481,6 +2480,12 @@
   }
   ASSERT_TRUE(theme_service()->UsingExtensionTheme());
 
+  theme_sync_service()->MergeDataAndStartSyncing(
+      syncer::THEMES, MakeThemeDataList(sync_pb::ThemeSpecifics()),
+      std::unique_ptr<syncer::SyncChangeProcessor>(
+          new syncer::SyncChangeProcessorWrapperForTest(
+              fake_change_processor())));
+
   sync_pb::ThemeSpecifics expected_theme_specifics;
   expected_theme_specifics.set_use_custom_theme(true);
   expected_theme_specifics.set_browser_color_scheme(
@@ -2504,22 +2509,20 @@
   // Local extension theme is still there.
   ASSERT_TRUE(theme_service()->UsingExtensionTheme());
 
-  theme_sync_service()->MergeDataAndStartSyncing(
-      syncer::THEMES, MakeThemeDataList(sync_pb::ThemeSpecifics()),
-      std::unique_ptr<syncer::SyncChangeProcessor>(
-          new syncer::SyncChangeProcessorWrapperForTest(
-              fake_change_processor())));
-
   // ThemeSpecifics should be valid, i.e. should not contain ntp background when
   // there is an extension theme.
   EXPECT_THAT(
       theme_sync_service()->GetThemeSpecificsFromCurrentThemeForTesting(),
       base::test::EqualsProto(expected_theme_specifics));
-  // Nothing committed to the server.
-  ASSERT_EQ(fake_change_processor()->changes().size(), 1u);
-  EXPECT_THAT(
-      fake_change_processor()->changes()[0].sync_data().GetSpecifics().theme(),
-      base::test::EqualsProto(expected_theme_specifics));
+  // Only the extension theme is committed.
+  ASSERT_GE(fake_change_processor()->changes().size(), 1u);
+  EXPECT_THAT(fake_change_processor()
+                  ->changes()
+                  .back()
+                  .sync_data()
+                  .GetSpecifics()
+                  .theme(),
+              base::test::EqualsProto(expected_theme_specifics));
 }
 
 TEST_F(RealThemeSyncableServiceTest,
@@ -2550,41 +2553,6 @@
 }
 
 TEST_F(RealThemeSyncableServiceTest,
-       ShouldCommitExtensionThemeAndBrowserColorSchemeOnInitialSync) {
-  // Local extension theme with browser color scheme.
-  theme_service()->SetTheme(theme_extension());
-  EXPECT_TRUE(base::test::RunUntil(
-      [&]() { return theme_service()->UsingExtensionTheme(); }));
-  theme_service()->SetBrowserColorScheme(
-      ThemeService::BrowserColorScheme::kLight);
-
-  // Start syncing.
-  ASSERT_FALSE(theme_sync_service()
-                   ->MergeDataAndStartSyncing(
-                       syncer::THEMES, syncer::SyncDataList(),
-                       std::unique_ptr<syncer::SyncChangeProcessor>(
-                           new syncer::SyncChangeProcessorWrapperForTest(
-                               fake_change_processor())))
-                   .has_value());
-
-  EXPECT_TRUE(theme_service()->UsingExtensionTheme());
-  EXPECT_EQ(theme_service()->GetBrowserColorScheme(),
-            ThemeService::BrowserColorScheme::kLight);
-  EXPECT_THAT(
-      theme_sync_service()->GetThemeSpecificsFromCurrentThemeForTesting(),
-      testing::AllOf(
-          testing::Property(&sync_pb::ThemeSpecifics::use_custom_theme, true),
-          testing::Property(&sync_pb::ThemeSpecifics::browser_color_scheme,
-                            sync_pb::ThemeSpecifics::LIGHT)));
-  // Local extension theme is committed along with the browser color scheme.
-  ASSERT_EQ(fake_change_processor()->changes().size(), 1u);
-  EXPECT_THAT(
-      fake_change_processor()->changes()[0].sync_data().GetSpecifics().theme(),
-      base::test::EqualsProto(
-          theme_sync_service()->GetThemeSpecificsFromCurrentThemeForTesting()));
-}
-
-TEST_F(RealThemeSyncableServiceTest,
        ShouldCommitBrowserColorSchemeAlongsideNewExtensionTheme) {
   // Local browser color scheme.
   theme_service()->SetBrowserColorScheme(
@@ -2792,6 +2760,41 @@
   EXPECT_EQ(fake_change_processor()->changes().size(), 1u);
 }
 
+TEST_F(ThemeSyncableServiceTestWithoutAccountThemesSeparation,
+       ShouldCommitExtensionThemeAndBrowserColorSchemeOnInitialSync) {
+  // Local extension theme with browser color scheme.
+  theme_service()->SetTheme(theme_extension());
+  EXPECT_TRUE(base::test::RunUntil(
+      [&]() { return theme_service()->UsingExtensionTheme(); }));
+  theme_service()->SetBrowserColorScheme(
+      ThemeService::BrowserColorScheme::kLight);
+
+  // Start syncing.
+  ASSERT_FALSE(theme_sync_service()
+                   ->MergeDataAndStartSyncing(
+                       syncer::THEMES, syncer::SyncDataList(),
+                       std::unique_ptr<syncer::SyncChangeProcessor>(
+                           new syncer::SyncChangeProcessorWrapperForTest(
+                               fake_change_processor())))
+                   .has_value());
+
+  EXPECT_TRUE(theme_service()->UsingExtensionTheme());
+  EXPECT_EQ(theme_service()->GetBrowserColorScheme(),
+            ThemeService::BrowserColorScheme::kLight);
+  EXPECT_THAT(
+      theme_sync_service()->GetThemeSpecificsFromCurrentThemeForTesting(),
+      testing::AllOf(
+          testing::Property(&sync_pb::ThemeSpecifics::use_custom_theme, true),
+          testing::Property(&sync_pb::ThemeSpecifics::browser_color_scheme,
+                            sync_pb::ThemeSpecifics::LIGHT)));
+  // Local extension theme is committed along with the browser color scheme.
+  ASSERT_EQ(fake_change_processor()->changes().size(), 1u);
+  EXPECT_THAT(
+      fake_change_processor()->changes()[0].sync_data().GetSpecifics().theme(),
+      base::test::EqualsProto(
+          theme_sync_service()->GetThemeSpecificsFromCurrentThemeForTesting()));
+}
+
 class ThemeSyncableServiceTestWithAccountThemesSeparation
     : public RealThemeSyncableServiceTest {
  public:
diff --git a/chrome/browser/touch_to_fill/autofill/android/BUILD.gn b/chrome/browser/touch_to_fill/autofill/android/BUILD.gn
index 664a6961b..d49376ce 100644
--- a/chrome/browser/touch_to_fill/autofill/android/BUILD.gn
+++ b/chrome/browser/touch_to_fill/autofill/android/BUILD.gn
@@ -26,6 +26,7 @@
     ":public",
     "//chrome/browser/autofill",
     "//chrome/browser/profiles",
+    "//components/autofill/android:main_autofill_jni_headers",
     "//components/autofill/core/common:features",
     "//content/public/browser:browser",
     "//third_party/libaddressinput:util",
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 072e3b6..764dd1b 100644
--- a/chrome/browser/touch_to_fill/autofill/android/internal/BUILD.gn
+++ b/chrome/browser/touch_to_fill/autofill/android/internal/BUILD.gn
@@ -58,6 +58,7 @@
   sources = [
     "java/res/layout/touch_to_fill_credit_card_sheet_item.xml",
     "java/res/layout/touch_to_fill_iban_sheet_item.xml",
+    "java/res/layout/touch_to_fill_loyalty_card_sheet_item.xml",
     "java/res/layout/touch_to_fill_payment_method_footer_item.xml",
     "java/res/layout/touch_to_fill_payment_method_header_item.xml",
     "java/res/layout/touch_to_fill_terms_label_sheet_item.xml",
diff --git a/chrome/browser/touch_to_fill/autofill/android/internal/java/res/layout/touch_to_fill_loyalty_card_sheet_item.xml b/chrome/browser/touch_to_fill/autofill/android/internal/java/res/layout/touch_to_fill_loyalty_card_sheet_item.xml
new file mode 100644
index 0000000..cfa601e1
--- /dev/null
+++ b/chrome/browser/touch_to_fill/autofill/android/internal/java/res/layout/touch_to_fill_loyalty_card_sheet_item.xml
@@ -0,0 +1,49 @@
+<?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.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:descendantFocusability="blocksDescendants"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_marginBottom="2dp"
+    android:layout_marginHorizontal="8dp"
+    android:background="@color/baseline_neutral_90"
+    android:gravity="center_vertical"
+    android:orientation="horizontal"
+    android:padding="16dp">
+
+    <ImageView
+        android:id="@+id/loyalty_card_icon"
+        android:src="@drawable/ic_globe_24dp"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginEnd="16dp"
+        android:layout_gravity="center"
+        android:contentDescription="@string/autofill_loyalty_card_generic"/>
+    <LinearLayout
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:orientation="vertical">
+        <TextView
+            android:id="@+id/loyalty_card_number"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:maxLines="1"
+            android:textAppearance="@style/TextAppearance.TextLarge.Primary"
+            android:ellipsize="middle"/>
+        <TextView
+            android:id="@+id/merchant_name"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:maxLines="1"
+            android:visibility="gone"
+            android:textAppearance="@style/TextAppearance.TextMedium.Secondary"
+            android:ellipsize="end"/>
+    </LinearLayout>
+</LinearLayout>
diff --git a/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodControllerRobolectricTest.java b/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodControllerRobolectricTest.java
index d5780e83..019895b7 100644
--- a/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodControllerRobolectricTest.java
+++ b/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodControllerRobolectricTest.java
@@ -44,7 +44,10 @@
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.FOOTER;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.HEADER;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.IBAN;
+import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.LOYALTY_CARD;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.TERMS_LABEL;
+import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.LoyaltyCardProperties.LOYALTY_CARD_NUMBER;
+import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.LoyaltyCardProperties.MERCHANT_NAME;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.SHEET_ITEMS;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.TermsLabelProperties.CARD_BENEFITS_TERMS_AVAILABLE;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.VISIBLE;
@@ -79,6 +82,7 @@
 import org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodMediator.TouchToFillIbanOutcome;
 import org.chromium.components.autofill.AutofillFeatures;
 import org.chromium.components.autofill.AutofillSuggestion;
+import org.chromium.components.autofill.LoyaltyCard;
 import org.chromium.components.autofill.SuggestionType;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
@@ -89,6 +93,7 @@
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.url.GURL;
 
+import java.util.Collections;
 import java.util.List;
 import java.util.Optional;
 import java.util.concurrent.TimeoutException;
@@ -160,6 +165,25 @@
                     /* label= */ "FR76 **** **** **** **** ***0 189",
                     /* nickname= */ "",
                     /* value= */ "FR7630006000011234567890189");
+
+    private static final LoyaltyCard LOYALTY_CARD_1 =
+            new LoyaltyCard(
+                    /* loyaltyCardId= */ "cvs",
+                    /* merchantName= */ "CVS Pharmacy",
+                    /* programName= */ "Loyalty program",
+                    /* programLogo= */ new GURL("https://site.com/icon.png"),
+                    /* loyaltyCardNumber= */ "1234",
+                    /* merchantDomains= */ Collections.emptyList());
+
+    private static final LoyaltyCard LOYALTY_CARD_2 =
+            new LoyaltyCard(
+                    /* loyaltyCardId= */ "stb",
+                    /* merchantName= */ "Starbucks",
+                    /* programName= */ "Coffee pro",
+                    /* programLogo= */ new GURL("https://coffee.com/logo.png"),
+                    /* loyaltyCardNumber= */ "4321",
+                    /* merchantDomains= */ Collections.emptyList());
+
     private static final AutofillSuggestion VISA_SUGGESTION =
             createCreditCardSuggestion(
                     VISA.getCardNameForAutofillDisplay(),
@@ -811,6 +835,40 @@
         assertEquals(0, getModelsOfType(itemList, FILL_BUTTON).size());
     }
 
+    @Test
+    public void testShowOneLoyaltyCard() throws TimeoutException {
+        mCoordinator.showLoyaltyCards(List.of(LOYALTY_CARD_1));
+
+        ModelList itemList = mTouchToFillPaymentMethodModel.get(SHEET_ITEMS);
+        assertThat(getModelsOfType(itemList, LOYALTY_CARD).size(), is(1));
+
+        PropertyModel loyaltyCardModel = itemList.get(0).model;
+        assertThat(
+                loyaltyCardModel.get(LOYALTY_CARD_NUMBER),
+                is(LOYALTY_CARD_1.getLoyaltyCardNumber()));
+        assertThat(loyaltyCardModel.get(MERCHANT_NAME), is(LOYALTY_CARD_1.getMerchantName()));
+    }
+
+    @Test
+    public void testShowTwoLoyaltyCards() throws TimeoutException {
+        mCoordinator.showLoyaltyCards(List.of(LOYALTY_CARD_1, LOYALTY_CARD_2));
+
+        ModelList itemList = mTouchToFillPaymentMethodModel.get(SHEET_ITEMS);
+        assertThat(getModelsOfType(itemList, LOYALTY_CARD).size(), is(2));
+
+        PropertyModel loyaltyCardModel1 = itemList.get(0).model;
+        assertThat(
+                loyaltyCardModel1.get(LOYALTY_CARD_NUMBER),
+                is(LOYALTY_CARD_1.getLoyaltyCardNumber()));
+        assertThat(loyaltyCardModel1.get(MERCHANT_NAME), is(LOYALTY_CARD_1.getMerchantName()));
+
+        PropertyModel loyaltyCardModel2 = itemList.get(1).model;
+        assertThat(
+                loyaltyCardModel2.get(LOYALTY_CARD_NUMBER),
+                is(LOYALTY_CARD_2.getLoyaltyCardNumber()));
+        assertThat(loyaltyCardModel2.get(MERCHANT_NAME), is(LOYALTY_CARD_2.getMerchantName()));
+    }
+
     private static List<PropertyModel> getModelsOfType(ModelList items, int type) {
         return StreamSupport.stream(items.spliterator(), false)
                 .filter(item -> item.type == type)
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 8a9ad36..85dc649 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
@@ -11,6 +11,7 @@
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.FOOTER;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.HEADER;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.IBAN;
+import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.LOYALTY_CARD;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.TERMS_LABEL;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.SHEET_ITEMS;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.VISIBLE;
@@ -26,6 +27,7 @@
 import org.chromium.chrome.browser.touch_to_fill.common.BottomSheetFocusHelper;
 import org.chromium.components.autofill.AutofillSuggestion;
 import org.chromium.components.autofill.ImageSize;
+import org.chromium.components.autofill.LoyaltyCard;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
 import org.chromium.ui.modelutil.PropertyModel;
@@ -83,6 +85,11 @@
     }
 
     @Override
+    public void showLoyaltyCards(List<LoyaltyCard> loyaltyCards) {
+        mMediator.showLoyaltyCards(loyaltyCards);
+    }
+
+    @Override
     public void hideSheet() {
         mMediator.hideSheet();
     }
@@ -109,6 +116,10 @@
                 TouchToFillPaymentMethodViewBinder::createIbanItemView,
                 TouchToFillPaymentMethodViewBinder::bindIbanItemView);
         adapter.registerType(
+                LOYALTY_CARD,
+                TouchToFillPaymentMethodViewBinder::createLoyaltyCardItemView,
+                TouchToFillPaymentMethodViewBinder::bindLoyaltyCardItemView);
+        adapter.registerType(
                 HEADER,
                 TouchToFillPaymentMethodViewBinder::createHeaderItemView,
                 TouchToFillPaymentMethodViewBinder::bindHeaderView);
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 7b9c8da..44cbb46 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
@@ -27,7 +27,11 @@
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.FOOTER;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.HEADER;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.IBAN;
+import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.LOYALTY_CARD;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.TERMS_LABEL;
+import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.LoyaltyCardProperties.LOYALTY_CARD_NUMBER;
+import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.LoyaltyCardProperties.MERCHANT_NAME;
+import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.LoyaltyCardProperties.NON_TRANSFORMING_LOYALTY_CARD_KEYS;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.SHEET_ITEMS;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.TermsLabelProperties.CARD_BENEFITS_TERMS_AVAILABLE;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.VISIBLE;
@@ -49,6 +53,7 @@
 import org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.TermsLabelProperties;
 import org.chromium.components.autofill.AutofillSuggestion;
 import org.chromium.components.autofill.IbanRecordType;
+import org.chromium.components.autofill.LoyaltyCard;
 import org.chromium.components.autofill.SuggestionType;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController.StateChangeReason;
@@ -137,6 +142,7 @@
     private PropertyModel mModel;
     private List<CreditCard> mCards;
     private List<Iban> mIbans;
+    private List<LoyaltyCard> mLoyaltyCards;
     private BottomSheetFocusHelper mBottomSheetFocusHelper;
 
     private InputProtector mInputProtector = new InputProtector();
@@ -160,6 +166,7 @@
         assert cards != null;
         mCards = cards;
         mIbans = null;
+        mLoyaltyCards = null;
         assert mCards.size() == suggestions.size()
                 : "The number of cards and suggestions should be same.";
 
@@ -204,6 +211,7 @@
         assert ibans != null;
         mIbans = ibans;
         mCards = null;
+        mLoyaltyCards = null;
 
         ModelList sheetItems = mModel.get(SHEET_ITEMS);
         sheetItems.clear();
@@ -229,6 +237,28 @@
         RecordHistogram.recordCount100Histogram(TOUCH_TO_FILL_NUMBER_OF_IBANS_SHOWN, mIbans.size());
     }
 
+    public void showLoyaltyCards(List<LoyaltyCard> loyaltyCards) {
+        mInputProtector.markShowTime();
+
+        assert loyaltyCards != null;
+        mLoyaltyCards = loyaltyCards;
+        mCards = null;
+        mIbans = null;
+
+        ModelList sheetItems = mModel.get(SHEET_ITEMS);
+        sheetItems.clear();
+
+        for (LoyaltyCard loyaltyCard : mLoyaltyCards) {
+            final PropertyModel model = createLoyaltyCardModel(loyaltyCard);
+            sheetItems.add(new ListItem(LOYALTY_CARD, model));
+        }
+
+        mBottomSheetFocusHelper.registerForOneTimeUse();
+        mModel.set(VISIBLE, true);
+
+        // TODO: crbug.com/404437211 - Log the number of loyalty cards shown.
+    }
+
     void hideSheet() {
         onDismissed(BottomSheetController.StateChangeReason.NONE);
     }
@@ -248,12 +278,14 @@
                         TOUCH_TO_FILL_CREDIT_CARD_OUTCOME_HISTOGRAM,
                         TouchToFillCreditCardOutcome.DISMISS,
                         TouchToFillCreditCardOutcome.MAX_VALUE);
-            } else {
-                assert mIbans != null;
+            } else if (mIbans != null) {
                 RecordHistogram.recordEnumeratedHistogram(
                         TOUCH_TO_FILL_IBAN_OUTCOME_HISTOGRAM,
                         TouchToFillIbanOutcome.DISMISS,
                         TouchToFillIbanOutcome.MAX_VALUE);
+            } else {
+                assert mLoyaltyCards != null;
+                // TODO: crbug.com/404437211 - Log loyalty card metrics.
             }
         }
     }
@@ -344,6 +376,15 @@
         return ibanModelBuilder.build();
     }
 
+    private PropertyModel createLoyaltyCardModel(LoyaltyCard loyaltyCard) {
+        PropertyModel.Builder loyaltyCardModelBuilder =
+                new PropertyModel.Builder(NON_TRANSFORMING_LOYALTY_CARD_KEYS)
+                        .with(LOYALTY_CARD_NUMBER, loyaltyCard.getLoyaltyCardNumber())
+                        .with(MERCHANT_NAME, loyaltyCard.getMerchantName());
+
+        return loyaltyCardModelBuilder.build();
+    }
+
     private ListItem buildTermsLabel(boolean cardBenefitsTermsAvailable) {
         return new ListItem(
                 TERMS_LABEL,
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 82a71f9..cdc330c4 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
@@ -39,15 +39,18 @@
         // A section containing the IBAN data.
         int IBAN = 2;
 
+        // A section containing the loyalty card data.
+        int LOYALTY_CARD = 3;
+
         // A "Continue" button, which is shown when there is only one payment
         // method available.
-        int FILL_BUTTON = 3;
+        int FILL_BUTTON = 4;
 
         // A footer section containing additional actions.
-        int FOOTER = 4;
+        int FOOTER = 5;
 
         // A section with a terms label is present when card benefits are available.
-        int TERMS_LABEL = 5;
+        int TERMS_LABEL = 6;
     }
 
     /** Metadata associated with a card's image. */
@@ -122,6 +125,20 @@
         private IbanProperties() {}
     }
 
+    /** Properties for a loyalty card entry in the TouchToFill sheet for payments. */
+    static class LoyaltyCardProperties {
+        static final PropertyModel.ReadableObjectPropertyKey<String> LOYALTY_CARD_NUMBER =
+                new PropertyModel.ReadableObjectPropertyKey<>("loyalty_card_number");
+        static final PropertyModel.ReadableObjectPropertyKey<String> MERCHANT_NAME =
+                new PropertyModel.ReadableObjectPropertyKey<>("merchant_name");
+
+        static final PropertyKey[] NON_TRANSFORMING_LOYALTY_CARD_KEYS = {
+            LOYALTY_CARD_NUMBER, MERCHANT_NAME
+        };
+
+        private LoyaltyCardProperties() {}
+    }
+
     /**
      * 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 86d5261..db49eb1 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
@@ -48,6 +48,7 @@
                     return true;
                 case ItemType.CREDIT_CARD:
                 case ItemType.IBAN:
+                case ItemType.LOYALTY_CARD:
                     return false;
             }
             assert false : "Undefined whether to skip setting background for item of type: " + type;
@@ -86,21 +87,25 @@
 
     @Override
     public @NonNull String getSheetContentDescription(Context context) {
+        // TODO - crbug.com/: Update for loyalty cards.
         return context.getString(R.string.autofill_payment_method_bottom_sheet_content_description);
     }
 
     @Override
     public @StringRes int getSheetHalfHeightAccessibilityStringId() {
+        // TODO - crbug.com/: Update for loyalty cards.
         return R.string.autofill_payment_method_bottom_sheet_half_height;
     }
 
     @Override
     public @StringRes int getSheetFullHeightAccessibilityStringId() {
+        // TODO - crbug.com/: Update for loyalty cards.
         return R.string.autofill_payment_method_bottom_sheet_full_height;
     }
 
     @Override
     public @StringRes int getSheetClosedAccessibilityStringId() {
+        // TODO - crbug.com/: Update for loyalty cards.
         return R.string.autofill_payment_method_bottom_sheet_closed;
     }
 
@@ -121,7 +126,10 @@
 
     @Override
     protected Set<Integer> listedItemTypes() {
-        return Set.of(TouchToFillPaymentMethodProperties.ItemType.CREDIT_CARD, TouchToFillPaymentMethodProperties.ItemType.IBAN);
+        return Set.of(
+                TouchToFillPaymentMethodProperties.ItemType.CREDIT_CARD,
+                TouchToFillPaymentMethodProperties.ItemType.IBAN,
+                TouchToFillPaymentMethodProperties.ItemType.LOYALTY_CARD);
     }
 
     @Override
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 f98c680b..4d20ae0 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
@@ -21,6 +21,8 @@
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.IbanProperties.IBAN_NICKNAME;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.IbanProperties.IBAN_VALUE;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.IbanProperties.ON_IBAN_CLICK_ACTION;
+import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.LoyaltyCardProperties.LOYALTY_CARD_NUMBER;
+import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.LoyaltyCardProperties.MERCHANT_NAME;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.SHEET_ITEMS;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.TermsLabelProperties.CARD_BENEFITS_TERMS_AVAILABLE;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.VISIBLE;
@@ -136,6 +138,14 @@
         return ibanItem;
     }
 
+    static View createLoyaltyCardItemView(ViewGroup parent) {
+        View loyaltyCardItem =
+                LayoutInflater.from(parent.getContext())
+                        .inflate(R.layout.touch_to_fill_loyalty_card_sheet_item, parent, false);
+        AutofillUiUtils.setFilterTouchForSecurity(loyaltyCardItem);
+        return loyaltyCardItem;
+    }
+
     /** 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);
@@ -219,8 +229,24 @@
         }
     }
 
+    static void bindLoyaltyCardItemView(PropertyModel model, View view, PropertyKey propertyKey) {
+        if (propertyKey == LOYALTY_CARD_NUMBER) {
+            TextView loyaltyCardNumber = view.findViewById(R.id.loyalty_card_number);
+            loyaltyCardNumber.setText(model.get(LOYALTY_CARD_NUMBER));
+            loyaltyCardNumber.setTextAppearance(R.style.TextAppearance_TextLarge_Primary);
+        } else if (propertyKey == MERCHANT_NAME) {
+            TextView merchantName = view.findViewById(R.id.merchant_name);
+            merchantName.setText(model.get(MERCHANT_NAME));
+            merchantName.setVisibility(View.VISIBLE);
+        } else {
+            assert false : "Unhandled update to property:" + propertyKey;
+        }
+    }
+
     /**
-     * Factory used to create a new header inside the ListView inside the TouchToFillPaymentMethodView.
+     * Factory used to create a new header inside the ListView inside the {@link
+     * TouchToFillPaymentMethodView}.
+     *
      * @param parent The parent {@link ViewGroup} of the new item.
      */
     static View createHeaderItemView(ViewGroup parent) {
@@ -271,7 +297,9 @@
                 || propertyKey == IBAN_VALUE
                 || propertyKey == IBAN_NICKNAME
                 || propertyKey == ITEM_COLLECTION_INFO
-                || propertyKey == APPLY_DEACTIVATED_STYLE) {
+                || propertyKey == APPLY_DEACTIVATED_STYLE
+                || propertyKey == LOYALTY_CARD_NUMBER
+                || propertyKey == MERCHANT_NAME) {
             // Skip, because none of these changes affect the button
         } else {
             assert false : "Unhandled update to property:" + propertyKey;
diff --git a/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodViewBridge.java b/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodViewBridge.java
index 2badd93..e308b54 100644
--- a/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodViewBridge.java
+++ b/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodViewBridge.java
@@ -18,6 +18,7 @@
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.touch_to_fill.common.BottomSheetFocusHelper;
 import org.chromium.components.autofill.AutofillSuggestion;
+import org.chromium.components.autofill.LoyaltyCard;
 import org.chromium.components.autofill.SuggestionType;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetControllerProvider;
@@ -83,6 +84,11 @@
     }
 
     @CalledByNative
+    private void showLoyaltyCards(@JniType("std::vector") List<LoyaltyCard> loyaltyCards) {
+        mComponent.showLoyaltyCards(loyaltyCards);
+    }
+
+    @CalledByNative
     private void hideSheet() {
         mComponent.hideSheet();
     }
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 ead3bce2..37c95df 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
@@ -42,7 +42,11 @@
 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.FILL_BUTTON;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.IBAN;
+import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.LOYALTY_CARD;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.TERMS_LABEL;
+import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.LoyaltyCardProperties.LOYALTY_CARD_NUMBER;
+import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.LoyaltyCardProperties.MERCHANT_NAME;
+import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.LoyaltyCardProperties.NON_TRANSFORMING_LOYALTY_CARD_KEYS;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.SHEET_ITEMS;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.TermsLabelProperties.ALL_TERMS_LABEL_KEYS;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.TermsLabelProperties.CARD_BENEFITS_TERMS_AVAILABLE;
@@ -85,6 +89,7 @@
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
 import org.chromium.components.autofill.AutofillSuggestion;
+import org.chromium.components.autofill.LoyaltyCard;
 import org.chromium.components.autofill.SuggestionType;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController.SheetState;
@@ -96,6 +101,8 @@
 import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 import org.chromium.url.GURL;
 
+import java.util.Collections;
+
 /** Tests for {@link TouchToFillPaymentMethodView} */
 @RunWith(ChromeJUnit4ClassRunner.class)
 @DoNotBatch(reason = "The methods of ChromeAccessibilityUtil don't seem to work with batching.")
@@ -258,6 +265,14 @@
                     /* label= */ "CH56 **** **** **** *800 9",
                     /* nickname= */ "",
                     /* value= */ "CH5604835012345678009");
+    private static final LoyaltyCard CVS_LOYALTY_CARD =
+            new LoyaltyCard(
+                    /* loyaltyCardId= */ "cvs",
+                    /* merchantName= */ "CVS Pharmacy",
+                    /* programName= */ "Loyalty program",
+                    /* programLogo= */ new GURL("https://site.com/icon.png"),
+                    /* loyaltyCardNumber= */ "1234",
+                    /* merchantDomains= */ Collections.emptyList());
 
     @Rule
     public final MockitoRule mMockitoRule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
@@ -952,6 +967,33 @@
         assertThat(ibanSecondaryText.getVisibility(), is(View.GONE));
     }
 
+    @Test
+    @MediumTest
+    public void testLoyaltyCardTouchToFillItem() {
+        runOnUiThreadBlocking(
+                () -> {
+                    mTouchToFillPaymentMethodModel
+                            .get(SHEET_ITEMS)
+                            .add(
+                                    new ListItem(
+                                            LOYALTY_CARD,
+                                            createLoyaltyCardModel(CVS_LOYALTY_CARD)));
+                    mTouchToFillPaymentMethodModel.set(VISIBLE, true);
+                });
+        BottomSheetTestSupport.waitForOpen(mBottomSheetController);
+
+        TextView loyaltyCardNumber =
+                mTouchToFillPaymentMethodView
+                        .getContentView()
+                        .findViewById(R.id.loyalty_card_number);
+        assertThat(
+                loyaltyCardNumber.getText().toString(),
+                is(CVS_LOYALTY_CARD.getLoyaltyCardNumber()));
+        TextView merchantName =
+                mTouchToFillPaymentMethodView.getContentView().findViewById(R.id.merchant_name);
+        assertThat(merchantName.getText().toString(), is(CVS_LOYALTY_CARD.getMerchantName()));
+    }
+
     private RecyclerView getCreditCardSuggestions() {
         return mTouchToFillPaymentMethodView.getContentView().findViewById(R.id.sheet_item_list);
     }
@@ -1023,6 +1065,14 @@
         return ibanModelBuilder.build();
     }
 
+    private static PropertyModel createLoyaltyCardModel(LoyaltyCard loyaltyCard) {
+        PropertyModel.Builder loyaltyCardModelBuilder =
+                new PropertyModel.Builder(NON_TRANSFORMING_LOYALTY_CARD_KEYS)
+                        .with(LOYALTY_CARD_NUMBER, loyaltyCard.getLoyaltyCardNumber())
+                        .with(MERCHANT_NAME, loyaltyCard.getMerchantName());
+        return loyaltyCardModelBuilder.build();
+    }
+
     private static PropertyModel createTermsLabelModel(boolean cardBenefitsTermsAvailable) {
         return new PropertyModel.Builder(ALL_TERMS_LABEL_KEYS)
                 .with(CARD_BENEFITS_TERMS_AVAILABLE, cardBenefitsTermsAvailable)
diff --git a/chrome/browser/touch_to_fill/autofill/android/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodComponent.java b/chrome/browser/touch_to_fill/autofill/android/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodComponent.java
index 1daf8222..ff52fcc 100644
--- a/chrome/browser/touch_to_fill/autofill/android/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodComponent.java
+++ b/chrome/browser/touch_to_fill/autofill/android/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodComponent.java
@@ -11,6 +11,7 @@
 import org.chromium.chrome.browser.autofill.PersonalDataManager;
 import org.chromium.chrome.browser.touch_to_fill.common.BottomSheetFocusHelper;
 import org.chromium.components.autofill.AutofillSuggestion;
+import org.chromium.components.autofill.LoyaltyCard;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 
 import java.util.List;
@@ -91,6 +92,9 @@
     /** Displays a new IBAN bottom sheet. */
     void showIbans(List<PersonalDataManager.Iban> ibans);
 
+    /** Displays a new loyalty card bottom sheet. */
+    void showLoyaltyCards(List<LoyaltyCard> loyaltyCards);
+
     /** Hides the bottom sheet if shown. */
     void hideSheet();
 }
diff --git a/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_delegate_android_impl.cc b/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_delegate_android_impl.cc
index eaaa6c5..1c316ad 100644
--- a/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_delegate_android_impl.cc
+++ b/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_delegate_android_impl.cc
@@ -6,11 +6,14 @@
 
 #include <variant>
 
+#include "base/containers/to_vector.h"
 #include "base/feature_list.h"
 #include "base/metrics/histogram_functions.h"
 #include "components/autofill/core/browser/autofill_browser_util.h"
 #include "components/autofill/core/browser/data_manager/payments/payments_data_manager.h"
+#include "components/autofill/core/browser/data_manager/valuables/valuables_data_manager.h"
 #include "components/autofill/core/browser/data_model/payments/credit_card.h"
+#include "components/autofill/core/browser/data_model/valuables/loyalty_card.h"
 #include "components/autofill/core/browser/field_types.h"
 #include "components/autofill/core/browser/form_structure.h"
 #include "components/autofill/core/browser/form_types.h"
@@ -46,23 +49,25 @@
          SanitizedFieldIsEmpty(form_field->value());
 }
 
-bool IsTriggeredOnIbanField(const FormStructure* form_field,
-                            const FormFieldData& field) {
+bool IsTriggeredOnFieldWithGroup(const FormStructure* form_field,
+                                 const FormFieldData& field,
+                                 FieldTypeGroup field_type_group) {
   if (!form_field) {
     return false;
   }
 
   const autofill::AutofillField* autofill_field =
       form_field->GetFieldById(field.global_id());
-  return autofill_field &&
-         autofill_field->Type().group() == FieldTypeGroup::kIban;
+  return autofill_field && autofill_field->Type().group() == field_type_group;
 }
 
 }  // namespace
 
 TouchToFillDelegateAndroidImpl::DryRunResult::DryRunResult(
     TriggerOutcome outcome,
-    std::variant<std::vector<CreditCard>, std::vector<Iban>> items_to_suggest)
+    std::variant<std::vector<CreditCard>,
+                 std::vector<Iban>,
+                 std::vector<LoyaltyCard>> items_to_suggest)
     : outcome(outcome), items_to_suggest(std::move(items_to_suggest)) {}
 
 TouchToFillDelegateAndroidImpl::DryRunResult::DryRunResult(DryRunResult&&) =
@@ -124,6 +129,8 @@
     return DryRunForIban();
   } else if (field->Type().group() == FieldTypeGroup::kCreditCard) {
     return DryRunForCreditCard(*field, *form, received_form);
+  } else if (field->Type().group() == FieldTypeGroup::kLoyaltyCard) {
+    return DryRunForLoyaltyCard();
   }
 
   return {TriggerOutcome::kUnsupportedFieldType, {}};
@@ -171,6 +178,19 @@
                             std::move(cards_to_suggest));
 }
 
+TouchToFillDelegateAndroidImpl::DryRunResult
+TouchToFillDelegateAndroidImpl::DryRunForLoyaltyCard() {
+  ValuablesDataManager* vdm = manager_->client().GetValuablesDataManager();
+  if (!vdm) {
+    return DryRunResult(TriggerOutcome::kNoValidPaymentMethods, {});
+  }
+  base::span<const LoyaltyCard> loyalty_cards = vdm->GetLoyaltyCards();
+  return loyalty_cards.empty()
+             ? DryRunResult(TriggerOutcome::kNoValidPaymentMethods, {})
+             : DryRunResult(TriggerOutcome::kShown,
+                            base::ToVector(loyalty_cards));
+}
+
 // TODO(crbug.com/40282650): Remove received FormData
 bool TouchToFillDelegateAndroidImpl::IntendsToShowTouchToFill(
     FormGlobalId form_id,
@@ -214,14 +234,29 @@
                     ->ShowTouchToFillIban(GetWeakPtr(),
                                           std::move(*ibans_to_suggest))) {
       dry_run.outcome = TriggerOutcome::kFailedToDisplayBottomSheet;
+    } else if (std::vector<LoyaltyCard>* loyalty_cards_to_suggest =
+                   std::get_if<std::vector<LoyaltyCard>>(
+                       &dry_run.items_to_suggest);
+               loyalty_cards_to_suggest &&
+               !manager_->client()
+                    .GetPaymentsAutofillClient()
+                    ->ShowTouchToFillLoyaltyCard(
+                        GetWeakPtr(), std::move(*loyalty_cards_to_suggest))) {
+      dry_run.outcome = TriggerOutcome::kFailedToDisplayBottomSheet;
     }
   }
 
   if (dry_run.outcome != TriggerOutcome::kUnsupportedFieldType) {
-    if (IsTriggeredOnIbanField(manager_->FindCachedFormById(form.global_id()),
-                               field)) {
+    if (IsTriggeredOnFieldWithGroup(
+            manager_->FindCachedFormById(form.global_id()), field,
+            FieldTypeGroup::kIban)) {
       base::UmaHistogramEnumeration(kUmaTouchToFillIbanTriggerOutcome,
                                     dry_run.outcome);
+    } else if (IsTriggeredOnFieldWithGroup(
+                   manager_->FindCachedFormById(form.global_id()), field,
+                   FieldTypeGroup::kLoyaltyCard)) {
+      base::UmaHistogramEnumeration(kUmaTouchToFillLoyaltyCardTriggerOutcome,
+                                    dry_run.outcome);
     } else {
       base::UmaHistogramEnumeration(kUmaTouchToFillCreditCardTriggerOutcome,
                                     dry_run.outcome);
@@ -244,6 +279,11 @@
     manager_->DidShowSuggestions({Suggestion(SuggestionType::kCreditCardEntry)},
                                  form, field.global_id(),
                                  /*update_suggestions_callback=*/{});
+  } else if (std::get_if<std::vector<LoyaltyCard>>(&dry_run.items_to_suggest)) {
+    manager_->DidShowSuggestions(
+        {Suggestion(SuggestionType::kLoyaltyCardEntry)}, form,
+        field.global_id(),
+        /*update_suggestions_callback=*/{});
   } else {
     manager_->DidShowSuggestions({Suggestion(SuggestionType::kIbanEntry)}, form,
                                  field.global_id(),
diff --git a/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_delegate_android_impl.h b/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_delegate_android_impl.h
index 14218e25..a0a363d5 100644
--- a/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_delegate_android_impl.h
+++ b/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_delegate_android_impl.h
@@ -11,6 +11,7 @@
 #include "base/memory/weak_ptr.h"
 #include "components/autofill/core/browser/data_model/payments/credit_card.h"
 #include "components/autofill/core/browser/data_model/payments/iban.h"
+#include "components/autofill/core/browser/data_model/valuables/loyalty_card.h"
 #include "components/autofill/core/browser/form_structure.h"
 #include "components/autofill/core/browser/foundations/autofill_manager.h"
 #include "components/autofill/core/browser/integrators/fast_checkout/fast_checkout_client.h"
@@ -73,13 +74,13 @@
     "Autofill.TouchToFill.CreditCard.TriggerOutcome";
 inline constexpr const char kUmaTouchToFillIbanTriggerOutcome[] =
     "Autofill.TouchToFill.Iban.TriggerOutcome";
+inline constexpr const char kUmaTouchToFillLoyaltyCardTriggerOutcome[] =
+    "Autofill.TouchToFill.LoyaltyCard.TriggerOutcome";
 
 class BrowserAutofillManager;
 class FormStructure;
 
 // Delegate for in-browser Touch To Fill (TTF) surface display and selection.
-// Currently TTF surface is eligible for credit card and IBAN forms on click
-// on an empty focusable field.
 //
 // If the surface was shown once, it won't be triggered again on the same page.
 // But calling |Reset()| on navigation restores such showing eligibility.
@@ -150,14 +151,18 @@
 
   struct DryRunResult {
     DryRunResult(TriggerOutcome outcome,
-                 std::variant<std::vector<CreditCard>, std::vector<Iban>>
-                     items_to_suggest);
+                 std::variant<std::vector<CreditCard>,
+                              std::vector<Iban>,
+                              std::vector<LoyaltyCard>> items_to_suggest);
     DryRunResult(DryRunResult&&);
     DryRunResult& operator=(DryRunResult&&);
     ~DryRunResult();
 
     TriggerOutcome outcome;
-    std::variant<std::vector<CreditCard>, std::vector<Iban>> items_to_suggest;
+    std::variant<std::vector<CreditCard>,
+                 std::vector<Iban>,
+                 std::vector<LoyaltyCard>>
+        items_to_suggest;
   };
 
   // Checks all preconditions for showing the TTF, that is, for calling
@@ -182,6 +187,10 @@
                                    const FormStructure& form,
                                    const FormData& received_form);
 
+  // Returns a DryRunResult with the user's fillable loyalty cards, or
+  // an error reason if TTF should not be triggered.
+  DryRunResult DryRunForLoyaltyCard();
+
   bool HasAnyAutofilledFields(const FormStructure& submitted_form) const;
 
   // The form is considered perfectly filled if all non-empty fields are
diff --git a/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_delegate_android_impl_unittest.cc b/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_delegate_android_impl_unittest.cc
index e1fab5cb..73b3b31 100644
--- a/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_delegate_android_impl_unittest.cc
+++ b/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_delegate_android_impl_unittest.cc
@@ -4,12 +4,17 @@
 
 #include "chrome/browser/touch_to_fill/autofill/android/touch_to_fill_delegate_android_impl.h"
 
+#include "base/notreached.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "components/autofill/core/browser/data_manager/payments/payments_data_manager.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_api.h"
 #include "components/autofill/core/browser/data_model/payments/credit_card.h"
+#include "components/autofill/core/browser/data_model/valuables/loyalty_card.h"
+#include "components/autofill/core/browser/filling/filling_product.h"
 #include "components/autofill/core/browser/form_structure.h"
 #include "components/autofill/core/browser/foundations/test_autofill_client.h"
 #include "components/autofill/core/browser/foundations/test_autofill_driver.h"
@@ -19,9 +24,11 @@
 #include "components/autofill/core/browser/suggestions/suggestion.h"
 #include "components/autofill/core/browser/suggestions/suggestion_type.h"
 #include "components/autofill/core/browser/test_utils/autofill_test_utils.h"
+#include "components/autofill/core/browser/test_utils/valuables_data_test_utils.h"
 #include "components/autofill/core/browser/ui/autofill_external_delegate.h"
 #include "components/autofill/core/common/autofill_clock.h"
 #include "components/autofill/core/common/autofill_constants.h"
+#include "components/autofill/core/common/autofill_features.h"
 #include "components/autofill/core/common/autofill_test_utils.h"
 #include "components/autofill/core/common/form_data_test_api.h"
 #include "components/autofill/core/common/mojom/autofill_types.mojom-shared.h"
@@ -81,6 +88,11 @@
               (base::WeakPtr<autofill::TouchToFillDelegate> delegate,
                base::span<const Iban> ibans_to_suggest),
               (override));
+  MOCK_METHOD(bool,
+              ShowTouchToFillLoyaltyCard,
+              (base::WeakPtr<autofill::TouchToFillDelegate> delegate,
+               base::span<const LoyaltyCard> loyalty_cards_to_suggest),
+              (override));
   MOCK_METHOD(void, HideTouchToFillPaymentMethod, (), (override));
 
   void ExpectDelegateWeakPtrFromShowInvalidatedOnHideForCards() {
@@ -108,6 +120,19 @@
     });
   }
 
+  void ExpectDelegateWeakPtrFromShowInvalidatedOnHideForLoyaltyCards() {
+    EXPECT_CALL(*this, ShowTouchToFillLoyaltyCard)
+        .WillOnce(
+            [this](base::WeakPtr<autofill::TouchToFillDelegate> delegate,
+                   base::span<const LoyaltyCard> loyalty_cards_to_suggest) {
+              captured_delegate_ = delegate;
+              return true;
+            });
+    EXPECT_CALL(*this, HideTouchToFillPaymentMethod).WillOnce([this] {
+      EXPECT_FALSE(captured_delegate_);
+    });
+  }
+
  private:
   base::WeakPtr<autofill::TouchToFillDelegate> captured_delegate_;
 };
@@ -194,6 +219,8 @@
         .WillByDefault(Return(true));
     ON_CALL(payments_autofill_client(), ShowTouchToFillIban)
         .WillByDefault(Return(true));
+    ON_CALL(payments_autofill_client(), ShowTouchToFillLoyaltyCard)
+        .WillByDefault(Return(true));
     // Calling HideTouchToFillPaymentMethod in production code leads to that
     // OnDismissed gets triggered (HideTouchToFillPaymentMethod calls
     // view->Hide() on java side, which in its turn triggers onDismissed). Here
@@ -233,6 +260,16 @@
     return guid;
   }
 
+  void ConfigureForLoyaltyCards() {
+    LoyaltyCard loyalty_card = test::CreateLoyaltyCard();
+    // The touch-to-fill bottom sheet is shown only if the user has at least
+    // 1 saved loyalty card.
+    test_api(*autofill_client_.GetValuablesDataManager())
+        .AddLoyaltyCard(loyalty_card);
+    form_ = test::CreateTestLoyaltyCardFormData();
+    test_api(form_).field(0).set_is_focusable(true);
+  }
+
   void OnFormsSeen() {
     if (!browser_autofill_manager_->FindCachedFormById(form_.global_id())) {
       browser_autofill_manager_->OnFormsSeen({form_}, {});
@@ -269,6 +306,8 @@
   base::test::TaskEnvironment task_environment_{
       base::test::TaskEnvironment::TimeSource::MOCK_TIME};
   test::AutofillUnitTestEnvironment autofill_test_environment_;
+  base::test::ScopedFeatureList features_{
+      features::kAutofillEnableLoyaltyCardsFilling};
   NiceMock<MockAutofillClient> autofill_client_;
   std::unique_ptr<TestAutofillDriver> autofill_driver_;
   std::unique_ptr<MockBrowserAutofillManager> browser_autofill_manager_;
@@ -277,32 +316,55 @@
 };
 
 // Params of TouchToFillDelegateAndroidImplPaymentMethodUnitTest:
-// -- bool IsCreditCard: Indicates whether the payment method is a credit card
-//  or an IBAN.
+// -- FillingProduct: Indicates the Autofill data type to test. Supported data
+// types are:
+// * Credit card
+// * IBAN
+// * Loyalty card
 class TouchToFillDelegateAndroidImplPaymentMethodUnitTest
     : public TouchToFillDelegateAndroidImplUnitTest,
-      public testing::WithParamInterface<bool> {
+      public testing::WithParamInterface<FillingProduct> {
  protected:
   void SetUp() override {
     TouchToFillDelegateAndroidImplUnitTest::SetUp();
-    if (IsCreditCard()) {
-      ConfigureForCreditCards(test::GetCreditCard());
-    } else {
-      ConfigureForIbans();
+    switch (GetFillingProduct()) {
+      case FillingProduct::kCreditCard:
+        ConfigureForCreditCards(test::GetCreditCard());
+        break;
+      case FillingProduct::kIban:
+        ConfigureForIbans();
+        break;
+      case FillingProduct::kLoyaltyCard:
+        ConfigureForLoyaltyCards();
+        break;
+      default:
+        NOTREACHED() << "Unsupported filling product: "
+                     << FillingProductToString(GetFillingProduct());
     }
   }
 
-  bool IsCreditCard() const { return GetParam(); }
+  FillingProduct GetFillingProduct() const { return GetParam(); }
 
   std::string GetTriggerOutcomeHistogramName() {
-    return IsCreditCard() ? kUmaTouchToFillCreditCardTriggerOutcome
-                          : kUmaTouchToFillIbanTriggerOutcome;
+    switch (GetFillingProduct()) {
+      case FillingProduct::kCreditCard:
+        return kUmaTouchToFillCreditCardTriggerOutcome;
+      case FillingProduct::kIban:
+        return kUmaTouchToFillIbanTriggerOutcome;
+      case FillingProduct::kLoyaltyCard:
+        return kUmaTouchToFillLoyaltyCardTriggerOutcome;
+      default:
+        NOTREACHED() << "Unsupported filling product: "
+                     << FillingProductToString(GetFillingProduct());
+    }
   }
 };
 
 INSTANTIATE_TEST_SUITE_P(All,
                          TouchToFillDelegateAndroidImplPaymentMethodUnitTest,
-                         testing::Bool());
+                         testing::ValuesIn({FillingProduct::kCreditCard,
+                                            FillingProduct::kIban,
+                                            FillingProduct::kLoyaltyCard}));
 
 TEST_P(TouchToFillDelegateAndroidImplPaymentMethodUnitTest,
        TryToShowTouchToFillFailsForInvalidForm) {
@@ -396,7 +458,7 @@
 }
 
 TEST_P(TouchToFillDelegateAndroidImplPaymentMethodUnitTest,
-       TryToShowTouchToFillPaymentMethodSucceeds) {
+       TryToShowTouchToFillSucceeds) {
   ASSERT_FALSE(touch_to_fill_delegate_->IsShowingTouchToFill());
 
   EXPECT_CALL(*browser_autofill_manager_, DidShowSuggestions);
@@ -407,7 +469,7 @@
 }
 
 TEST_P(TouchToFillDelegateAndroidImplPaymentMethodUnitTest,
-       TryToShowTouchToFillFailsForPaymentMethodIfWasShown) {
+       TryToShowTouchToFillFailsIfWasShown) {
   TryToShowTouchToFill(/*expected_success=*/true);
   touch_to_fill_delegate_->HideTouchToFill();
 
@@ -418,7 +480,7 @@
 }
 
 TEST_P(TouchToFillDelegateAndroidImplPaymentMethodUnitTest,
-       TryToShowTouchToFillFailsForPaymentMethodIfFieldIsNotFocusable) {
+       TryToShowTouchToFillFailsIfFieldIsNotFocusable) {
   test_api(form_).field(0).set_is_focusable(false);
   ASSERT_FALSE(touch_to_fill_delegate_->IsShowingTouchToFill());
 
@@ -429,7 +491,7 @@
 }
 
 TEST_P(TouchToFillDelegateAndroidImplPaymentMethodUnitTest,
-       TryToShowTouchToFillFailsForPaymentMethodIfFieldHasValue) {
+       TryToShowTouchToFillFailsIfFieldHasValue) {
   ASSERT_FALSE(touch_to_fill_delegate_->IsShowingTouchToFill());
   test_api(form_).field(0).set_value(u"Initial value");
 
@@ -440,11 +502,12 @@
 }
 
 TEST_P(TouchToFillDelegateAndroidImplPaymentMethodUnitTest,
-       TryToShowTouchToFillFailsForPaymentMethodIfNoPaymentMethodsOnFile) {
+       TryToShowTouchToFillFailsIfNoDataOnFile) {
   ASSERT_FALSE(touch_to_fill_delegate_->IsShowingTouchToFill());
   autofill_client_.GetPersonalDataManager()
       .test_payments_data_manager()
       .ClearAllLocalData();
+  test_api(*autofill_client_.GetValuablesDataManager()).ClearLoyaltyCards();
 
   TryToShowTouchToFill(/*expected_success=*/false);
   histogram_tester_.ExpectUniqueSample(
@@ -1058,6 +1121,46 @@
       Iban::InstrumentId(instrument_id));
 }
 
+class TouchToFillDelegateAndroidImplLoyaltyCardUnitTest
+    : public TouchToFillDelegateAndroidImplUnitTest {
+ protected:
+  void SetUp() override {
+    TouchToFillDelegateAndroidImplUnitTest::SetUp();
+    ConfigureForLoyaltyCards();
+  }
+};
+
+TEST_F(TouchToFillDelegateAndroidImplLoyaltyCardUnitTest,
+       TryToShowTouchToFillFailsIfShowLoyaltyCardsFails) {
+  ASSERT_FALSE(touch_to_fill_delegate_->IsShowingTouchToFill());
+  EXPECT_CALL(payments_autofill_client(), ShowTouchToFillLoyaltyCard)
+      .WillOnce(Return(false));
+
+  TryToShowTouchToFill(/*expected_success=*/false);
+}
+
+TEST_F(TouchToFillDelegateAndroidImplLoyaltyCardUnitTest,
+       PassTheLoyaltyCardsToTheClient) {
+  // TODO: crbug.com/404437211 - Test that the loyalty cards are sorted.
+  std::vector<LoyaltyCard> loyalty_cards{test::CreateLoyaltyCard()};
+  test_api(*autofill_client_.GetValuablesDataManager())
+      .SetLoyaltyCards(loyalty_cards);
+
+  EXPECT_CALL(payments_autofill_client(),
+              ShowTouchToFillLoyaltyCard(_, ElementsAreArray(loyalty_cards)));
+
+  TryToShowTouchToFill(/*expected_success=*/true);
+}
+
+TEST_F(TouchToFillDelegateAndroidImplLoyaltyCardUnitTest,
+       SafelyHideTouchToFillInDtor) {
+  payments_autofill_client()
+      .ExpectDelegateWeakPtrFromShowInvalidatedOnHideForLoyaltyCards();
+  TryToShowTouchToFill(/*expected_success=*/true);
+
+  browser_autofill_manager_.reset();
+}
+
 class TouchToFillDelegateAndroidImplVcnGrayOutForMerchantOptOutUnitTest
     : public TouchToFillDelegateAndroidImplCreditCardUnitTest {
  public:
diff --git a/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_controller.cc b/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_controller.cc
index 71c04cc..666c695 100644
--- a/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_controller.cc
+++ b/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_controller.cc
@@ -13,6 +13,7 @@
 #include "chrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_view.h"
 #include "components/autofill/content/browser/content_autofill_client.h"
 #include "components/autofill/content/browser/content_autofill_driver.h"
+#include "components/autofill/core/browser/data_model/valuables/loyalty_card.h"
 #include "components/autofill/core/browser/foundations/autofill_manager.h"
 #include "components/autofill/core/browser/foundations/browser_autofill_manager.h"
 #include "components/autofill/core/browser/integrators/touch_to_fill/touch_to_fill_delegate.h"
@@ -134,6 +135,31 @@
   return true;
 }
 
+bool TouchToFillPaymentMethodController::ShowLoyaltyCards(
+    std::unique_ptr<TouchToFillPaymentMethodView> view,
+    base::WeakPtr<TouchToFillDelegate> delegate,
+    base::span<const LoyaltyCard> loyalty_cards_to_suggest) {
+  // TODO(crbug.com/404437211): Unify `ShowX()` methods to avoid code
+  // duplication.
+  if (!keyboard_suppressor_.is_suppressing()) {
+    return false;
+  }
+
+  // Abort if TTF surface is already shown.
+  if (view_) {
+    return false;
+  }
+
+  if (!view->ShowLoyaltyCards(this, loyalty_cards_to_suggest)) {
+    ResetJavaObject();
+    return false;
+  }
+
+  view_ = std::move(view);
+  delegate_ = std::move(delegate);
+  return true;
+}
+
 void TouchToFillPaymentMethodController::Hide() {
   if (view_)
     view_->Hide();
diff --git a/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_controller.h b/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_controller.h
index cf6e5ea..a827094 100644
--- a/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_controller.h
+++ b/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_controller.h
@@ -20,6 +20,7 @@
 class ContentAutofillClient;
 class CreditCard;
 class Iban;
+class LoyaltyCard;
 class TouchToFillDelegate;
 class TouchToFillPaymentMethodView;
 
@@ -70,6 +71,13 @@
                  base::WeakPtr<TouchToFillDelegate> delegate,
                  base::span<const Iban> ibans_to_suggest);
 
+  // Shows the Touch To Fill `view`. `delegate` will provide the fillable
+  // loyalty cards and be notified of the user's decision. Returns whether the
+  // surface was successfully shown.
+  bool ShowLoyaltyCards(std::unique_ptr<TouchToFillPaymentMethodView> view,
+                        base::WeakPtr<TouchToFillDelegate> delegate,
+                        base::span<const LoyaltyCard> loyalty_cards_to_suggest);
+
   // Hides the surface if it is currently shown.
   void Hide();
 
diff --git a/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_controller_unittest.cc b/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_controller_unittest.cc
index 3cf28235..46574e3 100644
--- a/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_controller_unittest.cc
+++ b/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_controller_unittest.cc
@@ -15,11 +15,14 @@
 #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/data_model/valuables/loyalty_card.h"
 #include "components/autofill/core/browser/foundations/test_autofill_client.h"
 #include "components/autofill/core/browser/foundations/test_browser_autofill_manager.h"
 #include "components/autofill/core/browser/integrators/touch_to_fill/touch_to_fill_delegate.h"
 #include "components/autofill/core/browser/suggestions/suggestion.h"
 #include "components/autofill/core/browser/test_utils/autofill_test_utils.h"
+#include "components/autofill/core/browser/test_utils/valuables_data_test_utils.h"
+#include "components/autofill/core/common/autofill_test_utils.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -48,6 +51,10 @@
               ShowIbans,
               (TouchToFillPaymentMethodViewController * controller,
                base::span<const Iban> ibans_to_suggest));
+  MOCK_METHOD(bool,
+              ShowLoyaltyCards,
+              (TouchToFillPaymentMethodViewController * controller,
+               base::span<const LoyaltyCard> loyalty_cards_to_suggest));
   MOCK_METHOD(void, Hide, ());
 };
 
@@ -121,6 +128,12 @@
     some_field_ = test::MakeFieldGlobalId();
   }
 
+  void SetUpLoyaltyCardFormField() {
+    some_form_data_ = test::CreateTestLoyaltyCardFormData();
+    some_form_ = some_form_data_.global_id();
+    some_field_ = test::MakeFieldGlobalId();
+  }
+
   void TearDown() override {
     mock_view_.reset();
     ChromeRenderViewHostTestHarness::TearDown();
@@ -148,6 +161,8 @@
                                                  test::GetCreditCard2()};
   const std::vector<Iban> ibans_ = {test::GetLocalIban(),
                                     test::GetServerIban()};
+  const std::vector<LoyaltyCard> loyalty_cards_ = {test::CreateLoyaltyCard(),
+                                                   test::CreateLoyaltyCard2()};
   const std::vector<Suggestion> suggestions_{
       test::CreateAutofillSuggestion(
           credit_cards_[0].CardNameForAutofillDisplay(),
@@ -225,6 +240,18 @@
   OnAfterAskForValuesToFill();
 }
 
+TEST_F(TouchToFillPaymentMethodControllerTest,
+       ShowLoyaltyCardsPassesLoyaltyCardsToTheView) {
+  SetUpLoyaltyCardFormField();
+  // Test that the loyalty cards have propagated to the view.
+  EXPECT_CALL(*mock_view_, ShowLoyaltyCards(&payment_method_controller(),
+                                            ElementsAreArray(loyalty_cards_)));
+  OnBeforeAskForValuesToFill();
+  payment_method_controller().ShowLoyaltyCards(
+      std::move(mock_view_), ttf_delegate().GetWeakPointer(), loyalty_cards_);
+  OnAfterAskForValuesToFill();
+}
+
 TEST_F(TouchToFillPaymentMethodControllerTest, ScanCreditCardIsCalled) {
   OnBeforeAskForValuesToFill();
   payment_method_controller().ShowCreditCards(std::move(mock_view_),
diff --git a/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_view.h b/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_view.h
index 47f21c6..2c22edc 100644
--- a/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_view.h
+++ b/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_view.h
@@ -11,6 +11,7 @@
 
 class CreditCard;
 class Iban;
+class LoyaltyCard;
 struct Suggestion;
 class TouchToFillPaymentMethodViewController;
 
@@ -27,6 +28,9 @@
       bool should_show_scan_credit_card) = 0;
   virtual bool ShowIbans(TouchToFillPaymentMethodViewController* controller,
                          base::span<const Iban> ibans_to_suggest) = 0;
+  virtual bool ShowLoyaltyCards(
+      TouchToFillPaymentMethodViewController* controller,
+      base::span<const LoyaltyCard> loyalty_cards_to_suggest) = 0;
   virtual void Hide() = 0;
 };
 
diff --git a/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_view_impl.cc b/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_view_impl.cc
index 2f92fd6..85eefbe 100644
--- a/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_view_impl.cc
+++ b/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_view_impl.cc
@@ -10,10 +10,12 @@
 #include "base/android/jni_string.h"
 #include "base/android/scoped_java_ref.h"
 #include "base/containers/to_vector.h"
+#include "base/notimplemented.h"
 #include "base/strings/string_util.h"
 #include "chrome/browser/autofill/android/personal_data_manager_android.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_view_controller.h"
+#include "components/autofill/core/browser/data_model/valuables/loyalty_card.h"
 #include "components/autofill/core/browser/suggestions/suggestion.h"
 #include "components/autofill/core/browser/ui/autofill_resource_utils.h"
 #include "components/autofill/core/common/autofill_features.h"
@@ -24,6 +26,7 @@
 
 // Must come after all headers that specialize FromJniType() / ToJniType().
 #include "chrome/browser/touch_to_fill/autofill/android/internal/jni/TouchToFillPaymentMethodViewBridge_jni.h"
+#include "components/autofill/android/main_autofill_jni_headers/LoyaltyCard_jni.h"
 
 namespace autofill {
 
@@ -142,6 +145,28 @@
   return true;
 }
 
+bool TouchToFillPaymentMethodViewImpl::ShowLoyaltyCards(
+    TouchToFillPaymentMethodViewController* controller,
+    base::span<const LoyaltyCard> loyalty_cards_to_suggest) {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  if (!IsReadyToShow(controller, env)) {
+    return false;
+  }
+
+  std::vector<base::android::ScopedJavaLocalRef<jobject>> loyalty_cards_array;
+  loyalty_cards_array.reserve(loyalty_cards_to_suggest.size());
+  for (const LoyaltyCard& loyalty_card : loyalty_cards_to_suggest) {
+    loyalty_cards_array.push_back(Java_LoyaltyCard_Constructor(
+        env, *loyalty_card.id(), loyalty_card.merchant_name(),
+        loyalty_card.program_name(), loyalty_card.program_logo(),
+        loyalty_card.loyalty_card_number(), loyalty_card.merchant_domains()));
+  }
+  Java_TouchToFillPaymentMethodViewBridge_showLoyaltyCards(
+      env, java_object_, std::move(loyalty_cards_array));
+
+  return true;
+}
+
 void TouchToFillPaymentMethodViewImpl::Hide() {
   if (java_object_) {
     Java_TouchToFillPaymentMethodViewBridge_hideSheet(
diff --git a/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_view_impl.h b/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_view_impl.h
index 03a4c10..9cca41ed 100644
--- a/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_view_impl.h
+++ b/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_view_impl.h
@@ -17,6 +17,7 @@
 
 class CreditCard;
 class Iban;
+class LoyaltyCard;
 struct Suggestion;
 class TouchToFillPaymentMethodViewController;
 
@@ -42,6 +43,9 @@
                        bool should_show_scan_credit_card) override;
   bool ShowIbans(TouchToFillPaymentMethodViewController* controller,
                  base::span<const autofill::Iban> ibans_to_suggest) override;
+  bool ShowLoyaltyCards(
+      TouchToFillPaymentMethodViewController* controller,
+      base::span<const LoyaltyCard> loyalty_cards_to_suggest) override;
   void Hide() override;
 
   // The corresponding Java TouchToFillPaymentMethodViewBridge.
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 475f6c9..1c0780e 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -2599,7 +2599,6 @@
       "//chromeos/ash/experiences/arc/video_accelerator:protected_native_pixmap_query_client",
       "//chromeos/ash/experiences/system_web_apps/types",
       "//chromeos/ash/resources",
-      "//chromeos/ash/services/assistant:lib",
       "//chromeos/ash/services/assistant/public/cpp",
       "//chromeos/ash/services/assistant/public/mojom",
       "//chromeos/ash/services/assistant/public/proto",
@@ -3959,8 +3958,6 @@
       "views/data_sharing/collaboration_controller_delegate_desktop.h",
       "views/data_sharing/data_sharing_bubble_controller.cc",
       "views/data_sharing/data_sharing_bubble_controller.h",
-      "views/data_sharing/data_sharing_open_group_helper.cc",
-      "views/data_sharing/data_sharing_open_group_helper.h",
       "views/data_sharing/data_sharing_utils.cc",
       "views/data_sharing/data_sharing_utils.h",
       "views/desktop_capture/desktop_media_content_pane_view.cc",
@@ -5734,6 +5731,7 @@
     ]
     deps += [
       "//chrome/browser/actor",
+      "//chrome/browser/ai:ai",
       "//chrome/browser/background/glic",
       "//chrome/browser/glic",
       "//chrome/browser/glic:impl",
diff --git a/chrome/browser/ui/android/omnibox/BUILD.gn b/chrome/browser/ui/android/omnibox/BUILD.gn
index 4eae539..8b8171d 100644
--- a/chrome/browser/ui/android/omnibox/BUILD.gn
+++ b/chrome/browser/ui/android/omnibox/BUILD.gn
@@ -213,6 +213,7 @@
     "//components/page_info/android:java",
     "//components/permissions/android:java",
     "//components/prefs/android:java",
+    "//components/saved_tab_groups/public:java",
     "//components/search_engines/android:java",
     "//components/security_state/core:security_state_enums_java",
     "//components/signin/public/android:java",
diff --git a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/FullscreenSigninAndHistorySyncCoordinator.java b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/FullscreenSigninAndHistorySyncCoordinator.java
index 1c08379e..c2c6568 100644
--- a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/FullscreenSigninAndHistorySyncCoordinator.java
+++ b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/FullscreenSigninAndHistorySyncCoordinator.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.ui.signin;
 
+import static org.chromium.build.NullUtil.assertNonNull;
 import static org.chromium.build.NullUtil.assumeNonNull;
 
 import android.accounts.Account;
@@ -15,6 +16,7 @@
 import android.widget.FrameLayout;
 
 import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
 
 import org.chromium.base.Promise;
 import org.chromium.base.metrics.RecordHistogram;
@@ -182,8 +184,8 @@
 
     /** Implements {@link SigninAndHistorySyncCoordinator}. */
     @Override
-    public void onAccountAdded(String accountName) {
-        assumeNonNull(mSigninCoordinator);
+    public void onAccountAdded(@NonNull String accountName) {
+        assertNonNull(mSigninCoordinator);
         mSigninCoordinator.onAccountAdded(accountName);
     }
 
diff --git a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/fullscreen_signin/FullscreenSigninCoordinator.java b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/fullscreen_signin/FullscreenSigninCoordinator.java
index af00273..fdb5645 100644
--- a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/fullscreen_signin/FullscreenSigninCoordinator.java
+++ b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/fullscreen_signin/FullscreenSigninCoordinator.java
@@ -8,6 +8,7 @@
 import android.content.Context;
 
 import androidx.annotation.MainThread;
+import androidx.annotation.NonNull;
 import androidx.annotation.StringRes;
 
 import org.chromium.base.Promise;
@@ -168,7 +169,7 @@
         }
     }
 
-    public void onAccountAdded(String accountName) {
+    public void onAccountAdded(@NonNull String accountName) {
         mMediator.onAccountAdded(accountName);
     }
 
diff --git a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/fullscreen_signin/FullscreenSigninMediator.java b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/fullscreen_signin/FullscreenSigninMediator.java
index e6a1ce5..695de7d 100644
--- a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/fullscreen_signin/FullscreenSigninMediator.java
+++ b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/fullscreen_signin/FullscreenSigninMediator.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.ui.signin.fullscreen_signin;
 
+import static org.chromium.build.NullUtil.assertNonNull;
 import static org.chromium.build.NullUtil.assumeNonNull;
 
 import android.accounts.Account;
@@ -12,6 +13,7 @@
 import android.text.TextUtils;
 
 import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
 
 import org.chromium.base.BuildInfo;
@@ -57,6 +59,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 
 @NullMarked
 @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
@@ -103,12 +106,12 @@
     private boolean mInitialLoadCompleted;
 
     private @Nullable AccountPickerDialogCoordinator mDialogCoordinator;
-    // TODO(crbug.com/40921927): Replace with CoreAccountInfo.
-    private @Nullable String mAddedAccountEmail;
-    // TODO(crbug.com/40921927): Replace with CoreAccountInfo.
-    private @Nullable String mSelectedAccountEmail;
-    // TODO(crbug.com/40921927): Replace with CoreAccountInfo.
-    private @Nullable String mDefaultAccountEmail;
+    private @Nullable CoreAccountInfo mSelectedAccount;
+    private @Nullable CoreAccountInfo mDefaultAccount;
+    private @Nullable CoreAccountInfo mAddedAccount;
+    // This field is used to save the added account email while the account info becomes available
+    // in AccountManagerFacade for sign-in.
+    private @Nullable String mPendingAddedAccountEmail;
     private boolean mAllowMetricsAndCrashUploading;
 
     FullscreenSigninMediator(
@@ -174,7 +177,7 @@
     }
 
     private Account getSelectedAccount() {
-        return AccountUtils.createAccountFromName(assumeNonNull(mSelectedAccountEmail));
+        return AccountUtils.createAccountFromName(assertNonNull(mSelectedAccount).getEmail());
     }
 
     private void onNativeLoaded() {
@@ -278,9 +281,16 @@
                 getFooterString(isMetricsReportingDisabledByPolicy));
     }
 
-    void onAccountAdded(String accountEmail) {
-        mAddedAccountEmail = accountEmail;
-        setSelectedAccountEmail(accountEmail);
+    void onAccountAdded(@NonNull String accountEmail) {
+        var accounts =
+                AccountUtils.getAccountsIfFulfilledOrEmpty(mAccountManagerFacade.getAccounts());
+        mAddedAccount = AccountUtils.findAccountByEmail(accounts, accountEmail);
+        if (mAddedAccount == null) {
+            mPendingAddedAccountEmail = accountEmail;
+            return;
+        }
+
+        setSelectedAccount(mAddedAccount);
         if (mDialogCoordinator != null) mDialogCoordinator.dismissDialog();
     }
 
@@ -298,8 +308,14 @@
     }
 
     @Override
-    public void onAccountSelected(CoreAccountInfo coreAccountInfo) {
-        setSelectedAccountEmail(coreAccountInfo.getEmail());
+    public void onAccountSelected(CoreAccountInfo account) {
+        if (mPendingAddedAccountEmail != null) {
+            // If another account is selected before the added account is available in account
+            // manager facade then clear the pending added account email so that it doesn't get
+            // selected automatically in #updateAccounts().
+            mPendingAddedAccountEmail = null;
+        }
+        setSelectedAccount(account);
         if (mDialogCoordinator != null) mDialogCoordinator.dismissDialog();
     }
 
@@ -340,7 +356,7 @@
             mDelegate.advanceToNextPage();
             return;
         }
-        if (mSelectedAccountEmail == null) {
+        if (mSelectedAccount == null) {
             mDelegate.addAccount();
             return;
         }
@@ -368,7 +384,7 @@
                                                         .get()
                                                         .getOriginalProfile()))
                         .getPrimaryAccountInfo(ConsentLevel.SIGNIN);
-        if (signedInAccount != null && signedInAccount.getEmail().equals(mSelectedAccountEmail)) {
+        if (signedInAccount != null && Objects.equals(signedInAccount, mSelectedAccount)) {
             mDelegate.advanceToNextPage();
             return;
         }
@@ -400,11 +416,8 @@
                         mModel.set(FullscreenSigninProperties.SHOW_SIGNIN_PROGRESS_SPINNER, false);
                     }
                 };
-        CoreAccountInfo selectedAccount =
-                AccountUtils.findCoreAccountInfoByEmail(
-                        mAccountManagerFacade.getCoreAccountInfos().getResult(),
-                        assumeNonNull(mSelectedAccountEmail));
-        if (selectedAccount != null) {
+
+        if (mSelectedAccount != null) {
             mModel.set(FullscreenSigninProperties.SHOW_SIGNIN_PROGRESS_SPINNER_WITH_TEXT, true);
             final @SigninAccessPoint int accessPoint =
                     mModel.get(FullscreenSigninProperties.IS_SELECTED_ACCOUNT_SUPERVISED)
@@ -414,10 +427,10 @@
                 // If there already exists another signed-in account, first sign-out and then
                 // sign-in with the selected account.
                 signOutThenSignInWithSelectedAccount(
-                        selectedAccount, signinManager, accessPoint, signInCallback);
+                        mSelectedAccount, signinManager, accessPoint, signInCallback);
             } else {
                 FreManagementNoticeDialogHelper.checkAccountManagementAndSignIn(
-                        selectedAccount,
+                        mSelectedAccount,
                         signinManager,
                         accessPoint,
                         signInCallback,
@@ -446,11 +459,10 @@
     }
 
     private @AccountConsistencyPromoAction int getSigninPromoAction() {
-        assert mSelectedAccountEmail != null;
-        if (TextUtils.equals(mSelectedAccountEmail, mDefaultAccountEmail)) {
+        assert mSelectedAccount != null;
+        if (Objects.equals(mSelectedAccount, mDefaultAccount)) {
             return AccountConsistencyPromoAction.SIGNED_IN_WITH_DEFAULT_ACCOUNT;
-        } else if (mAddedAccountEmail != null
-                && TextUtils.equals(mSelectedAccountEmail, mAddedAccountEmail)) {
+        } else if (Objects.equals(mSelectedAccount, mAddedAccount)) {
             return AccountConsistencyPromoAction.SIGNED_IN_WITH_ADDED_ACCOUNT;
         }
         return AccountConsistencyPromoAction.SIGNED_IN_WITH_NON_DEFAULT_ACCOUNT;
@@ -507,13 +519,14 @@
                 || mModel.get(FullscreenSigninProperties.SHOW_SIGNIN_PROGRESS_SPINNER);
     }
 
-    private void setSelectedAccountEmail(String accountEmail) {
-        mSelectedAccountEmail = accountEmail;
-        updateSelectedAccountData(mSelectedAccountEmail);
+    private void setSelectedAccount(CoreAccountInfo account) {
+        mSelectedAccount = account;
+        updateSelectedAccountData(account.getEmail());
     }
 
     private void updateSelectedAccountData(String accountEmail) {
-        if (TextUtils.equals(mSelectedAccountEmail, accountEmail)) {
+        if (mSelectedAccount != null
+                && TextUtils.equals(mSelectedAccount.getEmail(), accountEmail)) {
             mModel.set(
                     FullscreenSigninProperties.SELECTED_ACCOUNT_DATA,
                     mProfileDataCache.getProfileDataOrDefault(accountEmail));
@@ -521,18 +534,33 @@
     }
 
     private void updateAccounts(List<AccountInfo> accounts) {
+        @Nullable AccountInfo pendingAddedAccount =
+                mPendingAddedAccountEmail == null
+                        ? null
+                        : AccountUtils.findAccountByEmail(accounts, mPendingAddedAccountEmail);
+        if (pendingAddedAccount != null) {
+            mPendingAddedAccountEmail = null;
+            mAddedAccount = pendingAddedAccount;
+            onAccountSelected(mAddedAccount);
+            return;
+        }
+
         if (accounts.isEmpty()) {
-            mDefaultAccountEmail = null;
-            mSelectedAccountEmail = null;
+            mDefaultAccount = null;
+            mSelectedAccount = null;
             mModel.set(FullscreenSigninProperties.SELECTED_ACCOUNT_DATA, null);
             if (mDialogCoordinator != null) {
                 mDialogCoordinator.dismissDialog();
             }
         } else {
-            mDefaultAccountEmail = accounts.get(0).getEmail();
-            if (mSelectedAccountEmail == null
-                    || AccountUtils.findAccountByEmail(accounts, mSelectedAccountEmail) == null) {
-                setSelectedAccountEmail(mDefaultAccountEmail);
+            mDefaultAccount = accounts.get(0);
+            mSelectedAccount =
+                    mSelectedAccount == null
+                            ? null
+                            : AccountUtils.findAccountByEmail(
+                                    accounts, mSelectedAccount.getEmail());
+            if (mSelectedAccount == null) {
+                setSelectedAccount(mDefaultAccount);
             }
         }
 
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
index b7a3d73d..619df4ef 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -1909,6 +1909,9 @@
       <message name="IDS_SAFETY_HUB_LOCAL_PASSWORD_CHECK_UNAVAILABLE_TITLE" desc="Title for the local password check row in the Safety Check page indicating that the local password checkup could not be completed.">
         Can’t check passwords on this device
       </message>
+      <message name="IDS_SAFETY_HUB_PASSWORD_CHECK_UNAVAILABLE_TITLE" desc="Title for the password check row in the Safety Check page indicating that the local and/or account password checkup could not be completed.">
+        Can’t check passwords
+      </message>
       <message name="IDS_SAFETY_HUB_UNAVAILABLE_SUMMARY" desc="Summary for the rows in the Safety Check page indicating that the checkup for the specific module could not be run at this time and to check again later.">
         Try again later
       </message>
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_SAFETY_HUB_PASSWORD_CHECK_UNAVAILABLE_TITLE.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_SAFETY_HUB_PASSWORD_CHECK_UNAVAILABLE_TITLE.png.sha1
new file mode 100644
index 0000000..08b27a8
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_SAFETY_HUB_PASSWORD_CHECK_UNAVAILABLE_TITLE.png.sha1
@@ -0,0 +1 @@
+47a77890973c8e8523d8b4866f61b75a13983463
\ No newline at end of file
diff --git a/chrome/browser/ui/android/tab_model/tab_model_jni_bridge.cc b/chrome/browser/ui/android/tab_model/tab_model_jni_bridge.cc
index a73adde..52672230 100644
--- a/chrome/browser/ui/android/tab_model/tab_model_jni_bridge.cc
+++ b/chrome/browser/ui/android/tab_model/tab_model_jni_bridge.cc
@@ -127,7 +127,7 @@
 
 WebContents* TabModelJniBridge::GetWebContentsAt(int index) const {
   TabAndroid* tab = GetTabAt(index);
-  return tab == NULL ? NULL : tab->web_contents();
+  return tab == nullptr ? nullptr : tab->web_contents();
 }
 
 TabAndroid* TabModelJniBridge::GetTabAt(int index) const {
@@ -135,7 +135,7 @@
   ScopedJavaLocalRef<jobject> jtab =
       Java_TabModelJniBridge_getTabAt(env, java_object_.get(env), index);
 
-  return jtab.is_null() ? NULL : TabAndroid::GetNativeTab(env, jtab);
+  return jtab.is_null() ? nullptr : TabAndroid::GetNativeTab(env, jtab);
 }
 
 ScopedJavaLocalRef<jobject> TabModelJniBridge::GetJavaObject() const {
@@ -169,12 +169,12 @@
           url::GURLAndroid::FromNativeGURL(env, url), new_window);
   if (obj.is_null()) {
     VLOG(0) << "Failed to create java tab";
-    return NULL;
+    return nullptr;
   }
   TabAndroid* tab = TabAndroid::GetNativeTab(env, obj);
   if (!tab) {
     VLOG(0) << "Failed to create java tab";
-    return NULL;
+    return nullptr;
   }
   return tab->web_contents();
 }
@@ -253,9 +253,7 @@
 }
 
 tabs::TabInterface* TabModelJniBridge::GetTab(int index) {
-  // TODO(crbug.com/415351293): Implement.
-  NOTIMPLEMENTED();
-  return nullptr;
+  return GetTabAt(index);
 }
 
 void TabModelJniBridge::HighlightTabs(std::set<int> indicies) {
@@ -269,8 +267,7 @@
 }
 
 void TabModelJniBridge::CloseTab(int index) {
-  // TODO(crbug.com/415351293): Implement.
-  NOTIMPLEMENTED();
+  CloseTabAt(index);
 }
 
 std::vector<tabs::TabInterface*> TabModelJniBridge::GetAllTabs() {
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/back_button/BackButtonCoordinator.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/back_button/BackButtonCoordinator.java
index 2a03ab2..5dee070 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/back_button/BackButtonCoordinator.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/back_button/BackButtonCoordinator.java
@@ -9,7 +9,6 @@
 import android.graphics.Rect;
 import android.view.View;
 
-import org.chromium.base.Callback;
 import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.base.supplier.Supplier;
 import org.chromium.build.annotations.NullMarked;
@@ -19,6 +18,7 @@
 import org.chromium.chrome.browser.toolbar.top.NavigationPopup;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
+import org.chromium.ui.util.ClickWithMetaStateCallback;
 import org.chromium.ui.widget.ChromeImageButton;
 
 /**
@@ -36,9 +36,9 @@
      * Creates an instance of {@link BackButtonCoordinator}.
      *
      * @param view an Android {@link ChromeImageButton}.
-     * @param onBackPressed a {@link Callback<Integer>} (taking a parameter of meta key state) that
-     *     is invoked on back button click event. Allows parent components to intercept click and
-     *     navigate back in the history or hide custom UI components.
+     * @param onBackPressed a {@link ClickWithMetaStateCallback} (taking a parameter of meta key
+     *     state) that is invoked on back button click event. Allows parent components to intercept
+     *     click and navigate back in the history or hide custom UI components.
      * @param themeColorProvider a provider that notifies about theme changes.
      * @param tabSupplier a supplier that provides current active tab.
      * @param historyDelegate a delegate that allows parent components to decide how to display
@@ -46,7 +46,7 @@
      */
     public BackButtonCoordinator(
             ChromeImageButton view,
-            Callback<Integer> onBackPressed,
+            ClickWithMetaStateCallback onBackPressed,
             ThemeColorProvider themeColorProvider,
             ObservableSupplier<@Nullable Tab> tabSupplier,
             ObservableSupplier<Boolean> enabledSupplier,
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/back_button/BackButtonMediator.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/back_button/BackButtonMediator.java
index 8e96ca2..95f0340 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/back_button/BackButtonMediator.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/back_button/BackButtonMediator.java
@@ -23,6 +23,7 @@
 import org.chromium.chrome.browser.ui.theme.BrandedColorScheme;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyModelAnimatorFactory;
+import org.chromium.ui.util.ClickWithMetaStateCallback;
 
 /**
  * A class responsible for mediating external events like theme changes or visibility changes from
@@ -51,7 +52,7 @@
      */
     public BackButtonMediator(
             PropertyModel model,
-            Callback<Integer> onBackPressed,
+            ClickWithMetaStateCallback onBackPressed,
             ThemeColorProvider themeColorProvider,
             ObservableSupplier<@Nullable Tab> tabSupplier,
             ObservableSupplier<Boolean> enabledSupplier,
@@ -62,7 +63,7 @@
         mModel.set(
                 BackButtonProperties.CLICK_LISTENER,
                 (metaState) -> {
-                    onBackPressed.onResult(metaState);
+                    onBackPressed.onClickWithMeta(metaState);
                     updateButtonEnabledState();
                 });
         mModel.set(
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/back_button/BackButtonMediatorTest.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/back_button/BackButtonMediatorTest.java
index 5caf3c4..641cf78 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/back_button/BackButtonMediatorTest.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/back_button/BackButtonMediatorTest.java
@@ -33,6 +33,7 @@
 import org.chromium.chrome.browser.theme.ThemeColorProvider;
 import org.chromium.chrome.browser.ui.theme.BrandedColorScheme;
 import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.util.ClickWithMetaStateCallback;
 
 @RunWith(BaseRobolectricTestRunner.class)
 @LooperMode(LooperMode.Mode.PAUSED)
@@ -40,7 +41,7 @@
     private static final int TAB_ID = 0;
 
     @Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
-    @Mock public Callback<Integer> mOnBackPressed;
+    @Mock public ClickWithMetaStateCallback mOnBackPressed;
     @Mock public ThemeColorProvider mThemeColorProvider;
     @Mock public Callback<Tab> mShowNavigationPopup;
     @Mock public Profile mProfile;
@@ -149,8 +150,8 @@
 
     @Test
     public void testClick_shouldForwardCallToParent() {
-        mModel.get(BackButtonProperties.CLICK_LISTENER).onResult(0);
-        verify(mOnBackPressed).onResult(0);
+        mModel.get(BackButtonProperties.CLICK_LISTENER).onClickWithMeta(0);
+        verify(mOnBackPressed).onClickWithMeta(0);
     }
 
     @Test
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/back_button/BackButtonProperties.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/back_button/BackButtonProperties.java
index 7985818..6b4dee6 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/back_button/BackButtonProperties.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/back_button/BackButtonProperties.java
@@ -7,13 +7,13 @@
 import android.content.res.ColorStateList;
 import android.view.View;
 
-import org.chromium.base.Callback;
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel.WritableBooleanPropertyKey;
 import org.chromium.ui.modelutil.PropertyModel.WritableFloatPropertyKey;
 import org.chromium.ui.modelutil.PropertyModel.WritableIntPropertyKey;
 import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey;
+import org.chromium.ui.util.ClickWithMetaStateCallback;
 
 /**
  * A set of back button properties to reflect its state.
@@ -22,7 +22,7 @@
  */
 @NullMarked
 class BackButtonProperties {
-    public static final WritableObjectPropertyKey<Callback<Integer>> CLICK_LISTENER =
+    public static final WritableObjectPropertyKey<ClickWithMetaStateCallback> CLICK_LISTENER =
             new WritableObjectPropertyKey<>();
     public static final WritableObjectPropertyKey<ColorStateList> TINT_COLOR_LIST =
             new WritableObjectPropertyKey<>();
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToggleTabStackButtonCoordinator.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToggleTabStackButtonCoordinator.java
index fdc51d3..2afbf1be 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToggleTabStackButtonCoordinator.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToggleTabStackButtonCoordinator.java
@@ -324,7 +324,6 @@
     }
 
     private void maybeShowDeclutterIph(int tabCount) {
-        if (!ChromeFeatureList.sAndroidTabDeclutter.isEnabled()) return;
         if (mIsIncognitoSupplier.get()) return;
         if (mAlreadyRequestedDeclutterIph) return;
         if (tabCount == 0) return;
diff --git a/chrome/browser/ui/ash/assistant/BUILD.gn b/chrome/browser/ui/ash/assistant/BUILD.gn
index 0fbdeae..8830ee0 100644
--- a/chrome/browser/ui/ash/assistant/BUILD.gn
+++ b/chrome/browser/ui/ash/assistant/BUILD.gn
@@ -48,7 +48,6 @@
     "//chromeos/ash/experiences/arc:arc_base_utils",
     "//chromeos/ash/experiences/arc/mojom",
     "//chromeos/ash/experiences/arc/session",
-    "//chromeos/ash/services/assistant:lib",
     "//chromeos/ash/services/assistant/public/cpp",
     "//chromeos/ash/services/assistant/public/proto",
     "//chromeos/ash/services/bluetooth_config/public/mojom",
@@ -88,7 +87,6 @@
     "//chromeos/ash/components/dbus/concierge",
     "//chromeos/ash/experiences/arc:arc_base_utils",
     "//chromeos/ash/experiences/arc:arc_test_support",
-    "//chromeos/ash/services/assistant:lib",
     "//chromeos/ash/services/assistant/public/cpp",
     "//chromeos/services/assistant/public/shared",
     "//components/language/core/browser",
diff --git a/chrome/browser/ui/ash/assistant/assistant_browser_delegate_impl.cc b/chrome/browser/ui/ash/assistant/assistant_browser_delegate_impl.cc
index 304d483..0a2d633 100644
--- a/chrome/browser/ui/ash/assistant/assistant_browser_delegate_impl.cc
+++ b/chrome/browser/ui/ash/assistant/assistant_browser_delegate_impl.cc
@@ -190,11 +190,6 @@
   device_actions_ = std::make_unique<DeviceActions>(
       std::make_unique<DeviceActionsDelegateImpl>());
 
-  service_ = std::make_unique<ash::assistant::Service>(
-      profile->GetURLLoaderFactory()->Clone(),
-      IdentityManagerFactory::GetForProfile(profile), profile->GetPrefs());
-  service_->Init();
-
   assistant_setup_ = std::make_unique<AssistantSetup>();
 }
 
@@ -210,8 +205,6 @@
   if (!initialized_) {
     return;
   }
-
-  ash::assistant::AssistantService::Get()->Shutdown();
 }
 
 void AssistantBrowserDelegateImpl::InitializeNewEntryPointFor(
diff --git a/chrome/browser/ui/ash/assistant/assistant_browser_delegate_impl.h b/chrome/browser/ui/ash/assistant/assistant_browser_delegate_impl.h
index 7c99e959..cffcc92 100644
--- a/chrome/browser/ui/ash/assistant/assistant_browser_delegate_impl.h
+++ b/chrome/browser/ui/ash/assistant/assistant_browser_delegate_impl.h
@@ -18,7 +18,6 @@
 #include "chrome/browser/web_applications/web_app_registrar.h"
 #include "chromeos/ash/components/assistant/buildflags.h"
 #include "chromeos/ash/services/assistant/public/cpp/assistant_browser_delegate.h"
-#include "chromeos/ash/services/assistant/service.h"
 #include "components/session_manager/core/session_manager_observer.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
@@ -124,7 +123,6 @@
   void InitializeNewEntryPointFor(Profile* primary_profile);
 
   std::unique_ptr<DeviceActions> device_actions_;
-  std::unique_ptr<ash::assistant::Service> service_;
   std::unique_ptr<AssistantSetup> assistant_setup_;
 
   bool initialized_ = false;
diff --git a/chrome/browser/ui/ash/assistant/test_support/BUILD.gn b/chrome/browser/ui/ash/assistant/test_support/BUILD.gn
deleted file mode 100644
index 50772e3..0000000
--- a/chrome/browser/ui/ash/assistant/test_support/BUILD.gn
+++ /dev/null
@@ -1,23 +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.
-
-assert(is_chromeos)
-
-static_library("test_support") {
-  testonly = true
-
-  sources = [
-    "fake_s3_server.cc",
-    "fake_s3_server.h",
-    "test_util.h",
-  ]
-
-  deps = [
-    "//chromeos/ash/services/assistant:lib",
-    "//chromeos/assistant/internal:internal",
-    "//testing/gmock",
-    "//testing/gtest",
-    "//ui/views:views",
-  ]
-}
diff --git a/chrome/browser/ui/ash/assistant/test_support/DEPS b/chrome/browser/ui/ash/assistant/test_support/DEPS
deleted file mode 100644
index 0e2a56a..0000000
--- a/chrome/browser/ui/ash/assistant/test_support/DEPS
+++ /dev/null
@@ -1,13 +0,0 @@
-include_rules = [
-  # ChromeOS should not depend on //chrome. See //docs/chromeos/code.md for
-  # details.
-  "-chrome",
-
-  # This directory is in //chrome, which violates the rule above. Allow this
-  # directory to #include its own files.
-  "+chrome/browser/ui/ash/assistant/test_support",
-
-  # Existing dependencies within //chrome. There is an active effort to
-  # refactor ash codes in //chrome to break these dependencies; see b/332804822.
-  # Whenever possible, avoid adding new //chrome dependencies to this list.
-]
diff --git a/chrome/browser/ui/ash/assistant/test_support/fake_s3_server.cc b/chrome/browser/ui/ash/assistant/test_support/fake_s3_server.cc
deleted file mode 100644
index 9dda67d5..0000000
--- a/chrome/browser/ui/ash/assistant/test_support/fake_s3_server.cc
+++ /dev/null
@@ -1,263 +0,0 @@
-// 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.
-
-#include "chrome/browser/ui/ash/assistant/test_support/fake_s3_server.h"
-
-#include <memory>
-
-#include "base/check.h"
-#include "base/check_op.h"
-#include "base/command_line.h"
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/notreached.h"
-#include "base/path_service.h"
-#include "base/process/launch.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
-#include "chromeos/ash/services/assistant/service.h"
-#include "chromeos/assistant/internal/internal_constants.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace ash::assistant {
-
-namespace {
-
-// TODO(b/258750971): remove when internal assistant codes are migrated to
-// namespace ash.
-using ::chromeos::assistant::kFakeS3ServerBinary;
-using ::chromeos::assistant::kFakeS3ServerBinaryV2;
-using ::chromeos::assistant::kGenerateTokenInstructions;
-
-// Folder where the S3 communications are stored when running in replay mode.
-constexpr char kTestDataFolder[] = "chromeos/assistant/internal/test_data/";
-
-// Fake device id passed to Libassistant. By fixing this we ensure it remains
-// consistent between the current session and the value stored in the stored
-// test data.
-// This must be a 16 characters hex string or it will be rejected.
-constexpr char kDeviceId[] = "11112222333344445555666677778888";
-
-base::FilePath GetExecutableDir() {
-  base::FilePath result;
-  base::PathService::Get(base::DIR_EXE, &result);
-  return result;
-}
-
-base::FilePath GetSourceDir() {
-  base::FilePath result;
-  base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &result);
-  return result;
-}
-
-std::string GetSanitizedTestName() {
-  std::string test_name = base::ToLowerASCII(base::StringPrintf(
-      "%s_%s",
-      testing::UnitTest::GetInstance()->current_test_info()->test_suite_name(),
-      testing::UnitTest::GetInstance()->current_test_info()->name()));
-  // The test name may has `disabled_`. Remove it to match the data_file
-  // name.
-  base::ReplaceSubstringsAfterOffset(&test_name, 0, "disabled_", "");
-  return test_name;
-}
-
-const std::string GetAccessTokenFromEnvironmentOrDie() {
-  const char* token = std::getenv("TOKEN");
-  CHECK(token && strlen(token))
-      << "No token found in the environmental variable $TOKEN.\n"
-      << kGenerateTokenInstructions;
-  return token;
-}
-
-std::string FakeS3ModeToString(FakeS3Mode mode) {
-  switch (mode) {
-    case FakeS3Mode::kProxy:
-      return "PROXY";
-    case FakeS3Mode::kRecord:
-      return "RECORD";
-    case FakeS3Mode::kReplay:
-      return "REPLAY";
-  }
-  NOTREACHED();
-}
-
-void AppendArgument(base::CommandLine* command_line,
-                    const std::string& name,
-                    const std::string& value) {
-  // Note we can't use |AppendSwitchASCII| as that will add "<name>=<value>",
-  // and the fake s3 server binary does not support '='.
-  command_line->AppendArg(name);
-  command_line->AppendArg(value);
-}
-
-}  // namespace
-
-// Selects a port for the fake S3 server to use.
-// This will use a file-based lock because different test shards might be trying
-// to run fake S3 servers at the same time, and we need to ensure they use
-// different ports.
-class PortSelector {
- public:
-  PortSelector() { SelectPort(); }
-  PortSelector(PortSelector&) = delete;
-  PortSelector& operator=(PortSelector&) = delete;
-  ~PortSelector() {
-    lock_file_.Close();
-    base::DeletePathRecursively(GetLockFilePath());
-  }
-
-  int port() const { return port_; }
-
- private:
-  // The first port we'll try to use. Randomly chosen to be outside of the range
-  // of known ports.
-  constexpr static int kStartPort = 23600;
-  // Maximum number of ports we'll try before we give up and conclude no ports
-  // are available (which really should not happen).
-  constexpr static int kMaxAttempts = 20000;
-
-  void SelectPort() {
-    for (int offset = 0; offset + 1 < kMaxAttempts; offset += 2) {
-      port_ = kStartPort + offset;
-      lock_file_ = base::File(GetLockFilePath(), GetFileFlags());
-      if (lock_file_.IsValid()) {
-        return;
-      }
-    }
-    NOTREACHED() << "Failed to find an available port.";
-  }
-
-  base::FilePath GetLockFilePath() const {
-    std::string file_name = "port_" + base::NumberToString(port_) + "_lock";
-    return GetLockFileDirectory().Append(file_name);
-  }
-  static base::FilePath GetLockFileDirectory() {
-    base::FilePath result;
-    bool success = base::GetTempDir(&result);
-    EXPECT_TRUE(success);
-    return result;
-  }
-
-  static int GetFileFlags() {
-    return base::File::FLAG_CREATE | base::File::FLAG_WRITE;
-  }
-
-  // File exclusively opened on the file-system, to ensure no other fake S3
-  // server uses the same port.
-  base::File lock_file_;
-  int port_;
-};
-
-FakeS3Server::FakeS3Server(int data_file_version)
-    : data_file_version_(data_file_version),
-      port_selector_(std::make_unique<PortSelector>()) {
-  DCHECK_GT(data_file_version, 0);
-}
-
-FakeS3Server::~FakeS3Server() {
-  Teardown();
-}
-
-void FakeS3Server::Setup(FakeS3Mode mode) {
-  SetAccessTokenForMode(mode);
-  StartS3ServerProcess(mode);
-  SetFakeS3ServerURI();
-  SetDeviceId();
-}
-
-void FakeS3Server::Teardown() {
-  StopS3ServerProcess();
-  UnsetDeviceId();
-  UnsetFakeS3ServerURI();
-}
-
-std::string FakeS3Server::GetAccessToken() const {
-  return access_token_;
-}
-
-void FakeS3Server::SetAccessTokenForMode(FakeS3Mode mode) {
-  if (mode == FakeS3Mode::kProxy || mode == FakeS3Mode::kRecord) {
-    access_token_ = GetAccessTokenFromEnvironmentOrDie();
-  }
-}
-
-void FakeS3Server::SetFakeS3ServerURI() {
-  // Note this must be stored in a local variable, as
-  // `Service::OverrideS3ServerUriForTesting` does not take ownership of the
-  // `const char *`.
-  fake_s3_server_uri_ = "localhost:" + base::NumberToString(port());
-  Service::OverrideS3ServerUriForTesting(fake_s3_server_uri_.c_str());
-}
-
-void FakeS3Server::SetDeviceId() {
-  Service::OverrideDeviceIdForTesting(kDeviceId);
-}
-
-void FakeS3Server::UnsetDeviceId() {
-  Service::OverrideDeviceIdForTesting(nullptr);
-}
-
-void FakeS3Server::UnsetFakeS3ServerURI() {
-  Service::OverrideS3ServerUriForTesting(nullptr);
-  fake_s3_server_uri_ = "";
-}
-
-void FakeS3Server::StartS3ServerProcess(FakeS3Mode mode) {
-  if (process_running_) {
-    LOG(WARNING)
-        << "Called FakeS3Server::StartS3ServerProcess when already running.";
-    return;
-  }
-
-  base::FilePath fake_s3_server_main;
-  fake_s3_server_main =
-      GetExecutableDir().Append(FILE_PATH_LITERAL(kFakeS3ServerBinaryV2));
-
-  base::CommandLine command_line(fake_s3_server_main);
-  AppendArgument(&command_line, "--port", base::NumberToString(port()));
-  AppendArgument(&command_line, "--http_port",
-                 base::NumberToString(port() + 1));
-  AppendArgument(&command_line, "--mode", FakeS3ModeToString(mode));
-  AppendArgument(&command_line, "--auth_token", GetAccessToken());
-  AppendArgument(&command_line, "--test_data_file", GetTestDataFileName());
-
-  fake_s3_server_ = base::LaunchProcess(command_line, base::LaunchOptions{});
-  process_running_ = true;
-}
-
-void FakeS3Server::StopS3ServerProcess() {
-  if (!process_running_) {
-    LOG(WARNING)
-        << "Called FakeS3Server::StopS3ServerProcess when already stopped.";
-    return;
-  }
-  fake_s3_server_.Terminate(/*exit_code=*/0, /*wait=*/true);
-  process_running_ = false;
-}
-
-std::string FakeS3Server::GetTestDataFileName() {
-  auto create_file_path = [](const std::string& test_name, int version) {
-    return GetSourceDir()
-        .Append(FILE_PATH_LITERAL(kTestDataFolder))
-        .Append(FILE_PATH_LITERAL(test_name + ".v" +
-                                  base::NumberToString(version) +
-                                  ".fake_s3.proto"));
-  };
-  // Look for the latest version of the data file, if not found, look for older
-  // ones.
-  auto data_file = create_file_path(GetSanitizedTestName(), data_file_version_);
-  for (int version = data_file_version_ - 1;
-       !base::PathExists(data_file) && version > 0; --version) {
-    data_file = create_file_path(GetSanitizedTestName(), version);
-  }
-
-  return data_file.MaybeAsASCII();
-}
-
-int FakeS3Server::port() const {
-  return port_selector_->port();
-}
-
-}  // namespace ash::assistant
diff --git a/chrome/browser/ui/ash/assistant/test_support/fake_s3_server.h b/chrome/browser/ui/ash/assistant/test_support/fake_s3_server.h
deleted file mode 100644
index 186a1bf5..0000000
--- a/chrome/browser/ui/ash/assistant/test_support/fake_s3_server.h
+++ /dev/null
@@ -1,81 +0,0 @@
-// 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.
-
-#ifndef CHROME_BROWSER_UI_ASH_ASSISTANT_TEST_SUPPORT_FAKE_S3_SERVER_H_
-#define CHROME_BROWSER_UI_ASH_ASSISTANT_TEST_SUPPORT_FAKE_S3_SERVER_H_
-
-#include <memory>
-#include <string>
-
-#include "base/process/process.h"
-
-namespace ash::assistant {
-
-class PortSelector;
-
-enum class FakeS3Mode {
-  // In this mode all S3 requests are forwarded to the S3 server.
-  kProxy,
-  // In this mode all S3 requests are forwarded to the S3 server, and the
-  // responses are recorded.
-  kRecord,
-  // In this mode all S3 requests are handled by replaying the responses stored
-  // while running in |kRecord| mode.
-  kReplay,
-};
-
-// Class that starts/stops a fake S3 server.
-// Note that this will also ensure the Assistant service knows to use the fake
-// s3 server.
-//
-// A valid access token is required if mode is |kProxy| or |kReplay|. See
-// |kGenerateTokenInstructions| for information on how to get one.
-class FakeS3Server {
- public:
-  // |data_file_version| is used to look for a particular set of test data
-  // files to use. This enables updating tests and checking in test data and
-  // test themselves separately.  If the latest version of the test data file
-  // does not exist, it will automatically looker for an older version of the
-  // file.
-  explicit FakeS3Server(int data_file_version);
-
-  FakeS3Server(const FakeS3Server&) = delete;
-  FakeS3Server& operator=(const FakeS3Server&) = delete;
-
-  ~FakeS3Server();
-
-  // Starts the fake S3 server, and tells the Assistant service to use its URI
-  // for all S3 requests.
-  void Setup(FakeS3Mode mode);
-  void Teardown();
-
-  // Returns the access token used by the S3 Server. This is only populated
-  // after |Setup| is called.
-  std::string GetAccessToken() const;
-
- private:
-  void SetAccessTokenForMode(FakeS3Mode mode);
-  void SetFakeS3ServerURI();
-  void UnsetFakeS3ServerURI();
-  void SetDeviceId();
-  void UnsetDeviceId();
-  void StartS3ServerProcess(FakeS3Mode mode);
-  void StopS3ServerProcess();
-  std::string GetTestDataFileName();
-
-  int port() const;
-
-  std::string access_token_{"FAKE_ACCESS_TOKEN"};
-  std::string fake_s3_server_uri_;
-  int data_file_version_;
-  bool process_running_ = false;
-
-  std::unique_ptr<PortSelector> port_selector_;
-
-  base::Process fake_s3_server_;
-};
-
-}  // namespace ash::assistant
-
-#endif  // CHROME_BROWSER_UI_ASH_ASSISTANT_TEST_SUPPORT_FAKE_S3_SERVER_H_
diff --git a/chrome/browser/ui/ash/assistant/test_support/test_util.h b/chrome/browser/ui/ash/assistant/test_support/test_util.h
deleted file mode 100644
index 711721c6..0000000
--- a/chrome/browser/ui/ash/assistant/test_support/test_util.h
+++ /dev/null
@@ -1,44 +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.
-
-#ifndef CHROME_BROWSER_UI_ASH_ASSISTANT_TEST_SUPPORT_TEST_UTIL_H_
-#define CHROME_BROWSER_UI_ASH_ASSISTANT_TEST_SUPPORT_TEST_UTIL_H_
-
-#include <string>
-#include <vector>
-
-#include "ui/views/view.h"
-
-namespace ash::assistant {
-
-// Finds any descendents of |parent| with the desired |class_name| and pushes
-// them onto the strongly typed |result| vector.
-// NOTE: Callers are expected to ensure that casting to <T> makes sense. It is
-// preferred to use the two argument variant of FindDescendentsOfClass() when
-// possible for stronger type safety.
-template <typename T>
-void FindDescendentsOfClass(views::View* parent,
-                            const std::string& class_name,
-                            std::vector<T*>* result) {
-  for (views::View* child : parent->children()) {
-    if (child->GetClassName() == class_name) {
-      result->push_back(static_cast<T*>(child));
-    }
-    FindDescendentsOfClass(child, class_name, result);
-  }
-}
-
-// Finds any descendents of |parent| with class name equal to the static class
-// variable |kViewClassName| and pushes them onto the strongly typed |result|
-// vector.
-// NOTE: This variant of FindDescendentsOfClass() is safer than the three
-// argument variant and its usage should be preferred where possible.
-template <typename T>
-void FindDescendentsOfClass(views::View* parent, std::vector<T*>* result) {
-  FindDescendentsOfClass(parent, T::kViewClassName, result);
-}
-
-}  // namespace ash::assistant
-
-#endif  // CHROME_BROWSER_UI_ASH_ASSISTANT_TEST_SUPPORT_TEST_UTIL_H_
diff --git a/chrome/browser/ui/ash/desks/chrome_desks_util.h b/chrome/browser/ui/ash/desks/chrome_desks_util.h
index 4f583d59..c9b66f2 100644
--- a/chrome/browser/ui/ash/desks/chrome_desks_util.h
+++ b/chrome/browser/ui/ash/desks/chrome_desks_util.h
@@ -16,7 +16,7 @@
 namespace chrome_desks_util {
 
 // Name for app not available toast.
-constexpr char kAppNotAvailableTemplateToastName[] =
+inline constexpr char kAppNotAvailableTemplateToastName[] =
     "AppNotAvailableTemplateToast";
 
 // Given a TabGroupModel that contains at least a single TabGroup this method
diff --git a/chrome/browser/ui/ash/editor_menu/BUILD.gn b/chrome/browser/ui/ash/editor_menu/BUILD.gn
index a4ab61d..3ec42db27 100644
--- a/chrome/browser/ui/ash/editor_menu/BUILD.gn
+++ b/chrome/browser/ui/ash/editor_menu/BUILD.gn
@@ -21,7 +21,6 @@
 
   deps = [
     "//base",
-    "//chrome/browser:browser_process",
     "//chromeos/ash/components/editor_menu/public/cpp",
     "//chromeos/crosapi/mojom",
     "//ui/aura",
@@ -98,6 +97,7 @@
     "//chromeos/constants:constants",
     "//chromeos/strings:strings_grit",
     "//chromeos/ui/vector_icons",
+    "//components/application_locale_storage",
     "//components/vector_icons",
     "//ui/aura",
     "//ui/base",
diff --git a/chrome/browser/ui/ash/editor_menu/DEPS b/chrome/browser/ui/ash/editor_menu/DEPS
index c43c417..6d35abdd 100644
--- a/chrome/browser/ui/ash/editor_menu/DEPS
+++ b/chrome/browser/ui/ash/editor_menu/DEPS
@@ -4,9 +4,14 @@
 
   "+chrome/browser/ash/input_method",
   "+chrome/browser/ash/lobster",
-  "+chrome/browser/browser_process.h",
   "+chrome/browser/profiles/profile.h",
   "+chrome/browser/ui/ash/read_write_cards",
   "+chrome/browser/ui/browser.h",
   "+chrome/test/views/chrome_views_test_base.h",
 ]
+
+specific_include_rules = {
+  ".*unittest\\.cc": [
+    "+chrome/browser/global_features.h",
+  ],
+}
diff --git a/chrome/browser/ui/ash/editor_menu/editor_menu_controller_impl.cc b/chrome/browser/ui/ash/editor_menu/editor_menu_controller_impl.cc
index fa22ef2..4933bbf9 100644
--- a/chrome/browser/ui/ash/editor_menu/editor_menu_controller_impl.cc
+++ b/chrome/browser/ui/ash/editor_menu/editor_menu_controller_impl.cc
@@ -14,7 +14,9 @@
 #include "ash/shell.h"
 #include "ash/webui/settings/public/constants/routes.mojom.h"
 #include "ash/webui/settings/public/constants/setting.mojom.h"
+#include "base/check_deref.h"
 #include "base/feature_list.h"
+#include "base/memory/raw_ref.h"
 #include "base/notreached.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_number_conversions.h"
@@ -79,7 +81,9 @@
 
 }  // namespace
 
-EditorMenuControllerImpl::EditorMenuControllerImpl() = default;
+EditorMenuControllerImpl::EditorMenuControllerImpl(
+    const ApplicationLocaleStorage* application_locale_storage)
+    : application_locale_storage_(CHECK_DEREF(application_locale_storage)) {}
 
 EditorMenuControllerImpl::~EditorMenuControllerImpl() = default;
 
@@ -328,8 +332,8 @@
       if (chromeos::features::IsMagicBoostRevampEnabled()) {
         NOTREACHED();
       }
-      editor_menu_widget_ =
-          EditorMenuPromoCardView::CreateWidget(anchor_bounds, this);
+      editor_menu_widget_ = EditorMenuPromoCardView::CreateWidget(
+          &application_locale_storage_.get(), anchor_bounds, this);
       editor_menu_widget_->ShowInactive();
       break;
     case TextAndImageMode::kEditorWriteOnly:
@@ -339,8 +343,8 @@
     case TextAndImageMode::kEditorWriteAndLobster:
     case TextAndImageMode::kEditorRewriteAndLobster:
       editor_menu_widget_ = EditorMenuView::CreateWidget(
-          text_and_image_mode, editor_menu_card_context.preset_queries(),
-          anchor_bounds, this);
+          &application_locale_storage_.get(), text_and_image_mode,
+          editor_menu_card_context.preset_queries(), anchor_bounds, this);
       editor_menu_widget_->ShowInactive();
       break;
   }
diff --git a/chrome/browser/ui/ash/editor_menu/editor_menu_controller_impl.h b/chrome/browser/ui/ash/editor_menu/editor_menu_controller_impl.h
index 899d542..12989f3 100644
--- a/chrome/browser/ui/ash/editor_menu/editor_menu_controller_impl.h
+++ b/chrome/browser/ui/ash/editor_menu/editor_menu_controller_impl.h
@@ -9,6 +9,7 @@
 #include <string_view>
 
 #include "ash/lobster/lobster_controller.h"
+#include "base/memory/raw_ref.h"
 #include "base/memory/weak_ptr.h"
 #include "build/build_config.h"
 #include "chrome/browser/ui/ash/editor_menu/editor_manager.h"
@@ -21,12 +22,13 @@
 #include "chromeos/ash/components/editor_menu/public/cpp/editor_mode.h"
 #include "content/public/browser/browser_context.h"
 
+class ApplicationLocaleStorage;
+class Profile;
+
 namespace views {
 class Widget;
 }
 
-class Profile;
-
 namespace chromeos::editor_menu {
 
 // Implementation of ReadWriteCardController. It manages the editor menu related
@@ -34,7 +36,8 @@
 class EditorMenuControllerImpl : public chromeos::ReadWriteCardController,
                                  public EditorMenuViewDelegate {
  public:
-  EditorMenuControllerImpl();
+  explicit EditorMenuControllerImpl(
+      const ApplicationLocaleStorage* application_locale_storage);
   EditorMenuControllerImpl(const EditorMenuControllerImpl&) = delete;
   EditorMenuControllerImpl& operator=(const EditorMenuControllerImpl&) = delete;
   ~EditorMenuControllerImpl() override;
@@ -138,6 +141,8 @@
   // buttons or textfield to receive keyboard or mouse input.
   void DisableEditorMenu();
 
+  const raw_ref<const ApplicationLocaleStorage> application_locale_storage_;
+
   std::unique_ptr<views::Widget> editor_menu_widget_;
 
   // May hold the currently active editor card session. If this is nullptr then
diff --git a/chrome/browser/ui/ash/editor_menu/editor_menu_promo_card_view.cc b/chrome/browser/ui/ash/editor_menu/editor_menu_promo_card_view.cc
index 1f5abd96..16a23cd9 100644
--- a/chrome/browser/ui/ash/editor_menu/editor_menu_promo_card_view.cc
+++ b/chrome/browser/ui/ash/editor_menu/editor_menu_promo_card_view.cc
@@ -4,8 +4,10 @@
 
 #include "chrome/browser/ui/ash/editor_menu/editor_menu_promo_card_view.h"
 
+#include "base/check_deref.h"
 #include "base/functional/bind.h"
 #include "base/memory/raw_ptr.h"
+#include "base/memory/raw_ref.h"
 #include "base/task/sequenced_task_runner.h"
 #include "chrome/browser/ui/ash/editor_menu/editor_menu_strings.h"
 #include "chrome/browser/ui/ash/editor_menu/editor_menu_view_delegate.h"
@@ -13,6 +15,7 @@
 #include "chrome/browser/ui/ash/editor_menu/utils/utils.h"
 #include "chromeos/strings/grit/chromeos_strings.h"
 #include "chromeos/ui/vector_icons/vector_icons.h"
+#include "components/application_locale_storage/application_locale_storage.h"
 #include "ui/base/accelerators/accelerator.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/base/models/image_model.h"
@@ -63,9 +66,11 @@
 }  // namespace
 
 EditorMenuPromoCardView::EditorMenuPromoCardView(
+    const ApplicationLocaleStorage* application_locale_storage,
     const gfx::Rect& anchor_view_bounds,
     EditorMenuViewDelegate* delegate)
-    : pre_target_handler_(
+    : application_locale_storage_(CHECK_DEREF(application_locale_storage)),
+      pre_target_handler_(
           std::make_unique<PreTargetHandler>(/*delegate=*/*this,
                                              CardType::kEditorMenu)),
       delegate_(delegate) {
@@ -80,6 +85,7 @@
 
 // static
 std::unique_ptr<views::Widget> EditorMenuPromoCardView::CreateWidget(
+    const ApplicationLocaleStorage* application_locale_storage,
     const gfx::Rect& anchor_view_bounds,
     EditorMenuViewDelegate* delegate) {
   views::Widget::InitParams params(
@@ -95,7 +101,7 @@
   auto widget = std::make_unique<views::Widget>(std::move(params));
   EditorMenuPromoCardView* editor_menu_promo_card_view =
       widget->SetContentsView(std::make_unique<EditorMenuPromoCardView>(
-          anchor_view_bounds, delegate));
+          application_locale_storage, anchor_view_bounds, delegate));
   editor_menu_promo_card_view->UpdateBounds(anchor_view_bounds);
 
   return widget;
@@ -177,7 +183,8 @@
 
 void EditorMenuPromoCardView::UpdateBounds(
     const gfx::Rect& anchor_view_bounds) {
-  GetWidget()->SetBounds(GetEditorMenuBounds(anchor_view_bounds, this));
+  GetWidget()->SetBounds(GetEditorMenuBounds(
+      anchor_view_bounds, this, application_locale_storage_->Get()));
 }
 
 views::View* EditorMenuPromoCardView::GetRootView() {
diff --git a/chrome/browser/ui/ash/editor_menu/editor_menu_promo_card_view.h b/chrome/browser/ui/ash/editor_menu/editor_menu_promo_card_view.h
index 60c1ba3..87b048c 100644
--- a/chrome/browser/ui/ash/editor_menu/editor_menu_promo_card_view.h
+++ b/chrome/browser/ui/ash/editor_menu/editor_menu_promo_card_view.h
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "base/memory/raw_ptr.h"
+#include "base/memory/raw_ref.h"
 #include "base/memory/weak_ptr.h"
 #include "base/scoped_observation.h"
 #include "chrome/browser/ui/ash/editor_menu/utils/pre_target_handler.h"
@@ -15,6 +16,8 @@
 #include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_observer.h"
 
+class ApplicationLocaleStorage;
+
 namespace views {
 class Label;
 class MdTextButton;
@@ -31,15 +34,20 @@
   METADATA_HEADER(EditorMenuPromoCardView, views::View)
 
  public:
-  EditorMenuPromoCardView(const gfx::Rect& anchor_view_bounds,
-                          EditorMenuViewDelegate* delegate);
+  // `application_locale_storage` must be non-null and must outlive `this`.
+  EditorMenuPromoCardView(
+      const ApplicationLocaleStorage* application_locale_storage,
+      const gfx::Rect& anchor_view_bounds,
+      EditorMenuViewDelegate* delegate);
 
   EditorMenuPromoCardView(const EditorMenuPromoCardView&) = delete;
   EditorMenuPromoCardView& operator=(const EditorMenuPromoCardView&) = delete;
 
   ~EditorMenuPromoCardView() override;
 
+  // `application_locale_storage` must be non-null and must outlive the widget.
   static std::unique_ptr<views::Widget> CreateWidget(
+      const ApplicationLocaleStorage* application_locale_storage,
       const gfx::Rect& anchor_view_bounds,
       EditorMenuViewDelegate* delegate);
 
@@ -76,6 +84,8 @@
 
   void ResetPreTargetHandler();
 
+  const raw_ref<const ApplicationLocaleStorage> application_locale_storage_;
+
   std::unique_ptr<PreTargetHandler> pre_target_handler_;
 
   // `delegate_` outlives `this`.
diff --git a/chrome/browser/ui/ash/editor_menu/editor_menu_view.cc b/chrome/browser/ui/ash/editor_menu/editor_menu_view.cc
index 973a27a..866d29d 100644
--- a/chrome/browser/ui/ash/editor_menu/editor_menu_view.cc
+++ b/chrome/browser/ui/ash/editor_menu/editor_menu_view.cc
@@ -10,9 +10,11 @@
 #include <utility>
 #include <vector>
 
+#include "base/check_deref.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback_forward.h"
 #include "base/memory/raw_ptr.h"
+#include "base/memory/raw_ref.h"
 #include "chrome/browser/ui/ash/editor_menu/editor_menu_badge_view.h"
 #include "chrome/browser/ui/ash/editor_menu/editor_menu_chip_view.h"
 #include "chrome/browser/ui/ash/editor_menu/editor_menu_strings.h"
@@ -24,6 +26,7 @@
 #include "chromeos/ash/components/editor_menu/public/cpp/icon.h"
 #include "chromeos/ash/components/editor_menu/public/cpp/preset_text_query.h"
 #include "chromeos/strings/grit/chromeos_strings.h"
+#include "components/application_locale_storage/application_locale_storage.h"
 #include "components/vector_icons/vector_icons.h"
 #include "ui/base/accelerators/accelerator.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
@@ -108,11 +111,14 @@
   return total_chips_height + total_chips_paddings;
 }
 
-EditorMenuView::EditorMenuView(TextAndImageMode text_and_image_mode,
-                               const PresetTextQueries& preset_text_queries,
-                               const gfx::Rect& anchor_view_bounds,
-                               EditorMenuViewDelegate* delegate)
+EditorMenuView::EditorMenuView(
+    const ApplicationLocaleStorage* application_locale_storage,
+    TextAndImageMode text_and_image_mode,
+    const PresetTextQueries& preset_text_queries,
+    const gfx::Rect& anchor_view_bounds,
+    EditorMenuViewDelegate* delegate)
     : PreTargetHandlerView(CardType::kEditorMenu),
+      application_locale_storage_(CHECK_DEREF(application_locale_storage)),
       text_and_image_mode_(text_and_image_mode),
       delegate_(delegate) {
   CHECK(delegate_);
@@ -127,6 +133,7 @@
 
 // static
 std::unique_ptr<views::Widget> EditorMenuView::CreateWidget(
+    const ApplicationLocaleStorage* application_locale_storage,
     TextAndImageMode text_and_image_mode,
     const PresetTextQueries& preset_text_queries,
     const gfx::Rect& anchor_view_bounds,
@@ -144,9 +151,10 @@
   params.name = kWidgetName;
 
   auto widget = std::make_unique<views::Widget>(std::move(params));
-  EditorMenuView* editor_menu_view = widget->SetContentsView(
-      std::make_unique<EditorMenuView>(text_and_image_mode, preset_text_queries,
-                                       anchor_view_bounds, delegate));
+  EditorMenuView* editor_menu_view =
+      widget->SetContentsView(std::make_unique<EditorMenuView>(
+          application_locale_storage, text_and_image_mode, preset_text_queries,
+          anchor_view_bounds, delegate));
   editor_menu_view->UpdateBounds(anchor_view_bounds);
 
   return widget;
@@ -244,7 +252,8 @@
 }
 
 void EditorMenuView::UpdateBounds(const gfx::Rect& anchor_view_bounds) {
-  gfx::Rect editor_menu_bounds = GetEditorMenuBounds(anchor_view_bounds, this);
+  gfx::Rect editor_menu_bounds = GetEditorMenuBounds(
+      anchor_view_bounds, this, application_locale_storage_->Get());
   GetWidget()->SetBounds(editor_menu_bounds);
   UpdateChipsContainer(/*editor_menu_width=*/editor_menu_bounds.width());
 }
diff --git a/chrome/browser/ui/ash/editor_menu/editor_menu_view.h b/chrome/browser/ui/ash/editor_menu/editor_menu_view.h
index 2e51b5c..bc6a3c18 100644
--- a/chrome/browser/ui/ash/editor_menu/editor_menu_view.h
+++ b/chrome/browser/ui/ash/editor_menu/editor_menu_view.h
@@ -8,6 +8,7 @@
 #include <string>
 #include <vector>
 
+#include "base/memory/raw_ref.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/ui/ash/editor_menu/utils/pre_target_handler_view.h"
 #include "chrome/browser/ui/ash/editor_menu/utils/text_and_image_mode.h"
@@ -17,6 +18,8 @@
 #include "ui/views/controls/tabbed_pane/tabbed_pane_listener.h"
 #include "ui/views/layout/flex_layout_view.h"
 
+class ApplicationLocaleStorage;
+
 namespace views {
 class ImageButton;
 class FlexLayoutView;
@@ -35,7 +38,9 @@
   METADATA_HEADER(EditorMenuView, views::View)
 
  public:
-  EditorMenuView(TextAndImageMode text_and_image_mode,
+  // `application_locale_storage` must be non-null and must outlive `this`.
+  EditorMenuView(const ApplicationLocaleStorage* application_locale_storage,
+                 TextAndImageMode text_and_image_mode,
                  const PresetTextQueries& preset_text_queries,
                  const gfx::Rect& anchor_view_bounds,
                  EditorMenuViewDelegate* delegate);
@@ -45,7 +50,9 @@
 
   ~EditorMenuView() override;
 
+  // `application_locale_storage` must be non-null and must outlive the widget.
   static std::unique_ptr<views::Widget> CreateWidget(
+      const ApplicationLocaleStorage* application_locale_storage,
       TextAndImageMode text_and_image_mode,
       const PresetTextQueries& preset_text_queries,
       const gfx::Rect& anchor_view_bounds,
@@ -73,8 +80,6 @@
   static const char* GetWidgetNameForTest();
 
  private:
-  const TextAndImageMode text_and_image_mode_;
-
   void InitLayout(const PresetTextQueries& preset_text_queries);
   gfx::Insets GetTitleContainerInsets() const;
   void AddTitleContainer();
@@ -88,6 +93,10 @@
   void OnSettingsButtonPressed();
   void OnChipButtonPressed(const std::string& text_query_id);
 
+  const raw_ref<const ApplicationLocaleStorage> application_locale_storage_;
+
+  const TextAndImageMode text_and_image_mode_;
+
   // `delegate_` outlives `this`.
   raw_ptr<EditorMenuViewDelegate> delegate_ = nullptr;
 
diff --git a/chrome/browser/ui/ash/editor_menu/editor_menu_view_unittest.cc b/chrome/browser/ui/ash/editor_menu/editor_menu_view_unittest.cc
index dbc1774..61cd8947 100644
--- a/chrome/browser/ui/ash/editor_menu/editor_menu_view_unittest.cc
+++ b/chrome/browser/ui/ash/editor_menu/editor_menu_view_unittest.cc
@@ -7,11 +7,13 @@
 #include <string_view>
 
 #include "base/test/scoped_feature_list.h"
+#include "chrome/browser/global_features.h"
 #include "chrome/browser/ui/ash/editor_menu/editor_menu_chip_view.h"
 #include "chrome/browser/ui/ash/editor_menu/editor_menu_strings.h"
 #include "chrome/browser/ui/ash/editor_menu/editor_menu_textfield_view.h"
 #include "chrome/browser/ui/ash/editor_menu/editor_menu_view.h"
 #include "chrome/browser/ui/ash/editor_menu/editor_menu_view_delegate.h"
+#include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/views/chrome_views_test_base.h"
 #include "chromeos/ash/components/editor_menu/public/cpp/preset_text_query.h"
 #include "chromeos/constants/chromeos_features.h"
@@ -81,7 +83,10 @@
       PresetTextQuery("ID2", u"Elaborate", PresetQueryCategory::kElaborate)};
 
   std::unique_ptr<views::Widget> editor_menu_widget =
-      EditorMenuView::CreateWidget(TextAndImageMode::kEditorRewriteOnly,
+      EditorMenuView::CreateWidget(TestingBrowserProcess::GetGlobal()
+                                       ->GetFeatures()
+                                       ->application_locale_storage(),
+                                   TextAndImageMode::kEditorRewriteOnly,
                                    queries, gfx::Rect(200, 300, 400, 200),
                                    &delegate);
   auto* editor_menu_view =
@@ -107,7 +112,10 @@
       PresetTextQuery("ID5", u"Formalize", PresetQueryCategory::kFormalize)};
 
   std::unique_ptr<views::Widget> editor_menu_widget =
-      EditorMenuView::CreateWidget(TextAndImageMode::kEditorRewriteOnly,
+      EditorMenuView::CreateWidget(TestingBrowserProcess::GetGlobal()
+                                       ->GetFeatures()
+                                       ->application_locale_storage(),
+                                   TextAndImageMode::kEditorRewriteOnly,
                                    queries, gfx::Rect(200, 300, 400, 200),
                                    &delegate);
   auto* editor_menu_view =
@@ -131,7 +139,10 @@
 
   // Create and focus the editor menu.
   std::unique_ptr<views::Widget> editor_menu_widget =
-      EditorMenuView::CreateWidget(TextAndImageMode::kEditorRewriteOnly,
+      EditorMenuView::CreateWidget(TestingBrowserProcess::GetGlobal()
+                                       ->GetFeatures()
+                                       ->application_locale_storage(),
+                                   TextAndImageMode::kEditorRewriteOnly,
                                    queries, gfx::Rect(200, 300, 400, 200),
                                    &delegate);
   editor_menu_widget->Show();
@@ -173,7 +184,10 @@
 
   // Create and show the editor menu.
   std::unique_ptr<views::Widget> editor_menu_widget =
-      EditorMenuView::CreateWidget(TextAndImageMode::kEditorRewriteOnly,
+      EditorMenuView::CreateWidget(TestingBrowserProcess::GetGlobal()
+                                       ->GetFeatures()
+                                       ->application_locale_storage(),
+                                   TextAndImageMode::kEditorRewriteOnly,
                                    queries, gfx::Rect(200, 300, 400, 200),
                                    &delegate);
   editor_menu_widget->Show();
@@ -200,7 +214,10 @@
 
   // Create and show the editor menu.
   std::unique_ptr<views::Widget> editor_menu_widget =
-      EditorMenuView::CreateWidget(TextAndImageMode::kEditorRewriteOnly,
+      EditorMenuView::CreateWidget(TestingBrowserProcess::GetGlobal()
+                                       ->GetFeatures()
+                                       ->application_locale_storage(),
+                                   TextAndImageMode::kEditorRewriteOnly,
                                    PresetTextQueries(),
                                    gfx::Rect(200, 300, 400, 200), &delegate);
   editor_menu_widget->Show();
@@ -230,7 +247,10 @@
       PresetTextQuery("ID2", u"Emojify", PresetQueryCategory::kEmojify)};
 
   std::unique_ptr<views::Widget> editor_menu_widget =
-      EditorMenuView::CreateWidget(TextAndImageMode::kEditorRewriteOnly,
+      EditorMenuView::CreateWidget(TestingBrowserProcess::GetGlobal()
+                                       ->GetFeatures()
+                                       ->application_locale_storage(),
+                                   TextAndImageMode::kEditorRewriteOnly,
                                    queries, gfx::Rect(200, 300, 400, 200),
                                    &delegate);
   editor_menu_widget->Show();
@@ -257,7 +277,10 @@
       PresetTextQuery("ID2", u"Emojify", PresetQueryCategory::kEmojify)};
 
   std::unique_ptr<views::Widget> editor_menu_widget =
-      EditorMenuView::CreateWidget(TextAndImageMode::kEditorWriteAndLobster,
+      EditorMenuView::CreateWidget(TestingBrowserProcess::GetGlobal()
+                                       ->GetFeatures()
+                                       ->application_locale_storage(),
+                                   TextAndImageMode::kEditorWriteAndLobster,
                                    queries, gfx::Rect(200, 300, 400, 200),
                                    &delegate);
   editor_menu_widget->Show();
@@ -285,7 +308,10 @@
 
   // Rewrite Editor Mode
   std::unique_ptr<views::Widget> editor_menu_widget =
-      EditorMenuView::CreateWidget(TextAndImageMode::kEditorRewriteOnly,
+      EditorMenuView::CreateWidget(TestingBrowserProcess::GetGlobal()
+                                       ->GetFeatures()
+                                       ->application_locale_storage(),
+                                   TextAndImageMode::kEditorRewriteOnly,
                                    queries, gfx::Rect(200, 300, 400, 200),
                                    &delegate);
   editor_menu_widget->Show();
@@ -300,7 +326,10 @@
 
   // Write Editor Mode
   editor_menu_widget =
-      EditorMenuView::CreateWidget(TextAndImageMode::kEditorWriteOnly, queries,
+      EditorMenuView::CreateWidget(TestingBrowserProcess::GetGlobal()
+                                       ->GetFeatures()
+                                       ->application_locale_storage(),
+                                   TextAndImageMode::kEditorWriteOnly, queries,
                                    gfx::Rect(200, 300, 400, 200), &delegate);
   editor_menu_widget->Show();
   editor_menu_view =
@@ -321,7 +350,10 @@
 
   // Rewrite Editor Mode
   std::unique_ptr<views::Widget> editor_menu_widget =
-      EditorMenuView::CreateWidget(TextAndImageMode::kEditorRewriteOnly,
+      EditorMenuView::CreateWidget(TestingBrowserProcess::GetGlobal()
+                                       ->GetFeatures()
+                                       ->application_locale_storage(),
+                                   TextAndImageMode::kEditorRewriteOnly,
                                    queries, gfx::Rect(200, 300, 400, 200),
                                    &delegate);
   editor_menu_widget->Show();
@@ -336,7 +368,10 @@
 
   // Write Editor Mode
   editor_menu_widget =
-      EditorMenuView::CreateWidget(TextAndImageMode::kEditorWriteOnly, queries,
+      EditorMenuView::CreateWidget(TestingBrowserProcess::GetGlobal()
+                                       ->GetFeatures()
+                                       ->application_locale_storage(),
+                                   TextAndImageMode::kEditorWriteOnly, queries,
                                    gfx::Rect(200, 300, 400, 200), &delegate);
   editor_menu_widget->Show();
   editor_menu_view =
diff --git a/chrome/browser/ui/ash/editor_menu/utils/utils.cc b/chrome/browser/ui/ash/editor_menu/utils/utils.cc
index e3ff5a2..d9469e5b 100644
--- a/chrome/browser/ui/ash/editor_menu/utils/utils.cc
+++ b/chrome/browser/ui/ash/editor_menu/utils/utils.cc
@@ -4,7 +4,8 @@
 
 #include "chrome/browser/ui/ash/editor_menu/utils/utils.h"
 
-#include "chrome/browser/browser_process.h"
+#include <string>
+
 #include "ui/display/screen.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
@@ -13,14 +14,8 @@
 
 namespace {
 
-std::string GetSystemLocale() {
-  return g_browser_process != nullptr
-             ? g_browser_process->GetApplicationLocale()
-             : "";
-}
-
-int ComputeWidthOnSide() {
-  if (GetSystemLocale() == "ta") {
+int ComputeWidthOnSide(const std::string& app_locale) {
+  if (app_locale == "ta") {
     return kBigEditorMenuMinWidthDip;
   }
   return kEditorMenuMinWidthDip;
@@ -31,7 +26,8 @@
     const views::View* target,
     const gfx::Rect screen_work_area,
     const gfx::Point cursor_point,
-    const CardType& card_type) {
+    const CardType& card_type,
+    const std::string& application_locale) {
   const int width_on_top_or_bottom = std::max(
       card_type == CardType::kMahiDefaultMenu ? kMahiMenuTopBottomMinWidthDip
                                               : kEditorMenuMinWidthDip,
@@ -39,7 +35,7 @@
   const int height_on_top_or_bottom =
       target->GetHeightForWidth(width_on_top_or_bottom);
 
-  const int width_on_side = ComputeWidthOnSide();
+  const int width_on_side = ComputeWidthOnSide(application_locale);
   const int height_on_side = target->GetHeightForWidth(width_on_side);
 
   // The vertical starting position of top side candidates which makes them be
@@ -212,6 +208,7 @@
 
 gfx::Rect GetEditorMenuBounds(const gfx::Rect& anchor_view_bounds,
                               const views::View* target,
+                              const std::string& application_locale,
                               const CardType card_type) {
   display::Screen* screen = display::Screen::GetScreen();
   const gfx::Rect screen_work_area =
@@ -219,7 +216,8 @@
   const gfx::Point cursor_point = screen->GetCursorScreenPoint();
 
   std::vector<gfx::Rect> candidates = GetEditorMenuBoundsCandidates(
-      anchor_view_bounds, target, screen_work_area, cursor_point, card_type);
+      anchor_view_bounds, target, screen_work_area, cursor_point, card_type,
+      application_locale);
   return PickBestEditorMenuBounds(candidates, screen_work_area, cursor_point);
 }
 
diff --git a/chrome/browser/ui/ash/editor_menu/utils/utils.h b/chrome/browser/ui/ash/editor_menu/utils/utils.h
index d9fe0b0..18a6f45d 100644
--- a/chrome/browser/ui/ash/editor_menu/utils/utils.h
+++ b/chrome/browser/ui/ash/editor_menu/utils/utils.h
@@ -83,6 +83,7 @@
 //
 gfx::Rect GetEditorMenuBounds(const gfx::Rect& anchor_view_bounds,
                               const views::View* target,
+                              const std::string& application_locale,
                               const CardType card_type = CardType::kDefault);
 
 }  // namespace chromeos::editor_menu
diff --git a/chrome/browser/ui/ash/editor_menu/utils/utils_unittest.cc b/chrome/browser/ui/ash/editor_menu/utils/utils_unittest.cc
index 755ad4d7..6b4b381 100644
--- a/chrome/browser/ui/ash/editor_menu/utils/utils_unittest.cc
+++ b/chrome/browser/ui/ash/editor_menu/utils/utils_unittest.cc
@@ -4,6 +4,11 @@
 
 #include "utils.h"
 
+#include <string>
+
+#include "chrome/browser/global_features.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "components/application_locale_storage/application_locale_storage.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/display/test/test_screen.h"
@@ -287,9 +292,13 @@
 TEST_P(GetEditorMenuBoundsTest, Verify) {
   test_screen_.set_cursor_screen_point(GetParam().cursor_point);
 
+  const std::string& locale = TestingBrowserProcess::GetGlobal()
+                                  ->GetFeatures()
+                                  ->application_locale_storage()
+                                  ->Get();
   const gfx::Rect editor_menu_bounds =
       chromeos::editor_menu::GetEditorMenuBounds(GetParam().anchor_view_bounds,
-                                                 target_.get());
+                                                 target_.get(), locale);
 
   EXPECT_EQ(editor_menu_bounds.x(), GetParam().editor_menu_bounds.x());
   EXPECT_EQ(editor_menu_bounds.y(), GetParam().editor_menu_bounds.y());
diff --git a/chrome/browser/ui/ash/google_one/google_one_offer_iph_tab_helper_constants.h b/chrome/browser/ui/ash/google_one/google_one_offer_iph_tab_helper_constants.h
index 7608a9a2..119b20172 100644
--- a/chrome/browser/ui/ash/google_one/google_one_offer_iph_tab_helper_constants.h
+++ b/chrome/browser/ui/ash/google_one/google_one_offer_iph_tab_helper_constants.h
@@ -7,12 +7,13 @@
 
 // Fallback texts are used if params are not provided, e.g. IPH demo mode. We
 // won't expect that those fallback texts are used in prod.
-constexpr char kFallbackNotificationDisplaySource[] = "ChromeOS Perks";
-constexpr char kFallbackNotificationTitle[] = "Get 100 GB of cloud storage";
-constexpr char kFallbackNotificationMessage[] =
+inline constexpr char kFallbackNotificationDisplaySource[] = "ChromeOS Perks";
+inline constexpr char kFallbackNotificationTitle[] =
+    "Get 100 GB of cloud storage";
+inline constexpr char kFallbackNotificationMessage[] =
     "Keep your files & photos backed up with 12 months of Google One at no "
     "charge";
-constexpr char kFallbackGetPerkButtonTitle[] = "Claim now";
+inline constexpr char kFallbackGetPerkButtonTitle[] = "Claim now";
 
 inline constexpr char kIPHGoogleOneOfferNotificationId[] =
     "iph-google-one-offer-notification-id";
@@ -31,13 +32,13 @@
     "google_one_offer_iph_notification_get_perk";
 
 // IPH Feature Param names
-constexpr char kNotificationDisplaySourceParamName[] =
+inline constexpr char kNotificationDisplaySourceParamName[] =
     "x_google-one-offer-notification-display-source";
-constexpr char kNotificationTitleParamName[] =
+inline constexpr char kNotificationTitleParamName[] =
     "x_google-one-offer-notification-title";
-constexpr char kNotificationMessageParamName[] =
+inline constexpr char kNotificationMessageParamName[] =
     "x_google-one-offer-notification-message";
-constexpr char kGetPerkButtonTitleParamName[] =
+inline constexpr char kGetPerkButtonTitleParamName[] =
     "x_google-one-offer-get-perk-title";
 
 #endif  // CHROME_BROWSER_UI_ASH_GOOGLE_ONE_GOOGLE_ONE_OFFER_IPH_TAB_HELPER_CONSTANTS_H_
diff --git a/chrome/browser/ui/ash/input_method/candidate_window_constants.h b/chrome/browser/ui/ash/input_method/candidate_window_constants.h
index 689b7cf..1675759 100644
--- a/chrome/browser/ui/ash/input_method/candidate_window_constants.h
+++ b/chrome/browser/ui/ash/input_method/candidate_window_constants.h
@@ -13,7 +13,7 @@
 const int kFontSizeDelta = 2;
 
 // Currently the infolist window only supports Japanese font.
-const char kJapaneseFontName[] = "Noto Sans CJK JP";
+inline constexpr char kJapaneseFontName[] = "Noto Sans CJK JP";
 
 // The minimum width of candidate labels in the vertical candidate
 // window. We use this value to prevent the candidate window from being
diff --git a/chrome/browser/ui/ash/input_method/completion_suggestion_view.h b/chrome/browser/ui/ash/input_method/completion_suggestion_view.h
index 5979527..f73d56aa 100644
--- a/chrome/browser/ui/ash/input_method/completion_suggestion_view.h
+++ b/chrome/browser/ui/ash/input_method/completion_suggestion_view.h
@@ -27,7 +27,7 @@
 class CompletionSuggestionLabelView;
 
 // Font-related constants
-constexpr char kFontStyle[] = "Roboto";
+inline constexpr char kFontStyle[] = "Roboto";
 constexpr int kAnnotationFontSize = 10;
 constexpr int kIndexFontSize = 10;
 
@@ -38,7 +38,7 @@
 constexpr int kAnnotationPaddingLeft = 12;
 constexpr int kAnnotationPaddingBottom = 16;
 constexpr int kAnnotationPaddingTop = 6;
-constexpr char kTabKey[] = "tab";
+inline constexpr char kTabKey[] = "tab";
 constexpr cros_styles::ColorName kButtonHighlightColor =
     cros_styles::ColorName::kRippleColor;
 
diff --git a/chrome/browser/ui/ash/magic_boost/BUILD.gn b/chrome/browser/ui/ash/magic_boost/BUILD.gn
index a151970..9310fb9 100644
--- a/chrome/browser/ui/ash/magic_boost/BUILD.gn
+++ b/chrome/browser/ui/ash/magic_boost/BUILD.gn
@@ -19,6 +19,8 @@
   deps = [
     ":magic_boost_constants",
     "//base",
+    "//chrome/browser:browser_process",
+    "//chrome/browser:global_features",
     "//chrome/browser/ash/magic_boost",
     "//chrome/browser/resources:component_extension_resources",
     "//chrome/browser/ui/ash/editor_menu:utils",
@@ -28,6 +30,7 @@
     "//chromeos/crosapi/mojom",
     "//chromeos/strings:strings_grit",
     "//chromeos/ui/vector_icons",
+    "//components/application_locale_storage",
     "//ui/color",
     "//ui/display",
     "//ui/gfx",
diff --git a/chrome/browser/ui/ash/magic_boost/DEPS b/chrome/browser/ui/ash/magic_boost/DEPS
index 651e79c..a0d9abb1 100644
--- a/chrome/browser/ui/ash/magic_boost/DEPS
+++ b/chrome/browser/ui/ash/magic_boost/DEPS
@@ -6,7 +6,16 @@
 ]
 
 specific_include_rules = {
+  # TODO(crbug.com/416170323): Remove browser_process dependency.
+  "magic_boost_opt_in_card\\.cc": [
+    "+chrome/browser/browser_process.h",
+    "+chrome/browser/global_features.h",
+  ],
+
   "magic_boost_card_controller_unittest.cc": [
       "+ash/system/mahi",
   ],
+  ".*unittest\\.cc": [
+    "+chrome/browser/global_features.h",
+  ],
 }
diff --git a/chrome/browser/ui/ash/magic_boost/magic_boost_card_controller_unittest.cc b/chrome/browser/ui/ash/magic_boost/magic_boost_card_controller_unittest.cc
index 500313e8..ed7c6772 100644
--- a/chrome/browser/ui/ash/magic_boost/magic_boost_card_controller_unittest.cc
+++ b/chrome/browser/ui/ash/magic_boost/magic_boost_card_controller_unittest.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ui/ash/magic_boost/magic_boost_card_controller.h"
 
 #include <memory>
+#include <string>
 
 #include "ash/system/mahi/test/mock_mahi_media_app_events_proxy.h"
 #include "base/test/metrics/histogram_tester.h"
@@ -12,13 +13,16 @@
 #include "chrome/browser/ash/magic_boost/magic_boost_state_ash.h"
 #include "chrome/browser/ash/magic_boost/mock_editor_panel_manager.h"
 #include "chrome/browser/ash/magic_boost/mock_magic_boost_state.h"
+#include "chrome/browser/global_features.h"
 #include "chrome/browser/ui/ash/magic_boost/magic_boost_metrics.h"
 #include "chrome/browser/ui/ash/magic_boost/magic_boost_opt_in_card.h"
 #include "chrome/browser/ui/ash/magic_boost/test/mock_magic_boost_controller_crosapi.h"
+#include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/views/chrome_views_test_base.h"
 #include "chromeos/components/magic_boost/public/cpp/magic_boost_state.h"
 #include "chromeos/components/mahi/public/cpp/mahi_media_app_events_proxy.h"
 #include "chromeos/constants/chromeos_features.h"
+#include "components/application_locale_storage/application_locale_storage.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -27,6 +31,15 @@
 
 namespace chromeos {
 
+namespace {
+const std::string& GetApplicationLocale() {
+  return TestingBrowserProcess::GetGlobal()
+      ->GetFeatures()
+      ->application_locale_storage()
+      ->Get();
+}
+}  // namespace
+
 using OptInFeatures = crosapi::mojom::MagicBoostController::OptInFeatures;
 
 class MagicBoostCardControllerTest : public ChromeViewsTestBase {
@@ -117,17 +130,19 @@
   EXPECT_TRUE(widget);
 
   // Correct bounds should be set.
-  EXPECT_EQ(editor_menu::GetEditorMenuBounds(anchor_bounds,
-                                             widget->GetContentsView()),
-            widget->GetRestoredBounds());
+  EXPECT_EQ(
+      editor_menu::GetEditorMenuBounds(anchor_bounds, widget->GetContentsView(),
+                                       GetApplicationLocale()),
+      widget->GetRestoredBounds());
 
   anchor_bounds = gfx::Rect(0, 50, 55, 80);
 
   // Widget should change bounds accordingly.
   card_controller_.OnAnchorBoundsChanged(anchor_bounds);
-  EXPECT_EQ(editor_menu::GetEditorMenuBounds(anchor_bounds,
-                                             widget->GetContentsView()),
-            widget->GetRestoredBounds());
+  EXPECT_EQ(
+      editor_menu::GetEditorMenuBounds(anchor_bounds, widget->GetContentsView(),
+                                       GetApplicationLocale()),
+      widget->GetRestoredBounds());
 }
 
 TEST_F(MagicBoostCardControllerTest, DisclaimerUi) {
diff --git a/chrome/browser/ui/ash/magic_boost/magic_boost_opt_in_card.cc b/chrome/browser/ui/ash/magic_boost/magic_boost_opt_in_card.cc
index 46d9385..b7173ed4 100644
--- a/chrome/browser/ui/ash/magic_boost/magic_boost_opt_in_card.cc
+++ b/chrome/browser/ui/ash/magic_boost/magic_boost_opt_in_card.cc
@@ -6,6 +6,8 @@
 
 #include <string>
 
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/global_features.h"
 #include "chrome/browser/ui/ash/editor_menu/utils/utils.h"
 #include "chrome/browser/ui/ash/magic_boost/magic_boost_card_controller.h"
 #include "chrome/browser/ui/ash/magic_boost/magic_boost_constants.h"
@@ -14,6 +16,7 @@
 #include "chromeos/crosapi/mojom/magic_boost.mojom.h"
 #include "chromeos/strings/grit/chromeos_strings.h"
 #include "chromeos/ui/vector_icons/vector_icons.h"
+#include "components/application_locale_storage/application_locale_storage.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/compositor/layer.h"
@@ -251,9 +254,13 @@
 
 void MagicBoostOptInCard::UpdateWidgetBounds(
     const gfx::Rect& anchor_view_bounds) {
+  // TODO(crbug.com/416170323): Remove g_browser_process usage.
+  const std::string& app_locale =
+      g_browser_process->GetFeatures()->application_locale_storage()->Get();
+
   // TODO(b/318733414): Move `GetEditorMenuBounds` to a common place to use.
   GetWidget()->SetBounds(
-      editor_menu::GetEditorMenuBounds(anchor_view_bounds, this));
+      editor_menu::GetEditorMenuBounds(anchor_view_bounds, this, app_locale));
 }
 
 void MagicBoostOptInCard::RequestFocus() {
diff --git a/chrome/browser/ui/ash/read_write_cards/read_write_cards_manager_impl.cc b/chrome/browser/ui/ash/read_write_cards/read_write_cards_manager_impl.cc
index 1db02085..5a9d1d7 100644
--- a/chrome/browser/ui/ash/read_write_cards/read_write_cards_manager_impl.cc
+++ b/chrome/browser/ui/ash/read_write_cards/read_write_cards_manager_impl.cc
@@ -49,7 +49,8 @@
   if (chromeos::features::IsOrcaEnabled() ||
       ash::features::IsLobsterEnabled()) {
     editor_menu_controller_ =
-        std::make_unique<editor_menu::EditorMenuControllerImpl>();
+        std::make_unique<editor_menu::EditorMenuControllerImpl>(
+            application_locale_storage);
   }
 
   if (chromeos::features::IsMahiEnabled()) {
diff --git a/chrome/browser/ui/ash/wallpaper/wallpaper_controller_client_impl.cc b/chrome/browser/ui/ash/wallpaper/wallpaper_controller_client_impl.cc
index bf8ac9e2..06b8841 100644
--- a/chrome/browser/ui/ash/wallpaper/wallpaper_controller_client_impl.cc
+++ b/chrome/browser/ui/ash/wallpaper/wallpaper_controller_client_impl.cc
@@ -53,10 +53,10 @@
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/pref_names.h"
 #include "chromeos/ash/components/cryptohome/system_salt_getter.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
 #include "chromeos/ui/base/window_properties.h"
 #include "components/account_id/account_id.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "components/prefs/pref_service.h"
 #include "components/services/app_service/public/cpp/app_launch_util.h"
 #include "components/session_manager/core/session_manager.h"
diff --git a/chrome/browser/ui/autofill/payments/chrome_payments_autofill_client.cc b/chrome/browser/ui/autofill/payments/chrome_payments_autofill_client.cc
index c828360..941ff22 100644
--- a/chrome/browser/ui/autofill/payments/chrome_payments_autofill_client.cc
+++ b/chrome/browser/ui/autofill/payments/chrome_payments_autofill_client.cc
@@ -31,6 +31,7 @@
 #include "components/autofill/core/browser/data_model/payments/autofill_offer_data.h"
 #include "components/autofill/core/browser/data_model/payments/bnpl_issuer.h"
 #include "components/autofill/core/browser/data_model/payments/credit_card.h"
+#include "components/autofill/core/browser/data_model/valuables/loyalty_card.h"
 #include "components/autofill/core/browser/integrators/touch_to_fill/touch_to_fill_delegate.h"
 #include "components/autofill/core/browser/metrics/payments/risk_data_metrics.h"
 #include "components/autofill/core/browser/payments/autofill_error_dialog_context.h"
@@ -901,6 +902,19 @@
 #endif
 }
 
+bool ChromePaymentsAutofillClient::ShowTouchToFillLoyaltyCard(
+    base::WeakPtr<TouchToFillDelegate> delegate,
+    base::span<const autofill::LoyaltyCard> loyalty_cards_to_suggest) {
+#if BUILDFLAG(IS_ANDROID)
+  return touch_to_fill_payment_method_controller_.ShowLoyaltyCards(
+      std::make_unique<TouchToFillPaymentMethodViewImpl>(web_contents()),
+      delegate, std::move(loyalty_cards_to_suggest));
+#else
+  // Touch To Fill is not supported on Desktop.
+  NOTREACHED();
+#endif
+}
+
 void ChromePaymentsAutofillClient::HideTouchToFillPaymentMethod() {
 #if BUILDFLAG(IS_ANDROID)
   touch_to_fill_payment_method_controller_.Hide();
diff --git a/chrome/browser/ui/autofill/payments/chrome_payments_autofill_client.h b/chrome/browser/ui/autofill/payments/chrome_payments_autofill_client.h
index a0fb40f..fcc7ec3 100644
--- a/chrome/browser/ui/autofill/payments/chrome_payments_autofill_client.h
+++ b/chrome/browser/ui/autofill/payments/chrome_payments_autofill_client.h
@@ -199,6 +199,9 @@
   bool ShowTouchToFillIban(
       base::WeakPtr<TouchToFillDelegate> delegate,
       base::span<const autofill::Iban> ibans_to_suggest) override;
+  bool ShowTouchToFillLoyaltyCard(base::WeakPtr<TouchToFillDelegate> delegate,
+                                  base::span<const autofill::LoyaltyCard>
+                                      loyalty_cards_to_suggest) override;
   void HideTouchToFillPaymentMethod() override;
   std::unique_ptr<webauthn::InternalAuthenticator>
   CreateCreditCardInternalAuthenticator(AutofillDriver* driver) override;
diff --git a/chrome/browser/ui/blocked_content/tab_under_navigation_throttle.h b/chrome/browser/ui/blocked_content/tab_under_navigation_throttle.h
index 2c02b7e..c35c5387 100644
--- a/chrome/browser/ui/blocked_content/tab_under_navigation_throttle.h
+++ b/chrome/browser/ui/blocked_content/tab_under_navigation_throttle.h
@@ -14,7 +14,7 @@
 class NavigationHandle;
 }
 
-constexpr char kBlockTabUnderFormatMessage[] =
+inline constexpr char kBlockTabUnderFormatMessage[] =
     "Chrome stopped this site from navigating to %s, see "
     "https://www.chromestatus.com/feature/5675755719622656 for more details.";
 
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc
index 1dbef7a2..25216a4 100644
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -299,6 +299,13 @@
 #include "ui/ozone/public/platform_session_manager.h"
 #endif
 
+#if BUILDFLAG(ENABLE_GLIC)
+#include "chrome/browser/ai/ai_data_keyed_service.h"          // nogncheck
+#include "chrome/browser/ai/ai_data_keyed_service_factory.h"  // nogncheck
+#include "chrome/browser/glic/glic_enabling.h"
+#include "chrome/browser/glic/glic_keyed_service.h"
+#endif
+
 using base::UserMetricsAction;
 using content::NavigationController;
 using content::NavigationEntry;
@@ -451,6 +458,26 @@
                                               : &AlwaysReturnFalse;
 }
 
+bool IsActorCoordinatorActingOnTab(Profile* profile,
+                                   const content::WebContents* tab) {
+#if BUILDFLAG(ENABLE_GLIC)
+  if (glic::GlicEnabling::IsEnabledByFlags()) {
+    if (const auto* glic_service = glic::GlicKeyedService::Get(profile);
+        glic_service && glic_service->IsActorCoordinatorActingOnTab(tab)) {
+      return true;
+    }
+  }
+  // TODO(https://crbug.com/411462297): Deduplicate ownership of
+  // ActorCoordinators.
+  if (const auto* ai_data_service =
+          AiDataKeyedServiceFactory::GetAiDataKeyedService(profile);
+      ai_data_service && ai_data_service->IsActorCoordinatorActingOnTab(tab)) {
+    return true;
+  }
+#endif
+  return false;
+}
+
 }  // namespace
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -2330,10 +2357,18 @@
     const GURL& opener_url,
     const std::string& frame_name,
     const GURL& target_url) {
-  return window_container_type ==
-             content::mojom::WindowContainerType::BACKGROUND &&
-         ShouldCreateBackgroundContents(source_site_instance, opener_url,
-                                        frame_name);
+  if (IsActorCoordinatorActingOnTab(
+          profile(), content::WebContents::FromRenderFrameHost(opener))) {
+    // If an ActorCoordinator is acting on the opener, prevent it from creating
+    // a new WebContents. We'll instead force the navigation to happen in the
+    // same tab.
+    return true;
+  }
+
+  return (window_container_type ==
+              content::mojom::WindowContainerType::BACKGROUND &&
+          ShouldCreateBackgroundContents(source_site_instance, opener_url,
+                                         frame_name));
 }
 
 WebContents* Browser::CreateCustomWebContents(
@@ -2345,6 +2380,23 @@
     const GURL& target_url,
     const content::StoragePartitionConfig& partition_config,
     content::SessionStorageNamespace* session_storage_namespace) {
+  if (auto* opener_contents = content::WebContents::FromRenderFrameHost(opener);
+      IsActorCoordinatorActingOnTab(profile(), opener_contents)) {
+    // If an ActorCoordinator is acting on the opener, we force the navigation
+    // to happen in the same tab.
+    content::NavigationController::LoadURLParams params(target_url);
+    params.initiator_frame_token = opener->GetFrameToken();
+    params.initiator_process_id = opener->GetProcess()->GetDeprecatedID();
+    params.initiator_origin = opener->GetLastCommittedOrigin();
+    params.source_site_instance = source_site_instance;
+    params.transition_type = ui::PAGE_TRANSITION_LINK;
+    params.is_renderer_initiated = true;
+    opener_contents->GetController().LoadURLWithParams(params);
+    VLOG(1) << "Actor treated window open as same tab navigation. "
+            << target_url;
+    return nullptr;
+  }
+
   BackgroundContents* background_contents = CreateBackgroundContents(
       source_site_instance, opener, opener_url, is_new_browsing_instance,
       frame_name, target_url, partition_config, session_storage_namespace);
diff --git a/chrome/browser/ui/browser_window/browser_window_features.cc b/chrome/browser/ui/browser_window/browser_window_features.cc
index 2d59c0c..587bdd0 100644
--- a/chrome/browser/ui/browser_window/browser_window_features.cc
+++ b/chrome/browser/ui/browser_window/browser_window_features.cc
@@ -40,7 +40,6 @@
 #include "chrome/browser/ui/toolbar/chrome_labs/chrome_labs_utils.h"
 #include "chrome/browser/ui/toolbar/pinned_toolbar/tab_search_toolbar_button_controller.h"
 #include "chrome/browser/ui/ui_features.h"
-#include "chrome/browser/ui/views/data_sharing/data_sharing_open_group_helper.h"
 #include "chrome/browser/ui/views/download/bubble/download_toolbar_ui_controller.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/frame/tab_strip_region_view.h"
@@ -239,15 +238,6 @@
       base::FeatureList::IsEnabled(toast_features::kToastFramework)) {
     toast_service_ = std::make_unique<ToastService>(browser);
   }
-
-  collaboration::CollaborationService* service =
-      collaboration::CollaborationServiceFactory::GetForProfile(
-          browser->profile());
-  if (service && service->GetServiceStatus().IsAllowedToJoin() &&
-      tab_groups::IsTabGroupSyncServiceDesktopMigrationEnabled()) {
-    data_sharing_open_group_helper_ =
-        std::make_unique<DataSharingOpenGroupHelper>(browser);
-  }
 }
 
 void BrowserWindowFeatures::InitPostBrowserViewConstruction(
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 ad185c6..6fa6100 100644
--- a/chrome/browser/ui/browser_window/public/browser_window_features.h
+++ b/chrome/browser/ui/browser_window/public/browser_window_features.h
@@ -32,7 +32,6 @@
 class TranslateBubbleController;
 class ToastController;
 class ToastService;
-class DataSharingOpenGroupHelper;
 class DownloadToolbarUIController;
 
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
@@ -197,10 +196,6 @@
     return extension_side_panel_manager_.get();
   }
 
-  DataSharingOpenGroupHelper* data_sharing_open_group_helper() {
-    return data_sharing_open_group_helper_.get();
-  }
-
   DownloadToolbarUIController* download_toolbar_ui_controller() {
     return download_toolbar_ui_controller_.get();
   }
@@ -286,8 +281,6 @@
   std::unique_ptr<extensions::ExtensionSidePanelManager>
       extension_side_panel_manager_;
 
-  std::unique_ptr<DataSharingOpenGroupHelper> data_sharing_open_group_helper_;
-
   std::unique_ptr<media_router::CastBrowserController> cast_browser_controller_;
 
   std::unique_ptr<DownloadToolbarUIController> download_toolbar_ui_controller_;
diff --git a/chrome/browser/ui/color/BUILD.gn b/chrome/browser/ui/color/BUILD.gn
index 77c7485..29c3daf 100644
--- a/chrome/browser/ui/color/BUILD.gn
+++ b/chrome/browser/ui/color/BUILD.gn
@@ -125,7 +125,6 @@
     if (is_chromeos) {
       deps += [
         "//chrome/services/sharing/nearby",
-        "//components/exo/wayland:test_controller_stub",
         "//components/exo/wayland:ui_controls_protocol_stub",
       ]
     }
diff --git a/chrome/browser/ui/global_media_controls/media_item_ui_metrics.h b/chrome/browser/ui/global_media_controls/media_item_ui_metrics.h
index d0fa3da..76bafc3 100644
--- a/chrome/browser/ui/global_media_controls/media_item_ui_metrics.h
+++ b/chrome/browser/ui/global_media_controls/media_item_ui_metrics.h
@@ -10,11 +10,12 @@
 
 namespace {
 
-const char kStartCastingModeHistogramName[] =
+inline constexpr char kStartCastingModeHistogramName[] =
     "Media.GlobalMediaControls.MediaCastMode.Start";
-const char kStopCastingModeHistogramName[] =
+inline constexpr char kStopCastingModeHistogramName[] =
     "Media.GlobalMediaControls.MediaCastMode.Stop";
-const char kCastStartStopHistogramName[] = "Media.Notification.Cast.StartStop";
+inline constexpr char kCastStartStopHistogramName[] =
+    "Media.Notification.Cast.StartStop";
 
 // These values are persisted to logs. Entries should not be renumbered and
 // numeric values should never be reused.
diff --git a/chrome/browser/ui/lens/lens_overlay_controller_browsertest.cc b/chrome/browser/ui/lens/lens_overlay_controller_browsertest.cc
index 6c10e622..3885564 100644
--- a/chrome/browser/ui/lens/lens_overlay_controller_browsertest.cc
+++ b/chrome/browser/ui/lens/lens_overlay_controller_browsertest.cc
@@ -2,9 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/test/bind.h"
-#include "components/optimization_guide/content/browser/page_context_eligibility.h"
-#include "components/optimization_guide/content/browser/page_context_eligibility_api.h"
 #ifdef UNSAFE_BUFFERS_BUILD
 // TODO(crbug.com/390223051): Remove C-library calls to fix the errors.
 #pragma allow_unsafe_libc_calls
@@ -14,6 +11,8 @@
 // web browser, but allow for inspection and modification of internal state of
 // LensOverlayController and other business-logic classes.
 
+#include "chrome/browser/ui/lens/lens_overlay_controller.h"
+
 #include <memory>
 
 #include "base/base64url.h"
@@ -22,6 +21,7 @@
 #include "base/memory/raw_ptr_exclusion.h"
 #include "base/path_service.h"
 #include "base/strings/string_util.h"
+#include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/metrics/user_action_tester.h"
 #include "base/test/protobuf_matchers.h"
@@ -61,7 +61,6 @@
 #include "chrome/browser/ui/hats/hats_service_factory.h"
 #include "chrome/browser/ui/hats/mock_hats_service.h"
 #include "chrome/browser/ui/lens/lens_overlay_colors.h"
-#include "chrome/browser/ui/lens/lens_overlay_controller.h"
 #include "chrome/browser/ui/lens/lens_overlay_entry_point_controller.h"
 #include "chrome/browser/ui/lens/lens_overlay_gen204_controller.h"
 #include "chrome/browser/ui/lens/lens_overlay_query_controller.h"
@@ -86,6 +85,7 @@
 #include "chrome/browser/ui/views/side_panel/side_panel_coordinator.h"
 #include "chrome/browser/ui/views/side_panel/side_panel_entry_id.h"
 #include "chrome/browser/ui/views/side_panel/side_panel_util.h"
+#include "chrome/browser/ui/webui/feedback/feedback_dialog.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/extensions/api/pdf_viewer_private.h"
 #include "chrome/test/base/in_process_browser_test.h"
@@ -101,6 +101,8 @@
 #include "components/lens/lens_overlay_side_panel_menu_option.h"
 #include "components/lens/lens_overlay_side_panel_result.h"
 #include "components/lens/proto/server/lens_overlay_response.pb.h"
+#include "components/optimization_guide/content/browser/page_context_eligibility.h"
+#include "components/optimization_guide/content/browser/page_context_eligibility_api.h"
 #include "components/permissions/test/permission_request_observer.h"
 #include "components/prefs/pref_service.h"
 #include "components/sessions/content/session_tab_helper.h"
@@ -5171,6 +5173,45 @@
   EXPECT_EQ(0u, observer.dispatched_events().size());
 }
 
+IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest,
+                       FeedbackRequestedOpensFeedbackUI) {
+  WaitForPaint();
+
+  // State should start in off.
+  auto* controller = GetLensOverlayController();
+  ASSERT_EQ(controller->state(), State::kOff);
+
+  // Showing UI should change the state to screenshot and eventually to overlay.
+  OpenLensOverlay(LensOverlayInvocationSource::kAppMenu);
+  ASSERT_EQ(controller->state(), State::kScreenshot);
+  ASSERT_TRUE(base::test::RunUntil(
+      [&]() { return controller->state() == State::kOverlay; }));
+  ASSERT_TRUE(content::WaitForLoadStop(GetOverlayWebContents()));
+
+  // Open the side panel.
+  controller->OpenSidePanelForTesting();
+  ASSERT_TRUE(content::WaitForLoadStop(
+      controller->GetSidePanelWebContentsForTesting()));
+
+  // Get the coordinator.
+  auto* coordinator = controller->results_side_panel_coordinator();
+  ASSERT_TRUE(coordinator);
+
+  base::HistogramTester histogram_tester;
+
+  ASSERT_FALSE(FeedbackDialog::GetInstanceForTest());
+  coordinator->RequestSendFeedback();
+
+// ChromeOS opens its own feedback dialog.
+#if !BUILDFLAG(IS_CHROMEOS)
+  // Wait for the feedback dialog to appear instead of a new tab.
+  ASSERT_TRUE(base::test::RunUntil(
+      []() { return FeedbackDialog::GetInstanceForTest() != nullptr; }));
+#endif  // !BUILDFLAG(IS_CHROMEOS)
+
+  histogram_tester.ExpectTotalCount("Feedback.RequestSource", 1);
+}
+
 class LensOverlayControllerBrowserStartQueryFlowOptimization
     : public LensOverlayControllerBrowserTest {
  protected:
diff --git a/chrome/browser/ui/lens/lens_overlay_side_panel_coordinator.cc b/chrome/browser/ui/lens/lens_overlay_side_panel_coordinator.cc
index ae6cc5b..e8827807 100644
--- a/chrome/browser/ui/lens/lens_overlay_side_panel_coordinator.cc
+++ b/chrome/browser/ui/lens/lens_overlay_side_panel_coordinator.cc
@@ -409,6 +409,10 @@
   GetLensOverlayController()->GetIsContextualSearchbox(std::move(callback));
 }
 
+void LensOverlaySidePanelCoordinator::RequestSendFeedback() {
+  GetLensOverlayController()->FeedbackRequestedByEvent(ui::EF_NONE);
+}
+
 void LensOverlaySidePanelCoordinator::OnScrollToMessage(
     const std::vector<std::string>& text_fragments,
     uint32_t pdf_page_number) {
diff --git a/chrome/browser/ui/lens/lens_overlay_side_panel_coordinator.h b/chrome/browser/ui/lens/lens_overlay_side_panel_coordinator.h
index 4bb495c5..6dcb705f 100644
--- a/chrome/browser/ui/lens/lens_overlay_side_panel_coordinator.h
+++ b/chrome/browser/ui/lens/lens_overlay_side_panel_coordinator.h
@@ -152,7 +152,8 @@
   void GetIsContextualSearchbox(
       GetIsContextualSearchboxCallback callback) override;
   void OnScrollToMessage(const std::vector<std::string>& text_fragments,
-                         uint32_t pdf_page_number) override;
+      uint32_t pdf_page_number) override;
+  void RequestSendFeedback() override;
 
   // This method is used to set up communication between this instance and the
   // side panel WebUI. This is called by the WebUIController when the WebUI is
diff --git a/chrome/browser/ui/safety_hub/notification_permission_review_service.h b/chrome/browser/ui/safety_hub/notification_permission_review_service.h
index fb07a027..a027ee2 100644
--- a/chrome/browser/ui/safety_hub/notification_permission_review_service.h
+++ b/chrome/browser/ui/safety_hub/notification_permission_review_service.h
@@ -18,8 +18,9 @@
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/site_engagement/content/site_engagement_service.h"
 
-constexpr char kSafetyHubNotificationInfoString[] = "notificationInfoString";
-constexpr char kSafetyHubNotificationPermissionsResultKey[] =
+inline constexpr char kSafetyHubNotificationInfoString[] =
+    "notificationInfoString";
+inline constexpr char kSafetyHubNotificationPermissionsResultKey[] =
     "notificationPermissions";
 
 struct NotificationPermissions {
diff --git a/chrome/browser/ui/safety_hub/revoked_permissions_service.h b/chrome/browser/ui/safety_hub/revoked_permissions_service.h
index 63eba06..f563a05 100644
--- a/chrome/browser/ui/safety_hub/revoked_permissions_service.h
+++ b/chrome/browser/ui/safety_hub/revoked_permissions_service.h
@@ -36,7 +36,7 @@
 class PrefChangeRegistrar;
 class PrefService;
 
-constexpr char kRevokedPermissionsResultKey[] = "permissions";
+inline constexpr char kRevokedPermissionsResultKey[] = "permissions";
 
 namespace url {
 class Origin;
diff --git a/chrome/browser/ui/safety_hub/safety_hub_service.h b/chrome/browser/ui/safety_hub/safety_hub_service.h
index 64dd43a..caf19912 100644
--- a/chrome/browser/ui/safety_hub/safety_hub_service.h
+++ b/chrome/browser/ui/safety_hub/safety_hub_service.h
@@ -19,8 +19,8 @@
 #include "base/values.h"
 #include "components/keyed_service/core/keyed_service.h"
 
-constexpr char kSafetyHubTimestampResultKey[] = "timestamp";
-constexpr char kSafetyHubOriginKey[] = "origin";
+inline constexpr char kSafetyHubTimestampResultKey[] = "timestamp";
+inline constexpr char kSafetyHubOriginKey[] = "origin";
 
 // Base class for Safety Hub services. The background and UI tasks of the
 // derived classes will be executed periodically, according to the time delta
diff --git a/chrome/browser/ui/tabs/tab_strip_api/BUILD.gn b/chrome/browser/ui/tabs/tab_strip_api/BUILD.gn
index 6fcd7bd1..b346f7d 100644
--- a/chrome/browser/ui/tabs/tab_strip_api/BUILD.gn
+++ b/chrome/browser/ui/tabs/tab_strip_api/BUILD.gn
@@ -91,3 +91,22 @@
     "//testing/gtest",
   ]
 }
+
+source_set("browser_tests") {
+  testonly = true
+  defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
+  sources = [ "tab_strip_service_impl_browsertest.cc" ]
+
+  deps = [
+    ":impl",
+    ":tab_strip_api",
+    "//base",
+    "//base/test:test_support",
+    "//chrome/browser/ui:ui_features",
+    "//chrome/browser/ui/browser_window:browser_window",
+    "//chrome/browser/ui/tabs:tab_strip",
+    "//chrome/test:test_support",
+    "//content/test:test_support",
+    "//testing/gtest",
+  ]
+}
diff --git a/chrome/browser/ui/tabs/tab_strip_api/tab_strip_service_impl.cc b/chrome/browser/ui/tabs/tab_strip_api/tab_strip_service_impl.cc
index 646370d..d83db22 100644
--- a/chrome/browser/ui/tabs/tab_strip_api/tab_strip_service_impl.cc
+++ b/chrome/browser/ui/tabs/tab_strip_api/tab_strip_service_impl.cc
@@ -86,14 +86,13 @@
     return;
   }
 
-  std::vector<tabs_api::mojom::PositionPtr> positions;
-  for (const auto& content : insert_change.contents) {
-    auto pos = tabs_api::mojom::Position::New();
-    pos->index = content.index;
-    positions.emplace_back(std::move(pos));
-  }
-
   for (auto& observer : observers_) {
+    std::vector<tabs_api::mojom::PositionPtr> positions;
+    for (const auto& content : insert_change.contents) {
+      auto pos = tabs_api::mojom::Position::New();
+      pos->index = content.index;
+      positions.emplace_back(std::move(pos));
+    }
     observer.OnTabsCreated(std::move(positions));
   }
 }
diff --git a/chrome/browser/ui/tabs/tab_strip_api/tab_strip_service_impl_browsertest.cc b/chrome/browser/ui/tabs/tab_strip_api/tab_strip_service_impl_browsertest.cc
new file mode 100644
index 0000000..d3c34e3
--- /dev/null
+++ b/chrome/browser/ui/tabs/tab_strip_api/tab_strip_service_impl_browsertest.cc
@@ -0,0 +1,138 @@
+// 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/tabs/tab_strip_api/tab_strip_service_impl.h"
+
+#include "base/logging.h"
+#include "base/run_loop.h"
+#include "base/test/bind.h"
+#include "base/test/scoped_feature_list.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/tabs/tab_strip_api/tab_strip_api.mojom.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/ui_features.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "content/public/test/browser_test.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "mojo/public/mojom/base/error.mojom.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+using testing::_;
+
+class MockTabsObserver : public tabs_api::mojom::TabsObserver {
+ public:
+  MockTabsObserver() = default;
+  ~MockTabsObserver() override = default;
+
+  MOCK_METHOD(void,
+              OnTabsCreated,
+              (std::vector<tabs_api::mojom::PositionPtr> positions),
+              (override));
+};
+
+class TabStripServiceImplBrowserTest : public InProcessBrowserTest {
+ public:
+  TabStripServiceImplBrowserTest() {
+    feature_list_.InitAndEnableFeature(features::kTabStripBrowserApi);
+  }
+
+  void SetUpOnMainThread() override {
+    InProcessBrowserTest::SetUpOnMainThread();
+    tab_strip_service_impl_ = std::make_unique<TabStripServiceImpl>(
+        browser(), browser()->tab_strip_model());
+  }
+
+  void TearDownOnMainThread() override {
+    tab_strip_service_impl_.reset();
+    InProcessBrowserTest::TearDownOnMainThread();
+  }
+
+  void CreateTabAtApiCallback(
+      base::RunLoop* run_loop,
+      base::expected<bool, mojo_base::mojom::ErrorPtr>* out_result,
+      base::expected<bool, mojo_base::mojom::ErrorPtr> result) {
+    *out_result = std::move(result);
+    if (run_loop) {
+      run_loop->Quit();
+    }
+  }
+
+ protected:
+  TabStripModel* GetTabStripModel() { return browser()->tab_strip_model(); }
+
+  tabs_api::mojom::PositionPtr CreatePosition(int index) {
+    auto position = tabs_api::mojom::Position::New();
+    position->index = index;
+    return position;
+  }
+
+  base::test::ScopedFeatureList feature_list_;
+  std::unique_ptr<TabStripServiceImpl> tab_strip_service_impl_;
+};
+
+IN_PROC_BROWSER_TEST_F(TabStripServiceImplBrowserTest, CreateTabAt) {
+  TabStripModel* model = GetTabStripModel();
+  const int expected_tab_count = model->count() + 1;
+  const GURL url("http://example.com/");
+
+  base::expected<bool, mojo_base::mojom::ErrorPtr> result;
+  base::RunLoop run_loop;
+  tabs_api::mojom::PositionPtr position = CreatePosition(0);
+
+  tab_strip_service_impl_->CreateTabAt(
+      std::move(position), std::make_optional(url),
+      base::BindOnce(&TabStripServiceImplBrowserTest::CreateTabAtApiCallback,
+                     base::Unretained(this), &run_loop, &result));
+  run_loop.Run();
+
+  ASSERT_TRUE(result.has_value())
+      << "CreateTabAt failed: " << (result.error()->message);
+  EXPECT_TRUE(result.value());
+  EXPECT_EQ(model->count(), expected_tab_count);
+}
+
+IN_PROC_BROWSER_TEST_F(TabStripServiceImplBrowserTest, ObserverOnTabsCreated) {
+  MockTabsObserver mock_observer;
+  tab_strip_service_impl_->AddObserver(&mock_observer);
+  const GURL url("http://example.com/");
+  uint32_t target_index = 0;
+
+  EXPECT_CALL(
+      mock_observer,
+      OnTabsCreated(testing::Truly(
+          [&target_index](
+              const std::vector<tabs_api::mojom::PositionPtr>& positions) {
+            if (positions.size() != 1) {
+              return false;
+            }
+            if (!positions[0]) {
+              return false;
+            }
+            return positions[0]->index == target_index;
+          })))
+      .Times(1);
+
+  base::expected<bool, mojo_base::mojom::ErrorPtr> result;
+  base::RunLoop run_loop;
+  tabs_api::mojom::PositionPtr position = CreatePosition(target_index);
+
+  tab_strip_service_impl_->CreateTabAt(
+      std::move(position), std::make_optional(url),
+      base::BindOnce(&TabStripServiceImplBrowserTest::CreateTabAtApiCallback,
+                     base::Unretained(this), &run_loop, &result));
+  run_loop.Run();
+
+  ASSERT_TRUE(result.has_value())
+      << "CreateTabAt failed: " << (result.error()->message);
+  EXPECT_TRUE(result.value());
+
+  tab_strip_service_impl_->RemoveObserver(&mock_observer);
+}
diff --git a/chrome/browser/ui/toasts/toast_view.cc b/chrome/browser/ui/toasts/toast_view.cc
index e94aadd..4c559e3 100644
--- a/chrome/browser/ui/toasts/toast_view.cc
+++ b/chrome/browser/ui/toasts/toast_view.cc
@@ -210,6 +210,7 @@
     action_button_->GetViewAccessibility().SetRole(ax::mojom::Role::kAlert);
     action_button_->SetProperty(views::kElementIdentifierKey,
                                 kToastActionButton);
+    action_button_->SetAppearDisabledInInactiveWidget(false);
     action_button_->SetProperty(
         views::kMarginsKey,
         GetLeftMargin(lp->GetDistanceMetric(
diff --git a/chrome/browser/ui/translate/partial_translate_bubble_ui_action_logger.h b/chrome/browser/ui/translate/partial_translate_bubble_ui_action_logger.h
index a41f262..d6828fc 100644
--- a/chrome/browser/ui/translate/partial_translate_bubble_ui_action_logger.h
+++ b/chrome/browser/ui/translate/partial_translate_bubble_ui_action_logger.h
@@ -9,7 +9,7 @@
 
 // Histogram for recording the UI events related to the Partial Translate
 // bubble.
-constexpr char kPartialTranslateBubbleUiEventHistogramName[] =
+inline constexpr char kPartialTranslateBubbleUiEventHistogramName[] =
     "Translate.PartialTranslateBubbleUiEvent";
 
 enum class PartialTranslateBubbleUiEvent {
diff --git a/chrome/browser/ui/views/chrome_views_export.h b/chrome/browser/ui/views/chrome_views_export.h
index c6b92e8f8..e5bd1c1 100644
--- a/chrome/browser/ui/views/chrome_views_export.h
+++ b/chrome/browser/ui/views/chrome_views_export.h
@@ -19,11 +19,7 @@
 
 #else  // !defined(WIN32)
 
-#if defined(CHROME_VIEWS_IMPLEMENTATION)
 #define CHROME_VIEWS_EXPORT __attribute__((visibility("default")))
-#else
-#define CHROME_VIEWS_EXPORT
-#endif
 
 #endif  // defined(WIN32)
 
diff --git a/chrome/browser/ui/views/data_sharing/collaboration_controller_delegate_desktop.cc b/chrome/browser/ui/views/data_sharing/collaboration_controller_delegate_desktop.cc
index 2d99db4..2c037d4 100644
--- a/chrome/browser/ui/views/data_sharing/collaboration_controller_delegate_desktop.cc
+++ b/chrome/browser/ui/views/data_sharing/collaboration_controller_delegate_desktop.cc
@@ -321,9 +321,14 @@
     std::optional<data_sharing::mojom::GroupActionProgress> progress) {
   // Joins flow should end when the shared tab group is open after join
   // or cancel without joining.
-  // TODO(crbug.org/380287432): Only cancel the flow if user doesn't join the
-  // group.
-  std::move(result).Run(CollaborationControllerDelegate::Outcome::kCancel);
+  CollaborationControllerDelegate::Outcome outcome =
+      CollaborationControllerDelegate::Outcome::kCancel;
+  if (action == data_sharing::mojom::GroupAction::kJoinGroup &&
+      progress == data_sharing::mojom::GroupActionProgress::kSuccess) {
+    outcome = CollaborationControllerDelegate::Outcome::kSuccess;
+  }
+
+  std::move(result).Run(outcome);
 }
 
 void CollaborationControllerDelegateDesktop::OnManageDialogClosing(
diff --git a/chrome/browser/ui/views/data_sharing/data_sharing_chrome_native_interactive_uitest.cc b/chrome/browser/ui/views/data_sharing/data_sharing_chrome_native_interactive_uitest.cc
index 3722601f..f0049b0 100644
--- a/chrome/browser/ui/views/data_sharing/data_sharing_chrome_native_interactive_uitest.cc
+++ b/chrome/browser/ui/views/data_sharing/data_sharing_chrome_native_interactive_uitest.cc
@@ -23,7 +23,6 @@
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/views/bubble/webui_bubble_dialog_view.h"
 #include "chrome/browser/ui/views/data_sharing/data_sharing_bubble_controller.h"
-#include "chrome/browser/ui/views/data_sharing/data_sharing_open_group_helper.h"
 #include "chrome/browser/ui/views/data_sharing/data_sharing_utils.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/tabs/tab_group_header.h"
@@ -300,41 +299,4 @@
   EXPECT_EQ(url.value().spec(), expected_close_flow_url_with_token);
 }
 
-IN_PROC_BROWSER_TEST_F(DataSharingChromeNativeUiTest, OpenGroupHelper) {
-  std::string fake_collab_id = "fake_collab_id";
-  tab_groups::LocalTabGroupID local_group_id = InstrumentATabGroup();
-  auto* tab_group_service =
-      tab_groups::SavedTabGroupUtils::GetServiceForProfile(
-          browser()->profile());
-  std::optional<tab_groups::SavedTabGroup> group_copy =
-      tab_group_service->GetGroup(local_group_id);
-
-  // Mock up a shared group.
-  group_copy->SetCollaborationId(tab_groups::CollaborationId(fake_collab_id));
-
-  RunTestSequence(
-      SaveGroupLeaveEditorBubbleOpen(local_group_id),
-      WaitForShow(kTabGroupHeaderElementId),
-      EnsurePresent(kTabGroupEditorBubbleId),
-      PressButton(kTabGroupEditorBubbleCloseGroupButtonId),
-      FinishTabstripAnimations(), WaitForHide(kTabGroupHeaderElementId),
-      Do([=, this]() {
-        DataSharingOpenGroupHelper* open_group_helper =
-            browser()
-                ->browser_window_features()
-                ->data_sharing_open_group_helper();
-        open_group_helper->OpenTabGroupWhenAvailable(fake_collab_id);
-        EXPECT_TRUE(open_group_helper->group_ids_for_testing().contains(
-            fake_collab_id));
-
-        // Mock group sync from remote.
-        open_group_helper->OnTabGroupAdded(group_copy.value(),
-                                           tab_groups::TriggerSource::REMOTE);
-        EXPECT_FALSE(open_group_helper->group_ids_for_testing().contains(
-            fake_collab_id));
-      }),
-      // The group is opened into the tab strip.
-      WaitForShow(kTabGroupHeaderElementId));
-}
-
 }  // namespace tab_groups
diff --git a/chrome/browser/ui/views/data_sharing/data_sharing_open_group_helper.cc b/chrome/browser/ui/views/data_sharing/data_sharing_open_group_helper.cc
deleted file mode 100644
index 137b4b22..0000000
--- a/chrome/browser/ui/views/data_sharing/data_sharing_open_group_helper.cc
+++ /dev/null
@@ -1,66 +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/ui/views/data_sharing/data_sharing_open_group_helper.h"
-
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_utils.h"
-#include "chrome/browser/ui/tabs/saved_tab_groups/tab_group_action_context_desktop.h"
-
-DataSharingOpenGroupHelper::DataSharingOpenGroupHelper(Browser* browser)
-    : browser_(browser) {
-  tab_group_service_ =
-      tab_groups::SavedTabGroupUtils::GetServiceForProfile(browser_->profile());
-  tab_group_sync_service_observation_.Observe(tab_group_service_);
-}
-
-DataSharingOpenGroupHelper::~DataSharingOpenGroupHelper() = default;
-
-void DataSharingOpenGroupHelper::OpenTabGroupWhenAvailable(
-    std::string group_id) {
-  if (!OpenTabGroupIfSynced(group_id)) {
-    group_ids_.insert(group_id);
-  }
-}
-
-void DataSharingOpenGroupHelper::OnTabGroupAdded(
-    const tab_groups::SavedTabGroup& group,
-    tab_groups::TriggerSource source) {
-  if (!group.collaboration_id().has_value()) {
-    return;
-  }
-
-  tab_groups::CollaborationId collab_id = group.collaboration_id().value();
-  if (source == tab_groups::TriggerSource::REMOTE &&
-      std::find(group_ids_.begin(), group_ids_.end(), collab_id.value()) !=
-          group_ids_.end()) {
-    group_ids_.erase(collab_id.value());
-    tab_group_service_->OpenTabGroup(
-        group.saved_guid(),
-        std::make_unique<tab_groups::TabGroupActionContextDesktop>(
-            browser_, tab_groups::OpeningSource::kAutoOpenedFromSync));
-  }
-}
-
-bool DataSharingOpenGroupHelper::OpenTabGroupIfSynced(std::string group_id) {
-  std::vector<tab_groups::SavedTabGroup> all =
-      tab_group_service_->GetAllGroups();
-
-  auto exist = [=](tab_groups::SavedTabGroup group) {
-    std::optional<tab_groups::CollaborationId> collab_id =
-        group.collaboration_id();
-    return collab_id && collab_id->value() == group_id;
-  };
-
-  if (auto it = std::find_if(begin(all), end(all), exist);
-      it != std::end(all)) {
-    tab_group_service_->OpenTabGroup(
-        it->saved_guid(),
-        std::make_unique<tab_groups::TabGroupActionContextDesktop>(
-            browser_, tab_groups::OpeningSource::kAutoOpenedFromSync));
-    return true;
-  } else {
-    return false;
-  }
-}
diff --git a/chrome/browser/ui/views/data_sharing/data_sharing_open_group_helper.h b/chrome/browser/ui/views/data_sharing/data_sharing_open_group_helper.h
deleted file mode 100644
index 9ec0ce00..0000000
--- a/chrome/browser/ui/views/data_sharing/data_sharing_open_group_helper.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 CHROME_BROWSER_UI_VIEWS_DATA_SHARING_DATA_SHARING_OPEN_GROUP_HELPER_H_
-#define CHROME_BROWSER_UI_VIEWS_DATA_SHARING_DATA_SHARING_OPEN_GROUP_HELPER_H_
-
-#include <set>
-
-#include "base/scoped_observation.h"
-#include "components/saved_tab_groups/public/tab_group_sync_service.h"
-#include "components/saved_tab_groups/public/types.h"
-
-class Browser;
-
-// A helper that opens the shared tab group into browser tab strip. When a user
-// tried to join a shared tab group via the share link, opens the group into
-// browser direcetly if they are already in the group or wait for the group to
-// be synced from remote after they press "Open and Join" on the Join dialog.
-class DataSharingOpenGroupHelper
-    : public tab_groups::TabGroupSyncService::Observer {
- public:
-  explicit DataSharingOpenGroupHelper(Browser* browser);
-  DataSharingOpenGroupHelper(const DataSharingOpenGroupHelper&) = delete;
-  DataSharingOpenGroupHelper& operator=(const DataSharingOpenGroupHelper&) =
-      delete;
-  ~DataSharingOpenGroupHelper() override;
-
-  // TabGroupSyncService::Observer override.
-  // Compare `group_ids` against the collaboration_ids synced from remote. If
-  // they match (meaning the syncs are triggered by the user pressing "Join and
-  // Open" on the Join dialog) open the tab groups into current window.
-  void OnTabGroupAdded(const tab_groups::SavedTabGroup& group,
-                       tab_groups::TriggerSource source) override;
-
-  // If group is synced open it otherwise store `group_id` and wait to be synced
-  // from remote. Called after users press "Open and Join" from the Join dialog.
-  void OpenTabGroupWhenAvailable(std::string group_id);
-
-  // Open the shared tab group in current window (focus if open) if the group
-  // the user tries to join from share link is already synced to the local
-  // client (namely the user is already in the group). Return true if it is the
-  // case.
-  bool OpenTabGroupIfSynced(std::string group_id);
-
-  std::set<std::string> group_ids_for_testing() const { return group_ids_; }
-
- private:
-  raw_ptr<Browser> browser_;
-  raw_ptr<tab_groups::TabGroupSyncService> tab_group_service_ = nullptr;
-
-  base::ScopedObservation<tab_groups::TabGroupSyncService,
-                          tab_groups::TabGroupSyncService::Observer>
-      tab_group_sync_service_observation_{this};
-
-  // The group_ids passed from the Join webui. They're the same as
-  // SavedTabGroup::collaboration_id for shared tab groups.
-  std::set<std::string> group_ids_;
-};
-
-#endif  // CHROME_BROWSER_UI_VIEWS_DATA_SHARING_DATA_SHARING_OPEN_GROUP_HELPER_H_
diff --git a/chrome/browser/ui/views/device_signals_consent/consent_dialog_browsertest.cc b/chrome/browser/ui/views/device_signals_consent/consent_dialog_browsertest.cc
index 8ad5cc5..3251fb75 100644
--- a/chrome/browser/ui/views/device_signals_consent/consent_dialog_browsertest.cc
+++ b/chrome/browser/ui/views/device_signals_consent/consent_dialog_browsertest.cc
@@ -68,7 +68,13 @@
   testing::NiceMock<policy::MockConfigurationPolicyProvider> provider_;
 };
 
-IN_PROC_BROWSER_TEST_F(ConsentDialogUiTest, GetConsentDialogBodyTest) {
+// TODO(crbug.com/416157468): Enable the test.
+#if BUILDFLAG(IS_WIN)
+#define MAYBE_GetConsentDialogBodyTest DISABLED_GetConsentDialogBodyTest
+#else
+#define MAYBE_GetConsentDialogBodyTest GetConsentDialogBodyTest
+#endif
+IN_PROC_BROWSER_TEST_F(ConsentDialogUiTest, MAYBE_GetConsentDialogBodyTest) {
   // Simulate a managed profile.
   AddEnterpriseManagedPolicies();
   policy::ScopedManagementServiceOverrideForTesting browser_management(
diff --git a/chrome/browser/ui/views/global_media_controls/media_item_ui_device_selector_view.h b/chrome/browser/ui/views/global_media_controls/media_item_ui_device_selector_view.h
index fb3f826..b6862ad9 100644
--- a/chrome/browser/ui/views/global_media_controls/media_item_ui_device_selector_view.h
+++ b/chrome/browser/ui/views/global_media_controls/media_item_ui_device_selector_view.h
@@ -26,13 +26,13 @@
 class ExpandDeviceSelectorLabel;
 class ExpandDeviceSelectorButton;
 
-const char kAudioDevicesCountHistogramName[] =
+inline constexpr char kAudioDevicesCountHistogramName[] =
     "Media.GlobalMediaControls.NumberOfAvailableAudioDevices";
-const char kCastDeviceCountHistogramName[] =
+inline constexpr char kCastDeviceCountHistogramName[] =
     "Media.GlobalMediaControls.CastDeviceCount";
-const char kDeviceSelectorAvailableHistogramName[] =
+inline constexpr char kDeviceSelectorAvailableHistogramName[] =
     "Media.GlobalMediaControls.DeviceSelectorAvailable";
-const char kDeviceSelectorOpenedHistogramName[] =
+inline constexpr char kDeviceSelectorOpenedHistogramName[] =
     "Media.GlobalMediaControls.DeviceSelectorOpened";
 }  // anonymous namespace
 
diff --git a/chrome/browser/ui/views/mahi/BUILD.gn b/chrome/browser/ui/views/mahi/BUILD.gn
index a739b338..642f472c 100644
--- a/chrome/browser/ui/views/mahi/BUILD.gn
+++ b/chrome/browser/ui/views/mahi/BUILD.gn
@@ -19,6 +19,8 @@
 
   deps = [
     "//base",
+    "//chrome/browser:browser_process",
+    "//chrome/browser:global_features",
     "//chrome/browser/ui/ash/editor_menu:utils",
     "//chrome/browser/ui/ash/magic_boost",
     "//chrome/browser/ui/ash/magic_boost:magic_boost_constants",
@@ -29,6 +31,7 @@
     "//chromeos/constants:constants",
     "//chromeos/strings:strings_grit",
     "//chromeos/ui/vector_icons",
+    "//components/application_locale_storage",
     "//ui/accessibility:ax_base",
     "//ui/color",
     "//ui/display",
diff --git a/chrome/browser/ui/views/mahi/DEPS b/chrome/browser/ui/views/mahi/DEPS
index 8e2c4cf..27eaf3c2 100644
--- a/chrome/browser/ui/views/mahi/DEPS
+++ b/chrome/browser/ui/views/mahi/DEPS
@@ -18,3 +18,15 @@
   "+chrome/test",
 
 ]
+
+specific_include_rules = {
+  # TODO(crbug.com/416170122): Remove browser_process dependency.
+  "mahi_menu_view\\.cc": [
+    "+chrome/browser/browser_process.h",
+    "+chrome/browser/global_features.h",
+  ],
+
+  ".*unittest\\.cc": [
+    "+chrome/browser/global_features.h",
+  ],
+}
diff --git a/chrome/browser/ui/views/mahi/mahi_menu_controller_unittest.cc b/chrome/browser/ui/views/mahi/mahi_menu_controller_unittest.cc
index 03eddfe..3ea6abb 100644
--- a/chrome/browser/ui/views/mahi/mahi_menu_controller_unittest.cc
+++ b/chrome/browser/ui/views/mahi/mahi_menu_controller_unittest.cc
@@ -16,15 +16,18 @@
 #include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
 #include "chrome/browser/ash/mahi/web_contents/test_support/fake_mahi_web_contents_manager.h"
+#include "chrome/browser/global_features.h"
 #include "chrome/browser/ui/ash/editor_menu/utils/utils.h"
 #include "chrome/browser/ui/ash/read_write_cards/read_write_cards_ui_controller.h"
 #include "chrome/browser/ui/views/mahi/mahi_condensed_menu_view.h"
 #include "chrome/browser/ui/views/mahi/mahi_menu_constants.h"
 #include "chrome/browser/ui/views/mahi/mahi_menu_view.h"
+#include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/views/chrome_views_test_base.h"
 #include "chromeos/components/mahi/public/cpp/mahi_media_app_events_proxy.h"
 #include "chromeos/components/mahi/public/cpp/mahi_web_contents_manager.h"
 #include "chromeos/constants/chromeos_features.h"
+#include "components/application_locale_storage/application_locale_storage.h"
 #include "testing/gmock/include/gmock/gmock-matchers.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -33,6 +36,15 @@
 
 namespace chromeos::mahi {
 
+namespace {
+const std::string& GetApplicationLocale() {
+  return TestingBrowserProcess::GetGlobal()
+      ->GetFeatures()
+      ->application_locale_storage()
+      ->Get();
+}
+}  // namespace
+
 using ::testing::IsNull;
 using ::testing::Mock;
 using ::testing::NiceMock;
@@ -146,6 +158,7 @@
 
   EXPECT_EQ(
       editor_menu::GetEditorMenuBounds(anchor_bounds, widget->GetContentsView(),
+                                       GetApplicationLocale(),
                                        editor_menu::CardType::kMahiDefaultMenu),
       widget->GetRestoredBounds());
 
@@ -155,6 +168,7 @@
   menu_controller()->OnAnchorBoundsChanged(anchor_bounds);
   EXPECT_EQ(
       editor_menu::GetEditorMenuBounds(anchor_bounds, widget->GetContentsView(),
+                                       GetApplicationLocale(),
                                        editor_menu::CardType::kMahiDefaultMenu),
       widget->GetRestoredBounds());
 }
diff --git a/chrome/browser/ui/views/mahi/mahi_menu_view.cc b/chrome/browser/ui/views/mahi/mahi_menu_view.cc
index be38770..0beb5f8 100644
--- a/chrome/browser/ui/views/mahi/mahi_menu_view.cc
+++ b/chrome/browser/ui/views/mahi/mahi_menu_view.cc
@@ -13,6 +13,8 @@
 #include "base/functional/callback_helpers.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/notreached.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/global_features.h"
 #include "chrome/browser/ui/ash/editor_menu/utils/pre_target_handler.h"
 #include "chrome/browser/ui/ash/editor_menu/utils/pre_target_handler_view.h"
 #include "chrome/browser/ui/ash/editor_menu/utils/utils.h"
@@ -26,6 +28,7 @@
 #include "chromeos/components/mahi/public/cpp/mahi_web_contents_manager.h"
 #include "chromeos/strings/grit/chromeos_strings.h"
 #include "chromeos/ui/vector_icons/vector_icons.h"
+#include "components/application_locale_storage/application_locale_storage.h"
 #include "components/vector_icons/vector_icons.h"
 #include "ui/accessibility/ax_enums.mojom-shared.h"
 #include "ui/accessibility/ax_node_data.h"
@@ -375,10 +378,15 @@
 }
 
 void MahiMenuView::UpdateBounds(const gfx::Rect& anchor_view_bounds) {
+  // TODO(crbug.com/416170122): Remove g_browser_process usage.
+  const std::string& app_locale =
+      g_browser_process->GetFeatures()->application_locale_storage()->Get();
+
   // TODO(b/318733414): Move `editor_menu::GetEditorMenuBounds` to a common
   // place for use
   GetWidget()->SetBounds(editor_menu::GetEditorMenuBounds(
-      anchor_view_bounds, this, editor_menu::CardType::kMahiDefaultMenu));
+      anchor_view_bounds, this, app_locale,
+      editor_menu::CardType::kMahiDefaultMenu));
 }
 
 void MahiMenuView::OnWidgetVisibilityChanged(views::Widget* widget,
diff --git a/chrome/browser/ui/views/mahi/mahi_menu_view_unittest.cc b/chrome/browser/ui/views/mahi/mahi_menu_view_unittest.cc
index 976d7c6..5a428ba 100644
--- a/chrome/browser/ui/views/mahi/mahi_menu_view_unittest.cc
+++ b/chrome/browser/ui/views/mahi/mahi_menu_view_unittest.cc
@@ -11,13 +11,16 @@
 #include "base/run_loop.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "chrome/browser/ash/mahi/web_contents/test_support/fake_mahi_web_contents_manager.h"
+#include "chrome/browser/global_features.h"
 #include "chrome/browser/ui/ash/editor_menu/utils/utils.h"
 #include "chrome/browser/ui/views/mahi/mahi_menu_constants.h"
+#include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/views/chrome_views_test_base.h"
 #include "chromeos/components/mahi/public/cpp/mahi_browser_util.h"
 #include "chromeos/components/mahi/public/cpp/mahi_util.h"
 #include "chromeos/components/mahi/public/cpp/mahi_web_contents_manager.h"
 #include "chromeos/strings/grit/chromeos_strings.h"
+#include "components/application_locale_storage/application_locale_storage.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -84,6 +87,13 @@
   }
 }
 
+const std::string& GetApplicationLocale() {
+  return TestingBrowserProcess::GetGlobal()
+      ->GetFeatures()
+      ->application_locale_storage()
+      ->Get();
+}
+
 }  // namespace
 
 TEST_F(MahiMenuViewTest, Bounds) {
@@ -94,7 +104,8 @@
   // The bounds of the created widget should be similar to the value from the
   // utils function.
   EXPECT_EQ(editor_menu::GetEditorMenuBounds(
-                anchor_view_bounds, menu_widget.get()->GetContentsView()),
+                anchor_view_bounds, menu_widget.get()->GetContentsView(),
+                GetApplicationLocale()),
             menu_widget->GetRestoredBounds());
 }
 
diff --git a/chrome/browser/ui/views/profiles/profile_menu_view.cc b/chrome/browser/ui/views/profiles/profile_menu_view.cc
index 02e209c..fab42a3 100644
--- a/chrome/browser/ui/views/profiles/profile_menu_view.cc
+++ b/chrome/browser/ui/views/profiles/profile_menu_view.cc
@@ -146,6 +146,58 @@
   }
 }
 
+std::u16string GetSyncPromoDescription(signin_metrics::AccessPoint access_point,
+                                       std::string_view email) {
+  switch (access_point) {
+    case signin_metrics::AccessPoint::kHistorySyncOptinExpansionPillOnStartup:
+    case signin_metrics::AccessPoint::
+        kHistorySyncOptinExpansionPillOnInactivity:
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+      if (base::FeatureList::IsEnabled(
+              switches::kEnableHistorySyncOptinExpansionPill)) {
+        switch (switches::kHistorySyncOptinExpansionPillOption.Get()) {
+          case switches::HistorySyncOptinExpansionPillOption::
+              kBrowseAcrossDevices:
+            return l10n_util::GetStringFUTF16(
+                IDS_PROFILE_MENU_SYNC_PROMO_BROWSE_ACROSS_DEVICES_DESCRIPTION,
+                base::UTF8ToUTF16(email));
+          case switches::HistorySyncOptinExpansionPillOption::kSyncHistory:
+            return l10n_util::GetStringFUTF16(
+                IDS_PROFILE_MENU_SYNC_PROMO_SYNC_HISTORY_DESCRIPTION,
+                base::UTF8ToUTF16(email));
+          case switches::HistorySyncOptinExpansionPillOption::
+              kSeeTabsFromOtherDevices:
+            return l10n_util::GetStringFUTF16(
+                IDS_PROFILE_MENU_SYNC_PROMO_SEE_TABS_FROM_OTHER_DEVICES_DESCRIPTION,
+                base::UTF8ToUTF16(email));
+        }
+      }
+#endif
+      [[fallthrough]];
+    default:
+      return l10n_util::GetStringUTF16(IDS_PROFILES_DICE_SYNC_PROMO);
+  }
+}
+
+std::u16string GetSyncPromoButtonLabel(
+    signin_metrics::AccessPoint access_point) {
+  switch (access_point) {
+    case signin_metrics::AccessPoint::kHistorySyncOptinExpansionPillOnStartup:
+    case signin_metrics::AccessPoint::
+        kHistorySyncOptinExpansionPillOnInactivity:
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+      if (base::FeatureList::IsEnabled(
+              switches::kEnableHistorySyncOptinExpansionPill)) {
+        return l10n_util::GetStringUTF16(
+            IDS_PROFILE_MENU_SYNC_PROMO_BUTTON_LABEL);
+      }
+#endif
+      [[fallthrough]];
+    default:
+      return l10n_util::GetStringUTF16(IDS_PROFILES_DICE_SIGNIN_BUTTON);
+  }
+}
+
 }  // namespace
 
 // static
@@ -688,9 +740,11 @@
       break;
     }
     case signin_util::SignedInState::kSignedIn:
-      params.subtitle = l10n_util::GetStringUTF16(IDS_PROFILES_DICE_SYNC_PROMO);
-      params.button_text =
-          l10n_util::GetStringUTF16(IDS_PROFILES_DICE_SIGNIN_BUTTON);
+      params.subtitle = GetSyncPromoDescription(
+          explicit_signin_access_point_.value_or(access_point),
+          primary_account_info.email);
+      params.button_text = GetSyncPromoButtonLabel(
+          explicit_signin_access_point_.value_or(access_point));
       signin_metrics::LogSyncOptInOffered(
           explicit_signin_access_point_.value_or(access_point));
       break;
diff --git a/chrome/browser/ui/views/safe_browsing/tailored_security_desktop_dialog_manager.h b/chrome/browser/ui/views/safe_browsing/tailored_security_desktop_dialog_manager.h
index 202ec7a..46dbdce 100644
--- a/chrome/browser/ui/views/safe_browsing/tailored_security_desktop_dialog_manager.h
+++ b/chrome/browser/ui/views/safe_browsing/tailored_security_desktop_dialog_manager.h
@@ -12,9 +12,9 @@
 namespace safe_browsing {
 
 // UMA histogram names for the dialogs.
-const char kDisabledDialogOutcome[] =
+inline constexpr char kDisabledDialogOutcome[] =
     "SafeBrowsing.TailoredSecurity.ConsentedDesktopDialogDisabledOutcome";
-const char kEnabledDialogOutcome[] =
+inline constexpr char kEnabledDialogOutcome[] =
     "SafeBrowsing.TailoredSecurity.ConsentedDesktopDialogEnabledOutcome";
 
 inline constexpr char kTailoredSecurityNoticeDialog[] =
diff --git a/chrome/browser/ui/views/tabs/groups/avatar_container_view.cc b/chrome/browser/ui/views/tabs/groups/avatar_container_view.cc
index ff157d09..be77ce9 100644
--- a/chrome/browser/ui/views/tabs/groups/avatar_container_view.cc
+++ b/chrome/browser/ui/views/tabs/groups/avatar_container_view.cc
@@ -207,6 +207,15 @@
 
 ManageSharingAvatarContainer::~ManageSharingAvatarContainer() = default;
 
+void ManageSharingAvatarContainer::OnDeviceScaleFactorChanged(
+    float old_device_scale_factor,
+    float new_device_scale_factor) {
+  views::View::OnDeviceScaleFactorChanged(old_device_scale_factor,
+                                          new_device_scale_factor);
+  RequeryMemberInfo();
+  RebuildChildren();
+}
+
 void ManageSharingAvatarContainer::RequeryMemberInfo() {
   // This action cant be performed if there is no DataSharingService.
   if (!data_sharing_service_) {
@@ -223,17 +232,25 @@
       image_fetcher_service->GetImageFetcher(
           image_fetcher::ImageFetcherConfig::kDiskCacheOnly);
 
+  // Attempt to get the exact scaled avatar image, default to a overscaled by 2
+  // to support HiDPI displays.
+  auto image_size = kCircleSize * 2;
+  if (GetWidget() && GetWidget()->GetCompositor()) {
+    image_size =
+        GetWidget()->GetCompositor()->device_scale_factor() * kCircleSize;
+  }
+
   // If we have up to three members, initiate fetch for each.
   if (members_for_display_.size() > 0) {
     data_sharing_service_->GetAvatarImageForURL(
-        members_for_display_[0].avatar_url, signin::kAccountInfoImageSize,
+        members_for_display_[0].avatar_url, image_size,
         base::BindOnce(&ManageSharingAvatarContainer::UpdateMemberGfxImage,
                        weak_ptr_factory_.GetWeakPtr(), 0),
         image_fetcher);
   }
   if (members_for_display_.size() > 1) {
     data_sharing_service_->GetAvatarImageForURL(
-        members_for_display_[1].avatar_url, signin::kAccountInfoImageSize,
+        members_for_display_[1].avatar_url, image_size,
         base::BindOnce(&ManageSharingAvatarContainer::UpdateMemberGfxImage,
                        weak_ptr_factory_.GetWeakPtr(), 1),
         image_fetcher);
@@ -242,7 +259,7 @@
   // we show the overflow.
   if (members_for_display_.size() == 3) {
     data_sharing_service_->GetAvatarImageForURL(
-        members_for_display_[2].avatar_url, signin::kAccountInfoImageSize,
+        members_for_display_[2].avatar_url, image_size,
         base::BindOnce(&ManageSharingAvatarContainer::UpdateMemberGfxImage,
                        weak_ptr_factory_.GetWeakPtr(), 2),
         image_fetcher);
diff --git a/chrome/browser/ui/views/tabs/groups/avatar_container_view.h b/chrome/browser/ui/views/tabs/groups/avatar_container_view.h
index 603c671..3ebb667 100644
--- a/chrome/browser/ui/views/tabs/groups/avatar_container_view.h
+++ b/chrome/browser/ui/views/tabs/groups/avatar_container_view.h
@@ -35,6 +35,10 @@
   void AddedToWidget() override;
   void OnThemeChanged() override;
 
+ protected:
+  void OnDeviceScaleFactorChanged(float old_device_scale_factor,
+                                  float new_device_scale_factor) override;
+
  private:
   // Callback when data sharing service fetches the avatar.
   void UpdateMemberGfxImage(size_t index, const gfx::Image&);
diff --git a/chrome/browser/ui/views/tabs/tab_style_views.cc b/chrome/browser/ui/views/tabs/tab_style_views.cc
index b7dbd8b..61171ed 100644
--- a/chrome/browser/ui/views/tabs/tab_style_views.cc
+++ b/chrome/browser/ui/views/tabs/tab_style_views.cc
@@ -492,7 +492,6 @@
 }
 
 gfx::Insets TabStyleViewsImpl::GetContentsInsets() const {
-  const int stroke_thickness = GetStrokeThickness(false);
   gfx::Insets base_style_insets = tab_style()->GetContentsInsets();
   gfx::Insets split_insets = gfx::Insets(0);
 
@@ -509,10 +508,8 @@
     split_insets.set_right(total_separator_width / -2);
   }
 
-  return gfx::Insets::TLBR(
-             stroke_thickness, 0,
-             stroke_thickness + GetLayoutConstant(TABSTRIP_TOOLBAR_OVERLAP),
-             0) +
+  return gfx::Insets::TLBR(0, 0, GetLayoutConstant(TABSTRIP_TOOLBAR_OVERLAP),
+                           0) +
          base_style_insets + split_insets;
 }
 
diff --git a/chrome/browser/ui/views/webid/account_selection_bubble_view_unittest.cc b/chrome/browser/ui/views/webid/account_selection_bubble_view_unittest.cc
index c2785d7..44e20b2 100644
--- a/chrome/browser/ui/views/webid/account_selection_bubble_view_unittest.cc
+++ b/chrome/browser/ui/views/webid/account_selection_bubble_view_unittest.cc
@@ -135,7 +135,8 @@
         gfx::Image::CreateFrom1xBitmap(gfx::test::CreateBitmap(1));
     idp_data_ = base::MakeRefCounted<content::IdentityProviderData>(
         kIdpForDisplay, idp_metadata, CreateTestClientMetadata(),
-        blink::mojom::RpContext::kSignIn, kDefaultDisclosureFields,
+        blink::mojom::RpContext::kSignIn, /*format=*/std::nullopt,
+        kDefaultDisclosureFields,
         /*has_login_status_mismatch=*/false);
     accounts_ = {CreateAccount(idp_data_)};
   }
@@ -850,12 +851,14 @@
       base::MakeRefCounted<content::IdentityProviderData>(
           kIdpForDisplay, content::IdentityProviderMetadata(),
           CreateTestClientMetadata(kTermsOfServiceUrl),
-          blink::mojom::RpContext::kSignIn, kDefaultDisclosureFields,
+          blink::mojom::RpContext::kSignIn, /*format=*/std::nullopt,
+          kDefaultDisclosureFields,
           /*has_login_status_mismatch=*/false),
       base::MakeRefCounted<content::IdentityProviderData>(
           kSecondIdpForDisplay, content::IdentityProviderMetadata(),
           CreateTestClientMetadata("https://tos-2.com"),
-          blink::mojom::RpContext::kSignIn, kDefaultDisclosureFields,
+          blink::mojom::RpContext::kSignIn, /*format=*/std::nullopt,
+          kDefaultDisclosureFields,
           /*has_login_status_mismatch=*/false)};
   std::vector<IdentityRequestAccountPtr> account_list = {
       CreateTestIdentityRequestAccount(kAccountSuffixes1[0], idp_list[0]),
@@ -893,12 +896,14 @@
       base::MakeRefCounted<content::IdentityProviderData>(
           kIdpForDisplay, content::IdentityProviderMetadata(),
           CreateTestClientMetadata(kTermsOfServiceUrl),
-          blink::mojom::RpContext::kSignIn, kDefaultDisclosureFields,
+          blink::mojom::RpContext::kSignIn, /*format=*/std::nullopt,
+          kDefaultDisclosureFields,
           /*has_login_status_mismatch=*/false),
       base::MakeRefCounted<content::IdentityProviderData>(
           kSecondIdpForDisplay, content::IdentityProviderMetadata(),
           CreateTestClientMetadata("https://tos-2.com"),
-          blink::mojom::RpContext::kSignIn, kDefaultDisclosureFields,
+          blink::mojom::RpContext::kSignIn, /*format=*/std::nullopt,
+          kDefaultDisclosureFields,
           /*has_login_status_mismatch=*/true)};
   std::vector<IdentityRequestAccountPtr> accounts_list =
       CreateTestIdentityRequestAccounts(kAccountSuffixes1, idp_list[0]);
@@ -937,12 +942,14 @@
       base::MakeRefCounted<content::IdentityProviderData>(
           kIdpForDisplay, idp_with_supports_add,
           CreateTestClientMetadata(kTermsOfServiceUrl),
-          blink::mojom::RpContext::kSignIn, kDefaultDisclosureFields,
+          blink::mojom::RpContext::kSignIn, /*format=*/std::nullopt,
+          kDefaultDisclosureFields,
           /*has_login_status_mismatch=*/false),
       base::MakeRefCounted<content::IdentityProviderData>(
           kSecondIdpForDisplay, idp_with_supports_add,
           CreateTestClientMetadata("https://tos-2.com"),
-          blink::mojom::RpContext::kSignIn, kDefaultDisclosureFields,
+          blink::mojom::RpContext::kSignIn, /*format=*/std::nullopt,
+          kDefaultDisclosureFields,
           /*has_login_status_mismatch=*/false)};
   std::vector<IdentityRequestAccountPtr> accounts_list = {
       CreateTestIdentityRequestAccount(kAccountSuffixes1[0], idp_list[0]),
@@ -978,22 +985,26 @@
   idp_list_ = {base::MakeRefCounted<content::IdentityProviderData>(
                    kIdpForDisplay, content::IdentityProviderMetadata(),
                    CreateTestClientMetadata(kTermsOfServiceUrl),
-                   blink::mojom::RpContext::kSignIn, kDefaultDisclosureFields,
+                   blink::mojom::RpContext::kSignIn, /*format=*/std::nullopt,
+                   kDefaultDisclosureFields,
                    /*has_login_status_mismatch=*/false),
                base::MakeRefCounted<content::IdentityProviderData>(
                    kSecondIdpForDisplay, content::IdentityProviderMetadata(),
                    CreateTestClientMetadata("https://tos-2.com"),
-                   blink::mojom::RpContext::kSignIn, kDefaultDisclosureFields,
+                   blink::mojom::RpContext::kSignIn, /*format=*/std::nullopt,
+                   kDefaultDisclosureFields,
                    /*has_login_status_mismatch=*/false),
                base::MakeRefCounted<content::IdentityProviderData>(
                    "idp3.com", content::IdentityProviderMetadata(),
                    CreateTestClientMetadata("https://tos-3.com"),
-                   blink::mojom::RpContext::kSignIn, kDefaultDisclosureFields,
+                   blink::mojom::RpContext::kSignIn, /*format=*/std::nullopt,
+                   kDefaultDisclosureFields,
                    /*has_login_status_mismatch=*/true),
                base::MakeRefCounted<content::IdentityProviderData>(
                    "idp4.com", content::IdentityProviderMetadata(),
                    CreateTestClientMetadata("https://tos-4.com"),
-                   blink::mojom::RpContext::kSignIn, kDefaultDisclosureFields,
+                   blink::mojom::RpContext::kSignIn, /*format=*/std::nullopt,
+                   kDefaultDisclosureFields,
                    /*has_login_status_mismatch=*/true)};
   accounts_ = {
       CreateTestIdentityRequestAccount(kAccountSuffixes2[0], idp_list_[1],
@@ -1036,12 +1047,14 @@
       base::MakeRefCounted<content::IdentityProviderData>(
           kIdpForDisplay, content::IdentityProviderMetadata(),
           CreateTestClientMetadata(kTermsOfServiceUrl),
-          blink::mojom::RpContext::kSignIn, kDefaultDisclosureFields,
+          blink::mojom::RpContext::kSignIn, /*format=*/std::nullopt,
+          kDefaultDisclosureFields,
           /*has_login_status_mismatch=*/true),
       base::MakeRefCounted<content::IdentityProviderData>(
           kSecondIdpForDisplay, content::IdentityProviderMetadata(),
           CreateTestClientMetadata("https://tos-2.com"),
-          blink::mojom::RpContext::kSignIn, kDefaultDisclosureFields,
+          blink::mojom::RpContext::kSignIn, /*format=*/std::nullopt,
+          kDefaultDisclosureFields,
           /*has_login_status_mismatch=*/true)};
   CreateAndShowMultiIdpAccountPicker(std::vector<IdentityRequestAccountPtr>(),
                                      idp_list);
@@ -1068,12 +1081,14 @@
       base::MakeRefCounted<content::IdentityProviderData>(
           kIdpForDisplay, content::IdentityProviderMetadata(),
           CreateTestClientMetadata(kTermsOfServiceUrl),
-          blink::mojom::RpContext::kSignIn, kDefaultDisclosureFields,
+          blink::mojom::RpContext::kSignIn, /*format=*/std::nullopt,
+          kDefaultDisclosureFields,
           /*has_login_status_mismatch=*/false),
       base::MakeRefCounted<content::IdentityProviderData>(
           kSecondIdpForDisplay, content::IdentityProviderMetadata(),
           CreateTestClientMetadata("https://tos-2.com"),
-          blink::mojom::RpContext::kSignIn, kDefaultDisclosureFields,
+          blink::mojom::RpContext::kSignIn, /*format=*/std::nullopt,
+          kDefaultDisclosureFields,
           /*has_login_status_mismatch=*/false)};
   // The UI code receives the accounts sorted in the order in which they should
   // be displayed.
@@ -1117,12 +1132,14 @@
       base::MakeRefCounted<content::IdentityProviderData>(
           kIdpForDisplay, content::IdentityProviderMetadata(),
           CreateTestClientMetadata(kTermsOfServiceUrl),
-          blink::mojom::RpContext::kSignIn, kDefaultDisclosureFields,
+          blink::mojom::RpContext::kSignIn, /*format=*/std::nullopt,
+          kDefaultDisclosureFields,
           /*has_login_status_mismatch=*/false),
       base::MakeRefCounted<content::IdentityProviderData>(
           kSecondIdpForDisplay, content::IdentityProviderMetadata(),
           CreateTestClientMetadata("https://tos-2.com"),
-          blink::mojom::RpContext::kSignIn, kDefaultDisclosureFields,
+          blink::mojom::RpContext::kSignIn, /*format=*/std::nullopt,
+          kDefaultDisclosureFields,
           /*has_login_status_mismatch=*/false)};
   // Note that `new2` is last despite having last_used_timestamp because it is
   // not considered a returning account.
diff --git a/chrome/browser/ui/views/webid/account_selection_modal_view_browsertest.cc b/chrome/browser/ui/views/webid/account_selection_modal_view_browsertest.cc
index fbdb00c..c313f5f 100644
--- a/chrome/browser/ui/views/webid/account_selection_modal_view_browsertest.cc
+++ b/chrome/browser/ui/views/webid/account_selection_modal_view_browsertest.cc
@@ -62,6 +62,7 @@
             content::IdentityProviderMetadata(),
             CreateTestClientMetadata(),
             blink::mojom::RpContext::kSignIn,
+            /*format=*/std::nullopt,
             kDefaultDisclosureFields,
             /*has_login_status_mismatch=*/false)) {
     test_shared_url_loader_factory_ =
diff --git a/chrome/browser/ui/views/webid/fedcm_account_selection_view_desktop_browsertest.cc b/chrome/browser/ui/views/webid/fedcm_account_selection_view_desktop_browsertest.cc
index de529a5..cb4d7d9 100644
--- a/chrome/browser/ui/views/webid/fedcm_account_selection_view_desktop_browsertest.cc
+++ b/chrome/browser/ui/views/webid/fedcm_account_selection_view_desktop_browsertest.cc
@@ -45,7 +45,8 @@
     idps_ = {base::MakeRefCounted<content::IdentityProviderData>(
         "idp-example.com", content::IdentityProviderMetadata(),
         content::ClientMetadata(GURL(), GURL(), GURL(), gfx::Image()),
-        blink::mojom::RpContext::kSignIn, kDefaultDisclosureFields,
+        blink::mojom::RpContext::kSignIn, /*format=*/std::nullopt,
+        kDefaultDisclosureFields,
         /*has_login_status_mismatch=*/false)};
     accounts_ = {base::MakeRefCounted<Account>(
         "id", "display_identifier", "display_name", "email", "name",
@@ -316,7 +317,8 @@
     idps_ = {base::MakeRefCounted<content::IdentityProviderData>(
         "idp-example.com", content::IdentityProviderMetadata(),
         content::ClientMetadata(GURL(), GURL(), GURL(), gfx::Image()),
-        blink::mojom::RpContext::kSignIn, kDefaultDisclosureFields,
+        blink::mojom::RpContext::kSignIn, /*format=*/std::nullopt,
+        kDefaultDisclosureFields,
         /*has_login_status_mismatch=*/false)};
     accounts_ = {base::MakeRefCounted<Account>(
         "id", "display_identifier", "display_name", "email", "name",
diff --git a/chrome/browser/ui/views/webid/fedcm_account_selection_view_desktop_unittest.cc b/chrome/browser/ui/views/webid/fedcm_account_selection_view_desktop_unittest.cc
index d57dfde7..5c95ced 100644
--- a/chrome/browser/ui/views/webid/fedcm_account_selection_view_desktop_unittest.cc
+++ b/chrome/browser/ui/views/webid/fedcm_account_selection_view_desktop_unittest.cc
@@ -303,8 +303,8 @@
     return base::MakeRefCounted<content::IdentityProviderData>(
         /*idp_for_display=*/"", content::IdentityProviderMetadata(),
         content::ClientMetadata(GURL(), GURL(), GURL(), gfx::Image()),
-        blink::mojom::RpContext::kSignIn, disclosure_fields,
-        has_login_status_mismatch);
+        blink::mojom::RpContext::kSignIn, /*format=*/std::nullopt,
+        disclosure_fields, has_login_status_mismatch);
   }
 
   IdentityRequestAccountPtr CreateAccount(
diff --git a/chrome/browser/ui/views/webid/fedcm_interactive_uitest.cc b/chrome/browser/ui/views/webid/fedcm_interactive_uitest.cc
index fef1f27..fd2151f 100644
--- a/chrome/browser/ui/views/webid/fedcm_interactive_uitest.cc
+++ b/chrome/browser/ui/views/webid/fedcm_interactive_uitest.cc
@@ -41,7 +41,8 @@
       idps_ = {base::MakeRefCounted<content::IdentityProviderData>(
           "idp-example.com", content::IdentityProviderMetadata(),
           content::ClientMetadata(GURL(), GURL(), GURL(), gfx::Image()),
-          blink::mojom::RpContext::kSignIn, kDefaultDisclosureFields,
+          blink::mojom::RpContext::kSignIn, /*format=*/std::nullopt,
+          kDefaultDisclosureFields,
           /*has_login_status_mismatch=*/false)};
       accounts_ = {base::MakeRefCounted<Account>(
           "id", "display_identifier", "display_name", "email", "name",
diff --git a/chrome/browser/ui/webid/identity_dialog_controller_unittest.cc b/chrome/browser/ui/webid/identity_dialog_controller_unittest.cc
index 13494efb..c713954 100644
--- a/chrome/browser/ui/webid/identity_dialog_controller_unittest.cc
+++ b/chrome/browser/ui/webid/identity_dialog_controller_unittest.cc
@@ -169,7 +169,8 @@
         base::MakeRefCounted<content::IdentityProviderData>(
             kIdpEtldPlusOne, content::IdentityProviderMetadata(),
             content::ClientMetadata(GURL(), GURL(), GURL(), gfx::Image()),
-            blink::mojom::RpContext::kSignIn, kDefaultPermissions,
+            blink::mojom::RpContext::kSignIn, /*format=*/std::nullopt,
+            kDefaultPermissions,
             /*has_login_status_mismatch=*/false);
     for (auto& account : accounts) {
       account->identity_provider = idp_data;
diff --git a/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_dialog.h b/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_dialog.h
index 0091639..f7efda0 100644
--- a/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_dialog.h
+++ b/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_dialog.h
@@ -67,15 +67,15 @@
 };
 
 // The string conversions of ash::cloud_upload::mojom::UserAction.
-constexpr char kUserActionCancel[] = "cancel";
-constexpr char kUserActionCancelGoogleDrive[] = "cancel-drive";
-constexpr char kUserActionCancelOneDrive[] = "cancel-onedrive";
-constexpr char kUserActionSetUpOneDrive[] = "setup-onedrive";
-constexpr char kUserActionUploadToGoogleDrive[] = "upload-drive";
-constexpr char kUserActionUploadToOneDrive[] = "upload-onedrive";
-constexpr char kUserActionConfirmOrUploadToGoogleDrive[] =
+inline constexpr char kUserActionCancel[] = "cancel";
+inline constexpr char kUserActionCancelGoogleDrive[] = "cancel-drive";
+inline constexpr char kUserActionCancelOneDrive[] = "cancel-onedrive";
+inline constexpr char kUserActionSetUpOneDrive[] = "setup-onedrive";
+inline constexpr char kUserActionUploadToGoogleDrive[] = "upload-drive";
+inline constexpr char kUserActionUploadToOneDrive[] = "upload-onedrive";
+inline constexpr char kUserActionConfirmOrUploadToGoogleDrive[] =
     "confirm-or-upload-google-drive";
-constexpr char kUserActionConfirmOrUploadToOneDrive[] =
+inline constexpr char kUserActionConfirmOrUploadToOneDrive[] =
     "confirm-or-upload-onedrive";
 
 // Options for which setup or move confirmation sub-page/flow we want to show.
diff --git a/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.h b/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.h
index 5c17c7a6..202e1ea 100644
--- a/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.h
+++ b/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.h
@@ -229,95 +229,96 @@
   kMaxValue = kFileNotAnOfficeFile,
 };
 
-constexpr char kGoogleDriveTaskResultMetricName[] =
+inline constexpr char kGoogleDriveTaskResultMetricName[] =
     "FileBrowser.OfficeFiles.TaskResult.Drive";
-constexpr char kGoogleDriveTaskResultMetricStateMetricName[] =
+inline constexpr char kGoogleDriveTaskResultMetricStateMetricName[] =
     "FileBrowser.OfficeFiles.TaskResult.GoogleDrive.MetricState";
 
-constexpr char kOneDriveTaskResultMetricName[] =
+inline constexpr char kOneDriveTaskResultMetricName[] =
     "FileBrowser.OfficeFiles.TaskResult.OneDrive";
-constexpr char kOneDriveTaskResultMetricStateMetricName[] =
+inline constexpr char kOneDriveTaskResultMetricStateMetricName[] =
     "FileBrowser.OfficeFiles.TaskResult.OneDrive.MetricState";
 
-constexpr char kGoogleDriveUploadResultMetricName[] =
+inline constexpr char kGoogleDriveUploadResultMetricName[] =
     "FileBrowser.OfficeFiles.Open.UploadResult.GoogleDrive";
-constexpr char kGoogleDriveUploadResultMetricStateMetricName[] =
+inline constexpr char kGoogleDriveUploadResultMetricStateMetricName[] =
     "FileBrowser.OfficeFiles.Open.UploadResult.GoogleDrive.MetricState";
 
-constexpr char kOneDriveUploadResultMetricName[] =
+inline constexpr char kOneDriveUploadResultMetricName[] =
     "FileBrowser.OfficeFiles.Open.UploadResult.OneDrive";
-constexpr char kOneDriveUploadResultMetricStateMetricName[] =
+inline constexpr char kOneDriveUploadResultMetricStateMetricName[] =
     "FileBrowser.OfficeFiles.Open.UploadResult.OneDrive.MetricState";
 
-constexpr char kGoogleDriveMoveErrorMetricName[] =
+inline constexpr char kGoogleDriveMoveErrorMetricName[] =
     "FileBrowser.OfficeFiles.Open.IOTaskError.GoogleDrive.Move";
-constexpr char kGoogleDriveMoveErrorMetricStateMetricName[] =
+inline constexpr char kGoogleDriveMoveErrorMetricStateMetricName[] =
     "FileBrowser.OfficeFiles.Open.IOTaskError.GoogleDrive.Move.MetricState";
 
-constexpr char kGoogleDriveCopyErrorMetricName[] =
+inline constexpr char kGoogleDriveCopyErrorMetricName[] =
     "FileBrowser.OfficeFiles.Open.IOTaskError.GoogleDrive.Copy";
-constexpr char kGoogleDriveCopyErrorMetricStateMetricName[] =
+inline constexpr char kGoogleDriveCopyErrorMetricStateMetricName[] =
     "FileBrowser.OfficeFiles.Open.IOTaskError.GoogleDrive.Copy.MetricState";
 
-constexpr char kOneDriveMoveErrorMetricName[] =
+inline constexpr char kOneDriveMoveErrorMetricName[] =
     "FileBrowser.OfficeFiles.Open.IOTaskError.OneDrive.Move";
-constexpr char kOneDriveMoveErrorMetricStateMetricName[] =
+inline constexpr char kOneDriveMoveErrorMetricStateMetricName[] =
     "FileBrowser.OfficeFiles.Open.IOTaskError.OneDrive.Move.MetricState";
 
-constexpr char kOneDriveCopyErrorMetricName[] =
+inline constexpr char kOneDriveCopyErrorMetricName[] =
     "FileBrowser.OfficeFiles.Open.IOTaskError.OneDrive.Copy";
-constexpr char kOneDriveCopyErrorMetricStateMetricName[] =
+inline constexpr char kOneDriveCopyErrorMetricStateMetricName[] =
     "FileBrowser.OfficeFiles.Open.IOTaskError.OneDrive.Copy.MetricState";
 
-constexpr char kDriveOpenSourceVolumeMetric[] =
+inline constexpr char kDriveOpenSourceVolumeMetric[] =
     "FileBrowser.OfficeFiles.Open.SourceVolume.GoogleDrive";
-constexpr char kDriveOpenSourceVolumeMetricStateMetric[] =
+inline constexpr char kDriveOpenSourceVolumeMetricStateMetric[] =
     "FileBrowser.OfficeFiles.Open.SourceVolume.GoogleDrive.MetricState";
 
-constexpr char kOneDriveOpenSourceVolumeMetric[] =
+inline constexpr char kOneDriveOpenSourceVolumeMetric[] =
     "FileBrowser.OfficeFiles.Open.SourceVolume.MicrosoftOneDrive";
-constexpr char kOneDriveOpenSourceVolumeMetricStateMetric[] =
+inline constexpr char kOneDriveOpenSourceVolumeMetricStateMetric[] =
     "FileBrowser.OfficeFiles.Open.SourceVolume.OneDrive.MetricState";
 
-constexpr char kNumberOfFilesToOpenWithGoogleDriveMetric[] =
+inline constexpr char kNumberOfFilesToOpenWithGoogleDriveMetric[] =
     "FileBrowser.OfficeFiles.Open.NumberOfFiles.GoogleDrive";
 
-constexpr char kNumberOfFilesToOpenWithOneDriveMetric[] =
+inline constexpr char kNumberOfFilesToOpenWithOneDriveMetric[] =
     "FileBrowser.OfficeFiles.Open.NumberOfFiles.OneDrive";
 
-constexpr char kOpenInitialCloudProviderMetric[] =
+inline constexpr char kOpenInitialCloudProviderMetric[] =
     "FileBrowser.OfficeFiles.Open.CloudProvider";
 
-constexpr char kDriveTransferRequiredMetric[] =
+inline constexpr char kDriveTransferRequiredMetric[] =
     "FileBrowser.OfficeFiles.Open.TransferRequired.GoogleDrive";
-constexpr char kDriveTransferRequiredMetricStateMetric[] =
+inline constexpr char kDriveTransferRequiredMetricStateMetric[] =
     "FileBrowser.OfficeFiles.Open.TransferRequired.GoogleDrive.MetricState";
 
-constexpr char kOneDriveTransferRequiredMetric[] =
+inline constexpr char kOneDriveTransferRequiredMetric[] =
     "FileBrowser.OfficeFiles.Open.TransferRequired.OneDrive";
-constexpr char kOneDriveTransferRequiredMetricStateMetric[] =
+inline constexpr char kOneDriveTransferRequiredMetricStateMetric[] =
     "FileBrowser.OfficeFiles.Open.TransferRequired.OneDrive.MetricState";
 
-constexpr char kDriveErrorMetricName[] = "FileBrowser.OfficeFiles.Errors.Drive";
-constexpr char kDriveErrorMetricStateMetricName[] =
+inline constexpr char kDriveErrorMetricName[] =
+    "FileBrowser.OfficeFiles.Errors.Drive";
+inline constexpr char kDriveErrorMetricStateMetricName[] =
     "FileBrowser.OfficeFiles.Errors.GoogleDrive.MetricState";
 
-constexpr char kOneDriveErrorMetricName[] =
+inline constexpr char kOneDriveErrorMetricName[] =
     "FileBrowser.OfficeFiles.Errors.OneDrive";
-constexpr char kOneDriveErrorMetricStateMetricName[] =
+inline constexpr char kOneDriveErrorMetricStateMetricName[] =
     "FileBrowser.OfficeFiles.Errors.OneDrive.MetricState";
 
 // Query actions for this path to get ODFS Metadata.
-const char kODFSMetadataQueryPath[] = "/";
+inline constexpr char kODFSMetadataQueryPath[] = "/";
 
 // Custom action ids passed from ODFS.
-const char kOneDriveUrlActionId[] = "HIDDEN_ONEDRIVE_URL";
-const char kUserEmailActionId[] = "HIDDEN_ONEDRIVE_USER_EMAIL";
+inline constexpr char kOneDriveUrlActionId[] = "HIDDEN_ONEDRIVE_URL";
+inline constexpr char kUserEmailActionId[] = "HIDDEN_ONEDRIVE_USER_EMAIL";
 // TODO(b/330786891): Remove this once it's no longer needed for backwards
 // compatibility with ODFS.
-const char kReauthenticationRequiredId[] =
+inline constexpr char kReauthenticationRequiredId[] =
     "HIDDEN_ONEDRIVE_REAUTHENTICATION_REQUIRED";
-const char kAccountStateId[] = "HIDDEN_ONEDRIVE_ACCOUNT_STATE";
+inline constexpr char kAccountStateId[] = "HIDDEN_ONEDRIVE_ACCOUNT_STATE";
 
 // Get generic error message for uploading office files.
 std::string GetGenericErrorMessage();
diff --git a/chrome/browser/ui/webui/ash/diagnostics_dialog/diagnostics_dialog.h b/chrome/browser/ui/webui/ash/diagnostics_dialog/diagnostics_dialog.h
index c23d7be..0a0bc2c 100644
--- a/chrome/browser/ui/webui/ash/diagnostics_dialog/diagnostics_dialog.h
+++ b/chrome/browser/ui/webui/ash/diagnostics_dialog/diagnostics_dialog.h
@@ -15,7 +15,7 @@
 // ID used to lookup existing DiagnosticsDialog instance from
 // SystemWebDialogDelegate list and ensure only one instance of
 // DiagnosticsDialog exists at a time.
-constexpr char kDiagnosticsDialogId[] = "diagnostics-dialog";
+inline constexpr char kDiagnosticsDialogId[] = "diagnostics-dialog";
 
 }  // namespace
 
diff --git a/chrome/browser/ui/webui/ash/login/consolidated_consent_screen_handler.h b/chrome/browser/ui/webui/ash/login/consolidated_consent_screen_handler.h
index 59e867a..52e1459 100644
--- a/chrome/browser/ui/webui/ash/login/consolidated_consent_screen_handler.h
+++ b/chrome/browser/ui/webui/ash/login/consolidated_consent_screen_handler.h
@@ -16,9 +16,9 @@
 class ConsolidatedConsentScreen;
 
 namespace {
-const char kGoogleEulaDefaultUrl[] =
+inline constexpr char kGoogleEulaDefaultUrl[] =
     "https://policies.google.com/terms/embedded?hl=en";
-const char kCrosEulaDefaultUrl[] =
+inline constexpr char kCrosEulaDefaultUrl[] =
     "https://www.google.com/intl/en/chrome/terms/";
 }  // namespace
 
diff --git a/chrome/browser/ui/webui/ash/office_fallback/office_fallback_ui.h b/chrome/browser/ui/webui/ash/office_fallback/office_fallback_ui.h
index 3184b57..1416c7d0 100644
--- a/chrome/browser/ui/webui/ash/office_fallback/office_fallback_ui.h
+++ b/chrome/browser/ui/webui/ash/office_fallback/office_fallback_ui.h
@@ -23,10 +23,10 @@
 namespace ash::office_fallback {
 
 // The string conversions of ash::office_fallback::mojom::DialogChoice.
-const char kDialogChoiceCancel[] = "cancel";
-const char kDialogChoiceOk[] = "ok";
-const char kDialogChoiceQuickOffice[] = "quick-office";
-const char kDialogChoiceTryAgain[] = "try-again";
+inline constexpr char kDialogChoiceCancel[] = "cancel";
+inline constexpr char kDialogChoiceOk[] = "ok";
+inline constexpr char kDialogChoiceQuickOffice[] = "quick-office";
+inline constexpr char kDialogChoiceTryAgain[] = "try-again";
 
 class OfficeFallbackUI;
 
diff --git a/chrome/browser/ui/webui/ash/parent_access/parent_access_metrics_utils.h b/chrome/browser/ui/webui/ash/parent_access/parent_access_metrics_utils.h
index 4402383..a7e03ca 100644
--- a/chrome/browser/ui/webui/ash/parent_access/parent_access_metrics_utils.h
+++ b/chrome/browser/ui/webui/ash/parent_access/parent_access_metrics_utils.h
@@ -14,11 +14,11 @@
 namespace parent_access {
 
 // Title bases used in histogram title generation.
-constexpr char kParentAccessWidgetShowDialogErrorHistogramBase[] =
+inline constexpr char kParentAccessWidgetShowDialogErrorHistogramBase[] =
     "ChromeOS.FamilyLinkUser.ParentAccessWidgetShowDialogError";
-constexpr char kParentAccessFlowResultHistogramBase[] =
+inline constexpr char kParentAccessFlowResultHistogramBase[] =
     "ChromeOS.FamilyLinkUser.ParentAccess.FlowResult";
-constexpr char kParentAccessWidgetErrorHistogramBase[] =
+inline constexpr char kParentAccessWidgetErrorHistogramBase[] =
     "ChromeOS.FamilyLinkUser.ParentAccessWidgetError";
 
 // Returns the title string for a histogram given a title base and flow
diff --git a/chrome/browser/ui/webui/ash/sanitize_dialog/sanitize_dialog.h b/chrome/browser/ui/webui/ash/sanitize_dialog/sanitize_dialog.h
index 7d8e9f1..8f25a331 100644
--- a/chrome/browser/ui/webui/ash/sanitize_dialog/sanitize_dialog.h
+++ b/chrome/browser/ui/webui/ash/sanitize_dialog/sanitize_dialog.h
@@ -13,7 +13,7 @@
 namespace {
 
 // ID used to check if there are any other instances of the dialog open.
-constexpr char kSanitizeDialogId[] = "sanitize-dialog";
+inline constexpr char kSanitizeDialogId[] = "sanitize-dialog";
 
 }  // namespace
 
diff --git a/chrome/browser/ui/webui/autofill_and_password_manager_internals/internals_ui_handler.h b/chrome/browser/ui/webui/autofill_and_password_manager_internals/internals_ui_handler.h
index 762ab8d..1f1abeb9 100644
--- a/chrome/browser/ui/webui/autofill_and_password_manager_internals/internals_ui_handler.h
+++ b/chrome/browser/ui/webui/autofill_and_password_manager_internals/internals_ui_handler.h
@@ -27,10 +27,11 @@
 
 namespace autofill {
 
-constexpr char kCacheResetDone[] =
+inline constexpr char kCacheResetDone[] =
     "Done. Please close and reopen all tabs that should be affected by the "
     "cache reset.";
-constexpr char kCacheResetAlreadyInProgress[] = "Reset already in progress";
+inline constexpr char kCacheResetAlreadyInProgress[] =
+    "Reset already in progress";
 
 void CreateAndAddInternalsHTMLSource(Profile* profile,
                                      const std::string& source_name);
diff --git a/chrome/browser/ui/webui/cr_components/history_clusters/history_clusters_util.h b/chrome/browser/ui/webui/cr_components/history_clusters/history_clusters_util.h
index 18354cd..70ce5ec2 100644
--- a/chrome/browser/ui/webui/cr_components/history_clusters/history_clusters_util.h
+++ b/chrome/browser/ui/webui/cr_components/history_clusters/history_clusters_util.h
@@ -5,10 +5,11 @@
 #ifndef CHROME_BROWSER_UI_WEBUI_CR_COMPONENTS_HISTORY_CLUSTERS_HISTORY_CLUSTERS_UTIL_H_
 #define CHROME_BROWSER_UI_WEBUI_CR_COMPONENTS_HISTORY_CLUSTERS_HISTORY_CLUSTERS_UTIL_H_
 
-constexpr char kIsHistoryClustersVisibleKey[] = "isHistoryClustersVisible";
-constexpr char kIsHistoryClustersVisibleManagedByPolicyKey[] =
+inline constexpr char kIsHistoryClustersVisibleKey[] =
+    "isHistoryClustersVisible";
+inline constexpr char kIsHistoryClustersVisibleManagedByPolicyKey[] =
     "isHistoryClustersVisibleManagedByPolicy";
-constexpr char kRenameJourneysKey[] = "renameJourneys";
+inline constexpr char kRenameJourneysKey[] = "renameJourneys";
 
 class Profile;
 
diff --git a/chrome/browser/ui/webui/data_sharing/data_sharing_page_handler.cc b/chrome/browser/ui/webui/data_sharing/data_sharing_page_handler.cc
index c432ccd8..d0fbec8 100644
--- a/chrome/browser/ui/webui/data_sharing/data_sharing_page_handler.cc
+++ b/chrome/browser/ui/webui/data_sharing/data_sharing_page_handler.cc
@@ -12,9 +12,9 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_features.h"
-#include "chrome/browser/ui/views/data_sharing/data_sharing_open_group_helper.h"
 #include "chrome/browser/ui/views/data_sharing/data_sharing_utils.h"
 #include "chrome/browser/ui/webui/data_sharing/data_sharing_ui.h"
+#include "components/saved_tab_groups/public/tab_group_sync_service.h"
 #include "components/signin/public/identity_manager/access_token_info.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 #include "google_apis/gaia/gaia_constants.h"
@@ -101,11 +101,6 @@
 }
 
 void DataSharingPageHandler::OpenTabGroup(const std::string& group_id) {
-  Browser* const browser = chrome::FindLastActiveWithProfile(GetProfile());
-  CHECK(browser);
-  browser->browser_window_features()
-      ->data_sharing_open_group_helper()
-      ->OpenTabGroupWhenAvailable(group_id);
 }
 
 void DataSharingPageHandler::AboutToUnShareTabGroup(
diff --git a/chrome/browser/ui/webui/data_sharing/data_sharing_page_handler_unittest.cc b/chrome/browser/ui/webui/data_sharing/data_sharing_page_handler_unittest.cc
index 2b63e8a2..b5da366 100644
--- a/chrome/browser/ui/webui/data_sharing/data_sharing_page_handler_unittest.cc
+++ b/chrome/browser/ui/webui/data_sharing/data_sharing_page_handler_unittest.cc
@@ -8,7 +8,6 @@
 #include "base/test/scoped_feature_list.h"
 #include "build/branding_buildflags.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_features.h"
-#include "chrome/browser/ui/views/data_sharing/data_sharing_open_group_helper.h"
 #include "chrome/browser/ui/webui/data_sharing/data_sharing_ui.h"
 #include "chrome/test/base/browser_with_test_window_test.h"
 #include "components/data_sharing/public/features.h"
@@ -118,15 +117,6 @@
                                 std::move(callback));
 }
 
-// TODO(crbug.com/381173816): This test should not run without setting sync
-// service.
-TEST_F(DataSharingPageHandlerUnitTest, DISABLED_OpenTabGroup) {
-  handler()->OpenTabGroup("FAKE_GROUP_ID");
-  DataSharingOpenGroupHelper* helper =
-      browser()->browser_window_features()->data_sharing_open_group_helper();
-  EXPECT_TRUE(helper->group_ids_for_testing().contains("FAKE_GROUP_ID"));
-}
-
 TEST_F(DataSharingPageHandlerUnitTest, OnAccessTokenFetched) {
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
   GTEST_SKIP() << "N/A for Google Chrome Branding Build";
diff --git a/chrome/browser/ui/webui/settings/glic_handler_browsertest.cc b/chrome/browser/ui/webui/settings/glic_handler_browsertest.cc
index d890fb2..ea64e670 100644
--- a/chrome/browser/ui/webui/settings/glic_handler_browsertest.cc
+++ b/chrome/browser/ui/webui/settings/glic_handler_browsertest.cc
@@ -75,7 +75,13 @@
 }
 #endif  //  !BUILDFLAG(IS_OZONE_WAYLAND)
 
-IN_PROC_BROWSER_TEST_F(GlicHandlerBrowserTest, UpdateGlicShortcut) {
+// TODO(crbug.com/416160303): Enable the test.
+#if BUILDFLAG(IS_WIN)
+#define MAYBE_UpdateGlicShortcut DISABLED_UpdateGlicShortcut
+#else
+#define MAYBE_UpdateGlicShortcut UpdateGlicShortcut
+#endif
+IN_PROC_BROWSER_TEST_F(GlicHandlerBrowserTest, MAYBE_UpdateGlicShortcut) {
   const ui::Accelerator invalid_shortcut(ui::VKEY_A, ui::EF_NONE);
   glic_handler()->HandleSetGlicShortcut(
       base::Value::List()
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 978ceaeb..e3a1e00 100644
--- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -755,6 +755,9 @@
       {"glicNavigationShortcut", IDS_SETTINGS_GLIC_NAVIGATION_SHORTCUT},
       {"glicNavigationShortcutSublabel",
        IDS_SETTINGS_GLIC_NAVIGATION_SHORTCUT_SUBLABEL},
+      {"glicClosedCaptionsToggle", IDS_SETTINGS_GLIC_CLOSED_CAPTIONING},
+      {"glicClosedCaptionsToggleSublabel",
+       IDS_SETTINGS_GLIC_CLOSED_CAPTIONING_SUBLABEL},
       {"glicLocationToggle", IDS_SETTINGS_GLIC_PERMISSIONS_LOCATION_TOGGLE},
       {"glicLocationToggleSublabel",
        IDS_SETTINGS_GLIC_PERMISSIONS_LOCATION_TOGGLE_SUBLABEL},
diff --git a/chrome/browser/ui/webui/settings/site_settings_helper.h b/chrome/browser/ui/webui/settings/site_settings_helper.h
index 5052df1ec..69e81f25 100644
--- a/chrome/browser/ui/webui/settings/site_settings_helper.h
+++ b/chrome/browser/ui/webui/settings/site_settings_helper.h
@@ -92,35 +92,35 @@
 using ChooserExceptionDetails =
     std::set<std::tuple<GURL, SiteSettingSource, bool>>;
 
-constexpr char kChooserType[] = "chooserType";
-constexpr char kCloseDescription[] = "closeDescription";
-constexpr char kDisabled[] = "disabled";
-constexpr char kDisplayName[] = "displayName";
-constexpr char kDescription[] = "description";
-constexpr char kEmbeddingOrigin[] = "embeddingOrigin";
-constexpr char kEmbeddingDisplayName[] = "embeddingDisplayName";
-constexpr char kExceptions[] = "exceptions";
-constexpr char kFileSystemFilePath[] = "filePath";
-constexpr char kFileSystemIsDirectory[] = "isDirectory";
-constexpr char kFileSystemEditGrants[] = "editGrants";
-constexpr char kFileSystemViewGrants[] = "viewGrants";
-constexpr char kHostOrSpec[] = "hostOrSpec";
-constexpr char kIncognito[] = "incognito";
-constexpr char kIsEmbargoed[] = "isEmbargoed";
-constexpr char kObject[] = "object";
-constexpr char kOpenDescription[] = "openDescription";
-constexpr char kOrigin[] = "origin";
-constexpr char kOrigins[] = "origins";
-constexpr char kOriginForFavicon[] = "originForFavicon";
-constexpr char kPermissions[] = "permissions";
-constexpr char kPolicyIndicator[] = "indicator";
-constexpr char kReaderName[] = "readerName";
-constexpr char kRecentPermissions[] = "recentPermissions";
-constexpr char kSetting[] = "setting";
-constexpr char kSites[] = "sites";
-constexpr char kSource[] = "source";
-constexpr char kType[] = "type";
-constexpr char kNotificationPermissionsReviewListMaybeChangedEvent[] =
+inline constexpr char kChooserType[] = "chooserType";
+inline constexpr char kCloseDescription[] = "closeDescription";
+inline constexpr char kDisabled[] = "disabled";
+inline constexpr char kDisplayName[] = "displayName";
+inline constexpr char kDescription[] = "description";
+inline constexpr char kEmbeddingOrigin[] = "embeddingOrigin";
+inline constexpr char kEmbeddingDisplayName[] = "embeddingDisplayName";
+inline constexpr char kExceptions[] = "exceptions";
+inline constexpr char kFileSystemFilePath[] = "filePath";
+inline constexpr char kFileSystemIsDirectory[] = "isDirectory";
+inline constexpr char kFileSystemEditGrants[] = "editGrants";
+inline constexpr char kFileSystemViewGrants[] = "viewGrants";
+inline constexpr char kHostOrSpec[] = "hostOrSpec";
+inline constexpr char kIncognito[] = "incognito";
+inline constexpr char kIsEmbargoed[] = "isEmbargoed";
+inline constexpr char kObject[] = "object";
+inline constexpr char kOpenDescription[] = "openDescription";
+inline constexpr char kOrigin[] = "origin";
+inline constexpr char kOrigins[] = "origins";
+inline constexpr char kOriginForFavicon[] = "originForFavicon";
+inline constexpr char kPermissions[] = "permissions";
+inline constexpr char kPolicyIndicator[] = "indicator";
+inline constexpr char kReaderName[] = "readerName";
+inline constexpr char kRecentPermissions[] = "recentPermissions";
+inline constexpr char kSetting[] = "setting";
+inline constexpr char kSites[] = "sites";
+inline constexpr char kSource[] = "source";
+inline constexpr char kType[] = "type";
+inline constexpr char kNotificationPermissionsReviewListMaybeChangedEvent[] =
     "notification-permission-review-list-maybe-changed";
 
 // Returns whether a group name has been registered for the given type.
diff --git a/chrome/browser/ui/webui/tab_strip/tab_strip_ui.h b/chrome/browser/ui/webui/tab_strip/tab_strip_ui.h
index 726be896..69046ce 100644
--- a/chrome/browser/ui/webui/tab_strip/tab_strip_ui.h
+++ b/chrome/browser/ui/webui/tab_strip/tab_strip_ui.h
@@ -28,8 +28,9 @@
 class TabStripUIEmbedder;
 
 // These data types must be in all lowercase.
-constexpr char16_t kWebUITabIdDataType[] = u"application/vnd.chromium.tab";
-constexpr char16_t kWebUITabGroupIdDataType[] =
+inline constexpr char16_t kWebUITabIdDataType[] =
+    u"application/vnd.chromium.tab";
+inline constexpr char16_t kWebUITabGroupIdDataType[] =
     u"application/vnd.chromium.tabgroup";
 
 class TabStripUI;
diff --git a/chrome/browser/vr/vr_base_export.h b/chrome/browser/vr/vr_base_export.h
index cf5ae11..ed5a94b2 100644
--- a/chrome/browser/vr/vr_base_export.h
+++ b/chrome/browser/vr/vr_base_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(VR_BASE_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(VR_BASE_IMPLEMENTATION)
 #define VR_BASE_EXPORT __attribute__((visibility("default")))
-#else
-#define VR_BASE_EXPORT
-#endif  // defined(VR_BASE_IMPLEMENTATION)
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/chrome/browser/vr/vr_export.h b/chrome/browser/vr/vr_export.h
index 32cb17b6..1f046359 100644
--- a/chrome/browser/vr/vr_export.h
+++ b/chrome/browser/vr/vr_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(VR_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(VR_IMPLEMENTATION)
 #define VR_EXPORT __attribute__((visibility("default")))
-#else
-#define VR_EXPORT
-#endif  // defined(VR_IMPLEMENTATION)
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/chrome/browser/vr/vr_ui_export.h b/chrome/browser/vr/vr_ui_export.h
index 23e591a9..9a35ec6 100644
--- a/chrome/browser/vr/vr_ui_export.h
+++ b/chrome/browser/vr/vr_ui_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(VR_UI_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(VR_UI_IMPLEMENTATION)
 #define VR_UI_EXPORT __attribute__((visibility("default")))
-#else
-#define VR_UI_EXPORT
-#endif  // defined(VR_UI_IMPLEMENTATION)
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/chrome/browser/web_applications/BUILD.gn b/chrome/browser/web_applications/BUILD.gn
index f2cc5ec..aff5551 100644
--- a/chrome/browser/web_applications/BUILD.gn
+++ b/chrome/browser/web_applications/BUILD.gn
@@ -394,6 +394,8 @@
       "commands/install_app_from_verified_manifest_command.h",
       "isolated_web_apps/commands/cleanup_bundle_cache_command.cc",
       "isolated_web_apps/commands/cleanup_bundle_cache_command.h",
+      "isolated_web_apps/commands/copy_bundle_to_cache_command.cc",
+      "isolated_web_apps/commands/copy_bundle_to_cache_command.h",
       "isolated_web_apps/policy/isolated_web_app_cache_client.cc",
       "isolated_web_apps/policy/isolated_web_app_cache_client.h",
       "isolated_web_apps/policy/isolated_web_app_cache_manager.cc",
@@ -982,6 +984,7 @@
     sources += [
       "chromeos_web_app_experiments_unittest.cc",
       "isolated_web_apps/commands/cleanup_bundle_cache_command_unittest.cc",
+      "isolated_web_apps/commands/copy_bundle_to_cache_command_unittest.cc",
       "isolated_web_apps/policy/isolated_web_app_cache_client_unittest.cc",
       "os_integration/web_app_run_on_os_login_chromeos_unittest.cc",
       "web_app_run_on_os_login_manager_unittest.cc",
diff --git a/chrome/browser/web_applications/isolated_web_apps/commands/cleanup_bundle_cache_command_unittest.cc b/chrome/browser/web_applications/isolated_web_apps/commands/cleanup_bundle_cache_command_unittest.cc
index 81c80c21..0ecd64c 100644
--- a/chrome/browser/web_applications/isolated_web_apps/commands/cleanup_bundle_cache_command_unittest.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/commands/cleanup_bundle_cache_command_unittest.cc
@@ -17,7 +17,6 @@
 #include "chrome/browser/web_applications/test/web_app_test.h"
 #include "chrome/browser/web_applications/web_app_command_scheduler.h"
 #include "chrome/common/chrome_features.h"
-#include "components/user_manager/fake_user_manager.h"
 #include "components/web_package/signed_web_bundles/signed_web_bundle_id.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -72,7 +71,7 @@
     EXPECT_TRUE(base::CreateTemporaryFileInDir(CacheRootPath(), &temp_file));
 
     base::FilePath bundle_path =
-        bundle_directory_path.AppendASCII(kMainSwbnFileName);
+        IwaCacheClient::GetBundleFullName(bundle_directory_path);
     EXPECT_TRUE(base::CopyFile(temp_file, bundle_path));
     return bundle_path;
   }
@@ -92,8 +91,11 @@
   base::FilePath GetBundleDirWithVersion(const SignedWebBundleId& bundle_id,
                                          const base::Version& version,
                                          SessionType session_type) {
-    return GetBundleDirForSession(bundle_id, session_type)
-        .AppendASCII(version.GetString());
+    auto session_cache_dir =
+        IwaCacheClient::GetCacheBaseDirectoryForSessionType(session_type,
+                                                            CacheRootPath());
+    return IwaCacheClient::GetCacheDirectoryForBundleWithVersion(
+        session_cache_dir, bundle_id, version);
   }
 
   void ScheduleCommand(
@@ -114,7 +116,6 @@
  private:
   base::test::ScopedFeatureList scoped_feature_list_{
       features::kIsolatedWebAppBundleCache};
-  user_manager::ScopedUserManager user_manager_;
   base::ScopedTempDir cache_root_dir_;
   std::unique_ptr<base::ScopedPathOverride> cache_root_dir_override_;
 };
diff --git a/chrome/browser/web_applications/isolated_web_apps/commands/copy_bundle_to_cache_command.cc b/chrome/browser/web_applications/isolated_web_apps/commands/copy_bundle_to_cache_command.cc
new file mode 100644
index 0000000..d7d60bd
--- /dev/null
+++ b/chrome/browser/web_applications/isolated_web_apps/commands/copy_bundle_to_cache_command.cc
@@ -0,0 +1,152 @@
+// 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/web_applications/isolated_web_apps/commands/copy_bundle_to_cache_command.h"
+
+#include "base/files/file.h"
+#include "base/files/file_util.h"
+#include "base/task/thread_pool.h"
+#include "base/types/expected.h"
+#include "base/version.h"
+#include "chrome/browser/web_applications/commands/web_app_command.h"
+#include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_info.h"
+#include "chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_cache_client.h"
+#include "chrome/browser/web_applications/locks/app_lock.h"
+#include "chrome/browser/web_applications/web_app.h"
+#include "chrome/browser/web_applications/web_app_registrar.h"
+
+namespace web_app {
+namespace {
+
+using SessionType = IwaCacheClient::SessionType;
+
+// This function is blocking. It should be called by
+// `CopyBundleToCacheCommand::StartWithLock`.
+CopyBundleToCacheResult CopyBundleToCacheCommandImpl(
+    const base::FilePath& copy_from_bundle_path,
+    const web_package::SignedWebBundleId& web_bundle_id,
+    base::Version version,
+    SessionType session_type) {
+  const base::FilePath cache_dir =
+      IwaCacheClient::GetCacheBaseDirectoryForSessionType(session_type);
+  base::FilePath bundle_dir_with_version =
+      IwaCacheClient::GetCacheDirectoryForBundleWithVersion(
+          cache_dir, web_bundle_id, version);
+  if (base::File::Error error;
+      !base::CreateDirectoryAndGetError(bundle_dir_with_version, &error)) {
+    LOG(ERROR) << "Failed to create IWA cache directory with path: "
+               << bundle_dir_with_version << ", error: " << error;
+    return base::unexpected(CopyBundleToCacheError::kFailedToCreateDir);
+  }
+
+  const base::FilePath destination_bundle_path =
+      IwaCacheClient::GetBundleFullName(bundle_dir_with_version);
+  if (!base::CopyFile(copy_from_bundle_path, destination_bundle_path)) {
+    base::DeleteFile(destination_bundle_path);
+    LOG(ERROR) << "Failed to copy IWA bundle to cache, destination path: "
+               << destination_bundle_path;
+    return base::unexpected(CopyBundleToCacheError::kFailedToCopyFile);
+  }
+
+  return CopyBundleToCacheSuccess(destination_bundle_path);
+}
+
+// Returns bundle path for owned bundle, otherwise returns std::nullopt.
+std::optional<base::FilePath> GetOwnedBundlePath(
+    const IsolatedWebAppStorageLocation& location,
+    Profile& profile) {
+  const auto* owned_bundle =
+      std::get_if<IsolatedWebAppStorageLocation::OwnedBundle>(
+          &location.variant());
+  if (!owned_bundle) {
+    return std::nullopt;
+  }
+  return owned_bundle->GetPath(profile.GetPath());
+}
+
+}  // namespace
+
+std::string CopyBundleToCacheErrorToString(CopyBundleToCacheError error) {
+  switch (error) {
+    case CopyBundleToCacheError::kSystemShutdown:
+      return "System shutdown";
+    case CopyBundleToCacheError::kAppNotInstalled:
+      return "IWA is not installed";
+    case CopyBundleToCacheError::kNotIwa:
+      return "App is not IWA";
+    case CopyBundleToCacheError::kCannotExtractOwnedBundlePath:
+      return "Cannot extract owned bundle path";
+    case CopyBundleToCacheError::kFailedToCreateDir:
+      return "Failed to create directory";
+    case CopyBundleToCacheError::kFailedToCopyFile:
+      return "Failed to copy file";
+  }
+}
+
+CopyBundleToCacheCommand::CopyBundleToCacheCommand(
+    const IsolatedWebAppUrlInfo& url_info,
+    SessionType session_type,
+    Profile& profile,
+    Callback callback)
+    : WebAppCommand<AppLock, CopyBundleToCacheResult>(
+          "CopyBundleToCacheCommand",
+          AppLockDescription(url_info.app_id()),
+          std::move(callback),
+          /*args_for_shutdown=*/
+          base::unexpected(
+              CopyBundleToCacheError{CopyBundleToCacheError::kSystemShutdown})),
+      url_info_(url_info),
+      session_type_(session_type),
+      profile_(profile) {}
+
+CopyBundleToCacheCommand::~CopyBundleToCacheCommand() = default;
+
+void CopyBundleToCacheCommand::StartWithLock(std::unique_ptr<AppLock> lock) {
+  CHECK(lock);
+  lock_ = std::move(lock);
+
+  const WebApp* app = lock_->registrar().GetAppById(url_info_.app_id());
+  if (!app) {
+    CommandComplete(base::unexpected(
+        CopyBundleToCacheError{CopyBundleToCacheError::kAppNotInstalled}));
+    return;
+  }
+  if (!app->isolation_data()) {
+    CommandComplete(base::unexpected(
+        CopyBundleToCacheError{CopyBundleToCacheError::kNotIwa}));
+    return;
+  }
+
+  // Only copies owned bundles, since all policy-installed IWAs have owned
+  // bundles.
+  std::optional<base::FilePath> bundle_path =
+      GetOwnedBundlePath(app->isolation_data()->location(), *profile_);
+  if (bundle_path->empty()) {
+    CommandComplete(base::unexpected(CopyBundleToCacheError{
+        CopyBundleToCacheError::kCannotExtractOwnedBundlePath}));
+    return;
+  }
+
+  base::ThreadPool::PostTaskAndReplyWithResult(
+      FROM_HERE,
+      {base::MayBlock(), base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
+      base::BindOnce(&CopyBundleToCacheCommandImpl, bundle_path.value(),
+                     url_info_.web_bundle_id(),
+                     app->isolation_data()->version(), session_type_),
+      base::BindOnce(&CopyBundleToCacheCommand::CommandComplete,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void CopyBundleToCacheCommand::CommandComplete(
+    const CopyBundleToCacheResult& result) {
+  if (!result.has_value()) {
+    LOG(ERROR) << "Copy IWA bundle to cache failed: "
+               << CopyBundleToCacheErrorToString(result.error());
+  }
+  CompleteAndSelfDestruct(
+      result.has_value() ? CommandResult::kSuccess : CommandResult::kFailure,
+      result);
+}
+
+}  // namespace web_app
diff --git a/chrome/browser/web_applications/isolated_web_apps/commands/copy_bundle_to_cache_command.h b/chrome/browser/web_applications/isolated_web_apps/commands/copy_bundle_to_cache_command.h
new file mode 100644
index 0000000..b9f3a2d
--- /dev/null
+++ b/chrome/browser/web_applications/isolated_web_apps/commands/copy_bundle_to_cache_command.h
@@ -0,0 +1,84 @@
+// 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_WEB_APPLICATIONS_ISOLATED_WEB_APPS_COMMANDS_COPY_BUNDLE_TO_CACHE_COMMAND_H_
+#define CHROME_BROWSER_WEB_APPLICATIONS_ISOLATED_WEB_APPS_COMMANDS_COPY_BUNDLE_TO_CACHE_COMMAND_H_
+
+#include "base/types/expected.h"
+#include "base/version.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/web_applications/commands/web_app_command.h"
+#include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_info.h"
+#include "chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_cache_client.h"
+#include "chrome/browser/web_applications/locks/app_lock.h"
+#include "components/web_package/signed_web_bundles/signed_web_bundle_id.h"
+
+namespace web_app {
+
+class CopyBundleToCacheSuccess {
+ public:
+  explicit CopyBundleToCacheSuccess(const base::FilePath& cached_bundle_path)
+      : cached_bundle_path_(cached_bundle_path) {}
+
+  CopyBundleToCacheSuccess(const CopyBundleToCacheSuccess& other) = default;
+  ~CopyBundleToCacheSuccess() = default;
+
+  bool operator==(const CopyBundleToCacheSuccess& other) const = default;
+
+  const base::FilePath& cached_bundle_path() const {
+    return cached_bundle_path_;
+  }
+
+ private:
+  base::FilePath cached_bundle_path_;
+};
+
+enum class CopyBundleToCacheError {
+  kSystemShutdown,
+  kAppNotInstalled,
+  kNotIwa,
+  kCannotExtractOwnedBundlePath,
+  kFailedToCreateDir,
+  kFailedToCopyFile,
+};
+
+std::string CopyBundleToCacheErrorToString(CopyBundleToCacheError error);
+
+using CopyBundleToCacheResult =
+    base::expected<CopyBundleToCacheSuccess, CopyBundleToCacheError>;
+
+// Copies IWA bundle file to the cache. To prevent race conditions with other
+// cache operations, this class takes `AppLock` for the bundle.
+class CopyBundleToCacheCommand
+    : public WebAppCommand<AppLock, CopyBundleToCacheResult> {
+ public:
+  using Callback = base::OnceCallback<void(CopyBundleToCacheResult)>;
+
+  CopyBundleToCacheCommand(const IsolatedWebAppUrlInfo& url_info,
+                           IwaCacheClient::SessionType session_type,
+                           Profile& profile,
+                           Callback callback);
+  CopyBundleToCacheCommand(const CopyBundleToCacheCommand&) = delete;
+  CopyBundleToCacheCommand& operator=(const CopyBundleToCacheCommand&) = delete;
+
+  ~CopyBundleToCacheCommand() override;
+
+ protected:
+  // WebAppCommand:
+  void StartWithLock(std::unique_ptr<AppLock> lock) override;
+
+ private:
+  void CommandComplete(const CopyBundleToCacheResult& result);
+
+  std::unique_ptr<AppLock> lock_;
+  const IsolatedWebAppUrlInfo url_info_;
+  const IwaCacheClient::SessionType session_type_;
+  const raw_ref<Profile> profile_;
+
+  base::WeakPtrFactory<CopyBundleToCacheCommand> weak_ptr_factory_{this};
+};
+
+}  // namespace web_app
+
+#endif  // CHROME_BROWSER_WEB_APPLICATIONS_ISOLATED_WEB_APPS_COMMANDS_COPY_BUNDLE_TO_CACHE_COMMAND_H_
diff --git a/chrome/browser/web_applications/isolated_web_apps/commands/copy_bundle_to_cache_command_unittest.cc b/chrome/browser/web_applications/isolated_web_apps/commands/copy_bundle_to_cache_command_unittest.cc
new file mode 100644
index 0000000..68dc415
--- /dev/null
+++ b/chrome/browser/web_applications/isolated_web_apps/commands/copy_bundle_to_cache_command_unittest.cc
@@ -0,0 +1,224 @@
+// 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/web_applications/isolated_web_apps/commands/copy_bundle_to_cache_command.h"
+
+#include "ash/constants/ash_paths.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/test/gmock_expected_support.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/test/scoped_path_override.h"
+#include "base/test/test_future.h"
+#include "base/version.h"
+#include "chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_cache_client.h"
+#include "chrome/browser/web_applications/isolated_web_apps/test/isolated_web_app_builder.h"
+#include "chrome/browser/web_applications/isolated_web_apps/test/test_signed_web_bundle_builder.h"
+#include "chrome/browser/web_applications/test/fake_web_app_provider.h"
+#include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
+#include "chrome/browser/web_applications/test/web_app_test.h"
+#include "chrome/browser/web_applications/web_app_command_scheduler.h"
+#include "chrome/common/chrome_features.h"
+#include "components/web_package/signed_web_bundles/signed_web_bundle_id.h"
+#include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace web_app {
+
+namespace {
+
+using base::test::ErrorIs;
+using base::test::HasValue;
+using base::test::TestFuture;
+using base::test::ValueIs;
+using web_package::SignedWebBundleId;
+using SessionType = IwaCacheClient::SessionType;
+
+const SignedWebBundleId kBundleId = test::GetDefaultEd25519WebBundleId();
+const web_package::test::Ed25519KeyPair kPublicKeyPair =
+    test::GetDefaultEd25519KeyPair();
+const base::Version kVersion1 = base::Version("0.0.1");
+const base::Version kVersion2 = base::Version("2.0.0");
+
+}  // namespace
+
+// TODO(crbug.com/414793394): Reduce code duplications for cache command unit
+// tests.
+class CopyBundleToCacheCommandTest
+    : public WebAppTest,
+      public testing::WithParamInterface<SessionType> {
+ public:
+  void SetUp() override {
+    WebAppTest::SetUp();
+    test::AwaitStartWebAppProviderAndSubsystems(profile());
+
+    ASSERT_TRUE(cache_root_dir_.CreateUniqueTempDir());
+    cache_root_dir_override_ = std::make_unique<base::ScopedPathOverride>(
+        ash::DIR_DEVICE_LOCAL_ACCOUNT_IWA_CACHE, cache_root_dir_.GetPath());
+  }
+
+  base::FilePath CreateBundleInCacheDir(const SignedWebBundleId& bundle_id,
+                                        const base::Version& version) {
+    base::FilePath bundle_directory_path =
+        GetBundleDirWithVersion(bundle_id, version);
+    EXPECT_TRUE(base::CreateDirectory(bundle_directory_path));
+
+    base::FilePath temp_file;
+    EXPECT_TRUE(base::CreateTemporaryFileInDir(CacheRootPath(), &temp_file));
+    base::FilePath bundle_path =
+        IwaCacheClient::GetBundleFullName(bundle_directory_path);
+    EXPECT_TRUE(base::CopyFile(temp_file, bundle_path));
+    return bundle_path;
+  }
+
+  base::FilePath GetBundleDirWithVersion(const SignedWebBundleId& bundle_id,
+                                         const base::Version& version) {
+    auto session_cache_dir =
+        IwaCacheClient::GetCacheBaseDirectoryForSessionType(GetSessionType(),
+                                                            CacheRootPath());
+    return IwaCacheClient::GetCacheDirectoryForBundleWithVersion(
+        session_cache_dir, bundle_id, version);
+  }
+
+  base::FilePath GetBundleFullPath(const SignedWebBundleId& bundle_id,
+                                   const base::Version& version) {
+    return IwaCacheClient::GetBundleFullName(
+        GetBundleDirWithVersion(bundle_id, version));
+  }
+
+  const base::FilePath& CacheRootPath() { return cache_root_dir_.GetPath(); }
+
+  void ScheduleCommand(
+      const web_package::SignedWebBundleId& web_bundle_id,
+      base::OnceCallback<void(CopyBundleToCacheResult)> callback) {
+    auto url_info =
+        IsolatedWebAppUrlInfo::CreateFromSignedWebBundleId(web_bundle_id);
+    fake_provider().scheduler().CopyIsolatedWebAppBundleToCache(
+        url_info, GetSessionType(), std::move(callback));
+  }
+
+  void RestrictDirectoryPermission(const base::FilePath& dir) {
+    // Allows to check that file exists, but disallows to change it.
+    EXPECT_TRUE(
+        SetPosixFilePermissions(dir, base::FILE_PERMISSION_EXECUTE_BY_USER));
+  }
+
+  std::unique_ptr<BundledIsolatedWebApp> CreateApp(const std::string& version) {
+    base::FilePath bundle_path =
+        IwaStorageOwnedBundle{"bundle-" + version, /*dev_mode=*/false}.GetPath(
+            profile()->GetPath());
+    EXPECT_TRUE(base::CreateDirectory(bundle_path.DirName()));
+
+    std::unique_ptr<BundledIsolatedWebApp> app =
+        IsolatedWebAppBuilder(ManifestBuilder().SetVersion(version))
+            .BuildBundle(bundle_path, kPublicKeyPair);
+    app->TrustSigningKey();
+    app->FakeInstallPageState(profile());
+    return app;
+  }
+
+  SessionType GetSessionType() { return GetParam(); }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_{
+      features::kIsolatedWebAppBundleCache};
+  base::ScopedTempDir cache_root_dir_;
+  std::unique_ptr<base::ScopedPathOverride> cache_root_dir_override_;
+  data_decoder::test::InProcessDataDecoder in_process_data_decoder_;
+};
+
+TEST_P(CopyBundleToCacheCommandTest, CopyBundleToCache) {
+  std::unique_ptr<BundledIsolatedWebApp> app = CreateApp(kVersion1.GetString());
+  ASSERT_THAT(app->Install(profile()), HasValue());
+
+  TestFuture<CopyBundleToCacheResult> copy_future;
+  ScheduleCommand(kBundleId, copy_future.GetCallback());
+
+  base::FilePath bundle_path = GetBundleFullPath(kBundleId, kVersion1);
+  EXPECT_THAT(copy_future.Get(),
+              ValueIs(CopyBundleToCacheSuccess{bundle_path}));
+  EXPECT_TRUE(base::PathExists(bundle_path));
+}
+
+TEST_P(CopyBundleToCacheCommandTest, AppNotInstalled) {
+  TestFuture<CopyBundleToCacheResult> copy_future;
+  ScheduleCommand(kBundleId, copy_future.GetCallback());
+
+  EXPECT_THAT(copy_future.Get(),
+              ErrorIs(CopyBundleToCacheError::kAppNotInstalled));
+}
+
+TEST_P(CopyBundleToCacheCommandTest, FailedToCreateDir) {
+  std::unique_ptr<BundledIsolatedWebApp> app = CreateApp(kVersion1.GetString());
+  ASSERT_THAT(app->Install(profile()), HasValue());
+
+  // Restricts cache root directory permissions, so copy to that directory will
+  // fail.
+  RestrictDirectoryPermission(CacheRootPath());
+
+  TestFuture<CopyBundleToCacheResult> copy_future;
+  ScheduleCommand(kBundleId, copy_future.GetCallback());
+
+  EXPECT_THAT(copy_future.Get(),
+              ErrorIs(CopyBundleToCacheError::kFailedToCreateDir));
+}
+
+TEST_P(CopyBundleToCacheCommandTest, FailedToCopyFile) {
+  std::unique_ptr<BundledIsolatedWebApp> app = CreateApp(kVersion1.GetString());
+  ASSERT_THAT(app->Install(profile()), HasValue());
+
+  // Bundle directory is already created, but restricted, so copy to that
+  // directory will fail.
+  base::FilePath bundle_directory_path =
+      GetBundleDirWithVersion(kBundleId, kVersion1);
+  EXPECT_TRUE(base::CreateDirectory(bundle_directory_path));
+  RestrictDirectoryPermission(bundle_directory_path);
+
+  TestFuture<CopyBundleToCacheResult> copy_future;
+  ScheduleCommand(kBundleId, copy_future.GetCallback());
+
+  EXPECT_THAT(copy_future.Get(),
+              ErrorIs(CopyBundleToCacheError::kFailedToCopyFile));
+}
+
+TEST_P(CopyBundleToCacheCommandTest, CopyBundleToCacheReplacesExistingFile) {
+  base::FilePath existing_bundle = CreateBundleInCacheDir(kBundleId, kVersion1);
+  std::unique_ptr<BundledIsolatedWebApp> app = CreateApp(kVersion1.GetString());
+  ASSERT_THAT(app->Install(profile()), HasValue());
+
+  TestFuture<CopyBundleToCacheResult> copy_future;
+  ScheduleCommand(kBundleId, copy_future.GetCallback());
+
+  base::FilePath bundle_path = GetBundleFullPath(kBundleId, kVersion1);
+  EXPECT_THAT(copy_future.Get(),
+              ValueIs(CopyBundleToCacheSuccess{bundle_path}));
+  EXPECT_TRUE(base::PathExists(bundle_path));
+}
+
+TEST_P(CopyBundleToCacheCommandTest, CopyAnotherBundleVersion) {
+  base::FilePath existing_bundle_path =
+      CreateBundleInCacheDir(kBundleId, kVersion1);
+  std::unique_ptr<BundledIsolatedWebApp> app = CreateApp(kVersion2.GetString());
+  ASSERT_THAT(app->Install(profile()), HasValue());
+
+  TestFuture<CopyBundleToCacheResult> copy_future;
+  ScheduleCommand(kBundleId, copy_future.GetCallback());
+
+  base::FilePath updated_bundle_path = GetBundleFullPath(kBundleId, kVersion2);
+  EXPECT_THAT(copy_future.Get(),
+              ValueIs(CopyBundleToCacheSuccess{updated_bundle_path}));
+  // Check that both versions are cached.
+  EXPECT_TRUE(base::PathExists(existing_bundle_path));
+  EXPECT_TRUE(base::PathExists(updated_bundle_path));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    /* no prefix */,
+    CopyBundleToCacheCommandTest,
+    testing::Values(IwaCacheClient::SessionType::kManagedGuestSession,
+                    IwaCacheClient::SessionType::kKiosk));
+
+}  // namespace web_app
diff --git a/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_update_apply_task.cc b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_update_apply_task.cc
index d8bb9ed..832af36 100644
--- a/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_update_apply_task.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_update_apply_task.cc
@@ -29,6 +29,7 @@
 #include "components/keep_alive_registry/scoped_keep_alive.h"
 
 #if BUILDFLAG(IS_CHROMEOS)
+#include "chrome/browser/web_applications/isolated_web_apps/commands/copy_bundle_to_cache_command.h"
 #include "chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_cache_client.h"
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
@@ -38,21 +39,6 @@
 
 #if BUILDFLAG(IS_CHROMEOS)
 constexpr char kCopyToCacheResult[] = "copy_to_cache_result";
-constexpr char kCannotExtractBundlePath[] = "Cannot extract bundle path";
-
-// Returns bundle path for owned bundle, otherwise returns std::nullopt.
-std::optional<base::FilePath> GetOwnedBundlePath(
-    const IsolatedWebAppStorageLocation& location,
-    Profile& profile) {
-  const auto* owned_bundle =
-      std::get_if<IsolatedWebAppStorageLocation::OwnedBundle>(
-          &location.variant());
-  if (!owned_bundle) {
-    return std::nullopt;
-  }
-  return owned_bundle->GetPath(profile.GetPath());
-}
-
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
 }  // namespace
@@ -120,37 +106,24 @@
 #if BUILDFLAG(IS_CHROMEOS)
 void IsolatedWebAppUpdateApplyTask::CopyUpdatedBundleToCache(
     const IsolatedWebAppApplyUpdateCommandSuccess& apply_success_result) {
-  const auto bundle_path =
-      GetOwnedBundlePath(apply_success_result.updated_location(), *profile_);
-  if (!bundle_path) {
-    debug_log_.Set(kCopyToCacheResult, kCannotExtractBundlePath);
-    std::move(callback_).Run(
-        base::unexpected<IsolatedWebAppApplyUpdateCommandError>(
-            IsolatedWebAppApplyUpdateCommandError{
-                .message = kCannotExtractBundlePath}));
-    return;
-  }
-
-  cache_client_->CopyBundleToCache(
-      bundle_path.value(), url_info_.web_bundle_id(),
-      apply_success_result.updated_version(),
+  command_scheduler_->CopyIsolatedWebAppBundleToCache(
+      url_info_, IwaCacheClient::GetCurrentSessionType(),
       base::BindOnce(&IsolatedWebAppUpdateApplyTask::OnBundleCopiedToCache,
                      weak_factory_.GetWeakPtr(), apply_success_result));
 }
 
 void IsolatedWebAppUpdateApplyTask::OnBundleCopiedToCache(
     const IsolatedWebAppApplyUpdateCommandSuccess& apply_success_result,
-    base::expected<IwaCacheClient::CopyBundleToCacheSuccess,
-                   IwaCacheClient::CopyBundleToCacheError> result) {
+    CopyBundleToCacheResult result) {
   if (result.has_value()) {
     debug_log_.Set(kCopyToCacheResult,
                    "Successfully copied bundle to: " +
-                       result->cached_bundle_path.MaybeAsASCII());
+                       result->cached_bundle_path().MaybeAsASCII());
     std::move(callback_).Run(apply_success_result);
     return;
   }
   debug_log_.Set(kCopyToCacheResult,
-                 IwaCacheClient::CopyErrorToString(result.error()));
+                 CopyBundleToCacheErrorToString(result.error()));
   std::move(callback_).Run(
       base::unexpected<IsolatedWebAppApplyUpdateCommandError>(
           IsolatedWebAppApplyUpdateCommandError{
diff --git a/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_update_apply_task.h b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_update_apply_task.h
index 4e1ef94..80e2809 100644
--- a/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_update_apply_task.h
+++ b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_update_apply_task.h
@@ -16,6 +16,7 @@
 #include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_info.h"
 
 #if BUILDFLAG(IS_CHROMEOS)
+#include "chrome/browser/web_applications/isolated_web_apps/commands/copy_bundle_to_cache_command.h"
 #include "chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_cache_client.h"
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
@@ -71,8 +72,7 @@
 
   void OnBundleCopiedToCache(
       const IsolatedWebAppApplyUpdateCommandSuccess& apply_success_result,
-      base::expected<IwaCacheClient::CopyBundleToCacheSuccess,
-                     IwaCacheClient::CopyBundleToCacheError> result);
+      CopyBundleToCacheResult result);
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
   IsolatedWebAppUrlInfo url_info_;
diff --git a/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_cache_browsertest.cc b/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_cache_browsertest.cc
index f96a27c..85060e1 100644
--- a/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_cache_browsertest.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_cache_browsertest.cc
@@ -45,7 +45,7 @@
 #include "chrome/common/chrome_features.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "chromeos/ash/components/dbus/session_manager/fake_session_manager_client.h"
-#include "components/policy/core/common/device_local_account_type.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "components/policy/proto/chrome_device_policy.pb.h"
 #include "components/web_package/signed_web_bundles/signed_web_bundle_id.h"
 #include "components/web_package/test_support/signed_web_bundles/ed25519_key_pair.h"
diff --git a/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_cache_client.cc b/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_cache_client.cc
index f27c27d5..73b4d88b 100644
--- a/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_cache_client.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_cache_client.cc
@@ -32,25 +32,13 @@
 
 using SessionType = IwaCacheClient::SessionType;
 
-base::FilePath GetCacheBundleDirectoryWithVersion(
-    const base::FilePath& cache_dir,
-    const web_package::SignedWebBundleId& web_bundle_id,
-    const base::Version& version) {
-  return IwaCacheClient::GetCacheDirectoryForBundle(cache_dir, web_bundle_id)
-      .AppendASCII(version.GetString());
-}
-
-base::FilePath GetBundleFullName(
-    const base::FilePath& bundle_dir_with_version) {
-  return bundle_dir_with_version.AppendASCII(kMainSwbnFileName);
-}
-
 base::FilePath GetBundleFullName(
     const base::FilePath& cache_dir,
     const web_package::SignedWebBundleId& web_bundle_id,
     const base::Version& version) {
-  return GetBundleFullName(
-      GetCacheBundleDirectoryWithVersion(cache_dir, web_bundle_id, version));
+  return IwaCacheClient::GetBundleFullName(
+      IwaCacheClient::GetCacheDirectoryForBundleWithVersion(
+          cache_dir, web_bundle_id, version));
 }
 
 // Expects the following bundle path:
@@ -117,36 +105,6 @@
       std::move(newest_version.value()));
 }
 
-// This function is blocking. It should be called by `CopyBundleToCache`.
-base::expected<IwaCacheClient::CopyBundleToCacheSuccess,
-               IwaCacheClient::CopyBundleToCacheError>
-CopyBundleToCacheImpl(const base::FilePath& copy_from_bundle_path,
-                      const web_package::SignedWebBundleId& web_bundle_id,
-                      base::Version version,
-                      const base::FilePath& cache_dir) {
-  base::FilePath bundle_dir_with_version =
-      GetCacheBundleDirectoryWithVersion(cache_dir, web_bundle_id, version);
-  if (base::File::Error error;
-      !base::CreateDirectoryAndGetError(bundle_dir_with_version, &error)) {
-    LOG(ERROR) << "Failed to create IWA cache directory with path: "
-               << bundle_dir_with_version << ", error: " << error;
-    return base::unexpected(
-        IwaCacheClient::CopyBundleToCacheError::kFailedToCreateDir);
-  }
-
-  const base::FilePath destination_bundle_path =
-      GetBundleFullName(bundle_dir_with_version);
-  if (!base::CopyFile(copy_from_bundle_path, destination_bundle_path)) {
-    base::DeleteFile(destination_bundle_path);
-    LOG(ERROR) << "Failed to copy IWA bundle to cache, destination path: "
-               << destination_bundle_path;
-    return base::unexpected(
-        IwaCacheClient::CopyBundleToCacheError::kFailedToCopyFile);
-  }
-
-  return IwaCacheClient::CopyBundleToCacheSuccess(destination_bundle_path);
-}
-
 base::FilePath GetIwaCacheDirectoryForCurrentSession(
     const base::FilePath& base = base::PathService::CheckedGet(
         ash::DIR_DEVICE_LOCAL_ACCOUNT_IWA_CACHE)) {
@@ -167,21 +125,16 @@
          (chromeos::IsManagedGuestSession() || chromeos::IsKioskSession());
 }
 
-base::FilePath GetCacheBundleDirectory(
-    const base::FilePath& main_cache_dir,
-    const web_package::SignedWebBundleId& web_bundle_id) {
-  return main_cache_dir.AppendASCII(web_bundle_id.id());
-}
-
 // static
-std::string IwaCacheClient::CopyErrorToString(
-    IwaCacheClient::CopyBundleToCacheError error) {
-  switch (error) {
-    case IwaCacheClient::CopyBundleToCacheError::kFailedToCreateDir:
-      return "FailedToCreateDir";
-    case IwaCacheClient::CopyBundleToCacheError::kFailedToCopyFile:
-      return "FailedToCopyFile";
+SessionType IwaCacheClient::GetCurrentSessionType() {
+  if (chromeos::IsKioskSession()) {
+    return SessionType::kKiosk;
   }
+  if (chromeos::IsManagedGuestSession()) {
+    return SessionType::kManagedGuestSession;
+  }
+  NOTREACHED()
+      << "IwaCacheClient supports only kiosk and Managed Guest Session.";
 }
 
 IwaCacheClient::IwaCacheClient()
@@ -201,19 +154,6 @@
       std::move(callback));
 }
 
-void IwaCacheClient::CopyBundleToCache(
-    const base::FilePath& copy_from_bundle_path,
-    const web_package::SignedWebBundleId& web_bundle_id,
-    const base::Version& version,
-    base::OnceCallback<void(base::expected<CopyBundleToCacheSuccess,
-                                           CopyBundleToCacheError>)> callback) {
-  base::ThreadPool::PostTaskAndReplyWithResult(
-      FROM_HERE, {base::MayBlock()},
-      base::BindOnce(&CopyBundleToCacheImpl, copy_from_bundle_path,
-                     web_bundle_id, version, cache_dir_),
-      std::move(callback));
-}
-
 void IwaCacheClient::SetCacheDirForTesting(const base::FilePath& cache_dir) {
   cache_dir_ = GetIwaCacheDirectoryForCurrentSession(cache_dir);
 }
@@ -242,6 +182,22 @@
 }
 
 // static
+base::FilePath IwaCacheClient::GetCacheDirectoryForBundleWithVersion(
+    const base::FilePath& cache_base_dir,
+    const web_package::SignedWebBundleId& web_bundle_id,
+    const base::Version& version) {
+  return IwaCacheClient::GetCacheDirectoryForBundle(cache_base_dir,
+                                                    web_bundle_id)
+      .AppendASCII(version.GetString());
+}
+
+// static
+base::FilePath IwaCacheClient::GetBundleFullName(
+    const base::FilePath& bundle_dir_with_version) {
+  return bundle_dir_with_version.AppendASCII(kMainSwbnFileName);
+}
+
+// static
 std::string IwaCacheClient::SessionTypeToString(SessionType session_type) {
   switch (session_type) {
     case SessionType::kKiosk:
diff --git a/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_cache_client.h b/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_cache_client.h
index a5139c1..e4b705a 100644
--- a/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_cache_client.h
+++ b/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_cache_client.h
@@ -32,17 +32,7 @@
     kManagedGuestSession,
   };
 
-  enum class CopyBundleToCacheError {
-    kFailedToCreateDir = 0,
-    kFailedToCopyFile = 1,
-  };
-
-  static std::string CopyErrorToString(
-      IwaCacheClient::CopyBundleToCacheError error);
-
-  struct CopyBundleToCacheSuccess {
-    base::FilePath cached_bundle_path;
-  };
+  static SessionType GetCurrentSessionType();
 
   struct CachedBundleData {
     base::FilePath path;
@@ -65,17 +55,6 @@
       const std::optional<base::Version>& version,
       base::OnceCallback<void(std::optional<CachedBundleData>)> callback);
 
-  // Copies bundle file to the cache, so next time the installation can be done
-  // from the cache.
-  // TODO(crbug.com/411116232): use AppLock to prevent race conditions.
-  void CopyBundleToCache(
-      const base::FilePath& copy_from_bundle_path,
-      const web_package::SignedWebBundleId& web_bundle_id,
-      const base::Version& version,
-      base::OnceCallback<void(
-          base::expected<CopyBundleToCacheSuccess, CopyBundleToCacheError>)>
-          callback);
-
   // TODO(crbug.com/392069400): clean cache for old IWA versions.
 
   void SetCacheDirForTesting(const base::FilePath& cache_dir);
@@ -89,6 +68,14 @@
       const base::FilePath& cache_base_dir,
       const web_package::SignedWebBundleId& web_bundle_id);
 
+  static base::FilePath GetCacheDirectoryForBundleWithVersion(
+      const base::FilePath& cache_dir,
+      const web_package::SignedWebBundleId& web_bundle_id,
+      const base::Version& version);
+
+  static base::FilePath GetBundleFullName(
+      const base::FilePath& bundle_dir_with_version);
+
   static std::string SessionTypeToString(SessionType session_type);
 
   static constexpr base::FilePath::CharType kMgsDirName[] = "mgs";
diff --git a/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_cache_client_unittest.cc b/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_cache_client_unittest.cc
index 1b52047..215cc72fc 100644
--- a/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_cache_client_unittest.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_cache_client_unittest.cc
@@ -36,8 +36,6 @@
 
 using base::test::TestFuture;
 using Bundle = IwaCacheClient::CachedBundleData;
-using CopyBundleToCacheSuccess = IwaCacheClient::CopyBundleToCacheSuccess;
-using CopyBundleToCacheError = IwaCacheClient::CopyBundleToCacheError;
 using base::test::ErrorIs;
 using base::test::ValueIs;
 using testing::Field;
@@ -90,8 +88,6 @@
 
   IwaCacheClient* cache_client() { return cache_client_.get(); }
 
-  const base::FilePath& CacheDirPath() { return cache_root_dir_.GetPath(); }
-
   base::FilePath CreateBundleInCacheDir(const SignedWebBundleId& bundle_id,
                                         const base::Version& version) {
     base::FilePath bundle_directory_path =
@@ -99,7 +95,7 @@
     EXPECT_TRUE(base::CreateDirectory(bundle_directory_path));
 
     base::FilePath temp_file;
-    EXPECT_TRUE(base::CreateTemporaryFileInDir(CacheDirPath(), &temp_file));
+    EXPECT_TRUE(base::CreateTemporaryFileInDir(CacheRootPath(), &temp_file));
 
     base::FilePath bundle_path =
         bundle_directory_path.AppendASCII(kMainSwbnFileName);
@@ -107,9 +103,10 @@
     return bundle_path;
   }
 
+ private:
   base::FilePath GetBundleDirWithVersion(const SignedWebBundleId& bundle_id,
                                          const base::Version& version) {
-    base::FilePath bundle_directory_path = CacheDirPath();
+    base::FilePath bundle_directory_path = CacheRootPath();
     switch (GetSessionType()) {
       case SessionType::kMgs:
         bundle_directory_path =
@@ -126,26 +123,8 @@
         .AppendASCII(version.GetString());
   }
 
-  base::FilePath GetFullBundlePath(const SignedWebBundleId& bundle_id,
-                                   const base::Version& version) {
-    return GetBundleDirWithVersion(bundle_id, version)
-        .AppendASCII(kMainSwbnFileName);
-  }
+  const base::FilePath& CacheRootPath() { return cache_root_dir_.GetPath(); }
 
-  base::FilePath CreateFileInDir(base::ScopedTempDir& dir) {
-    EXPECT_TRUE(dir.CreateUniqueTempDir());
-    base::FilePath temp_file;
-    EXPECT_TRUE(base::CreateTemporaryFileInDir(dir.GetPath(), &temp_file));
-    return temp_file;
-  }
-
-  void ResetCacheDir() {
-    cache_root_dir_override_.reset();
-    cache_client()->SetCacheDirForTesting(
-        base::PathService::CheckedGet(ash::DIR_DEVICE_LOCAL_ACCOUNT_IWA_CACHE));
-  }
-
- private:
   SessionType GetSessionType() { return GetParam(); }
 
   base::test::ScopedFeatureList scoped_feature_list_{
@@ -257,106 +236,6 @@
   EXPECT_FALSE(bundle_future.Get());
 }
 
-TEST_P(IwaCacheClientTest, CopyBundleToCache) {
-  base::ScopedTempDir original_file_dir;
-  base::FilePath original_file = CreateFileInDir(original_file_dir);
-
-  TestFuture<base::expected<CopyBundleToCacheSuccess, CopyBundleToCacheError>>
-      copy_future;
-  cache_client()->CopyBundleToCache(original_file, kBundleId, kVersion1,
-                                    copy_future.GetCallback());
-
-  base::FilePath bundle_path = GetFullBundlePath(kBundleId, kVersion1);
-  EXPECT_THAT(copy_future.Get(),
-              ValueIs(Field(&CopyBundleToCacheSuccess::cached_bundle_path,
-                            bundle_path)));
-  EXPECT_TRUE(base::PathExists(bundle_path));
-}
-
-TEST_P(IwaCacheClientTest, FailedToCreateDirForFileCopying) {
-  // Reset the cache directory back to the production value, since tests cannot
-  // create subdirectories there.
-  ResetCacheDir();
-
-  base::ScopedTempDir original_file_dir;
-  base::FilePath original_file = CreateFileInDir(original_file_dir);
-
-  TestFuture<base::expected<CopyBundleToCacheSuccess, CopyBundleToCacheError>>
-      copy_future;
-  cache_client()->CopyBundleToCache(original_file, kBundleId, kVersion1,
-                                    copy_future.GetCallback());
-
-  EXPECT_THAT(copy_future.Get(),
-              ErrorIs(CopyBundleToCacheError::kFailedToCreateDir));
-}
-
-TEST_P(IwaCacheClientTest, FailedToCopyFile) {
-  TestFuture<base::expected<CopyBundleToCacheSuccess, CopyBundleToCacheError>>
-      copy_future;
-  cache_client()->CopyBundleToCache(base::FilePath("/do/not/exist/file"),
-                                    kBundleId, kVersion1,
-                                    copy_future.GetCallback());
-
-  EXPECT_THAT(copy_future.Get(),
-              ErrorIs(CopyBundleToCacheError::kFailedToCopyFile));
-}
-
-TEST_P(IwaCacheClientTest, CopyAndGet) {
-  base::ScopedTempDir original_file_dir;
-  base::FilePath original_file = CreateFileInDir(original_file_dir);
-  TestFuture<base::expected<CopyBundleToCacheSuccess, CopyBundleToCacheError>>
-      copy_future;
-
-  cache_client()->CopyBundleToCache(original_file, kBundleId, kVersion1,
-                                    copy_future.GetCallback());
-  EXPECT_TRUE(copy_future.Get().has_value());
-
-  TestFuture<std::optional<Bundle>> get_future;
-  cache_client()->GetCacheFilePath(kBundleId, kVersion1,
-                                   get_future.GetCallback());
-
-  EXPECT_EQ(get_future.Get()->path, GetFullBundlePath(kBundleId, kVersion1));
-  EXPECT_EQ(get_future.Get()->version, kVersion1);
-}
-
-TEST_P(IwaCacheClientTest, CopyBundleToCacheReplacesExistingFile) {
-  base::ScopedTempDir original_file_dir;
-  base::FilePath original_file = CreateFileInDir(original_file_dir);
-
-  base::FilePath existing_bundle = CreateBundleInCacheDir(kBundleId, kVersion1);
-
-  TestFuture<base::expected<CopyBundleToCacheSuccess, CopyBundleToCacheError>>
-      copy_future;
-  cache_client()->CopyBundleToCache(original_file, kBundleId, kVersion1,
-                                    copy_future.GetCallback());
-
-  EXPECT_THAT(copy_future.Get(),
-              ValueIs(Field(&CopyBundleToCacheSuccess::cached_bundle_path,
-                            GetFullBundlePath(kBundleId, kVersion1))));
-}
-
-TEST_P(IwaCacheClientTest, CopyAnotherBundleVersion) {
-  base::ScopedTempDir original_file_dir;
-  base::FilePath original_file = CreateFileInDir(original_file_dir);
-
-  base::FilePath existing_bundle_path =
-      CreateBundleInCacheDir(kBundleId, kVersion1);
-
-  TestFuture<base::expected<CopyBundleToCacheSuccess, CopyBundleToCacheError>>
-      copy_future;
-  cache_client()->CopyBundleToCache(original_file, kBundleId, kVersion2,
-                                    copy_future.GetCallback());
-
-  base::FilePath updated_bundle_path = GetFullBundlePath(kBundleId, kVersion2);
-  EXPECT_THAT(copy_future.Get(),
-              ValueIs(Field(&CopyBundleToCacheSuccess::cached_bundle_path,
-                            updated_bundle_path)));
-
-  // Check that both versions are cached.
-  EXPECT_TRUE(base::PathExists(existing_bundle_path));
-  EXPECT_TRUE(base::PathExists(updated_bundle_path));
-}
-
 INSTANTIATE_TEST_SUITE_P(
     /* no prefix */,
     IwaCacheClientTest,
diff --git a/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_cache_manager.cc b/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_cache_manager.cc
index a5056f5..8405a28 100644
--- a/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_cache_manager.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_cache_manager.cc
@@ -30,17 +30,6 @@
 
 using SessionType = IwaCacheClient::SessionType;
 
-SessionType GetCurrentSessionType() {
-  if (chromeos::IsKioskSession()) {
-    return SessionType::kKiosk;
-  }
-  if (chromeos::IsManagedGuestSession()) {
-    return SessionType::kManagedGuestSession;
-  }
-  NOTREACHED() << "IwaCacheManager::Starts already checked the current session "
-               << "is kiosk or Managed Guest Session.";
-}
-
 std::vector<web_package::SignedWebBundleId> GetPolicyInstalledIwasForKiosk() {
   const std::vector<policy::DeviceLocalAccount> device_local_accounts =
       policy::GetDeviceLocalAccounts(ash::CrosSettings::Get());
@@ -104,7 +93,7 @@
 }
 
 void IwaBundleCacheManager::CleanCacheForIwasDeletedFromPolicy() {
-  SessionType session_type = GetCurrentSessionType();
+  SessionType session_type = IwaCacheClient::GetCurrentSessionType();
   std::vector<web_package::SignedWebBundleId> iwas_in_policy =
       GetIwasInPolicy(session_type, *profile_);
 
diff --git a/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_installer.cc b/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_installer.cc
index f5595739..e142792 100644
--- a/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_installer.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_installer.cc
@@ -25,6 +25,7 @@
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
 #if BUILDFLAG(IS_CHROMEOS)
+#include "chrome/browser/web_applications/isolated_web_apps/commands/copy_bundle_to_cache_command.h"
 #include "chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_cache_client.h"
 #include "chromeos/components/mgs/managed_guest_session_utils.h"
 #endif  // BUILDFLAG(IS_CHROMEOS)
@@ -141,12 +142,14 @@
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     std::unique_ptr<IwaInstallCommandWrapper> install_command_wrapper,
     base::Value::List& log,
+    WebAppProvider* provider,
     ResultCallback callback)
     : install_options_(std::move(install_options)),
       install_source_type_(install_source_type),
       url_loader_factory_(std::move(url_loader_factory)),
       install_command_wrapper_(std::move(install_command_wrapper)),
       log_(log),
+      provider_(provider),
       callback_(std::move(callback)) {
 #if BUILDFLAG(IS_CHROMEOS)
   if (IsIwaBundleCacheEnabled()) {
@@ -235,16 +238,13 @@
   // installation.
 }
 
-void IwaInstaller::OnBundleCopiedToCache(
-    base::expected<IwaCacheClient::CopyBundleToCacheSuccess,
-                   IwaCacheClient::CopyBundleToCacheError> result) {
+void IwaInstaller::OnBundleCopiedToCache(CopyBundleToCacheResult result) {
   if (result.has_value()) {
     log_->Append(base::Value(u"successfully copied bundle to the cache: " +
-                             result->cached_bundle_path.LossyDisplayName()));
+                             result->cached_bundle_path().LossyDisplayName()));
   } else {
-    log_->Append(
-        base::Value("failed to copy bundle to cache: " +
-                    IwaCacheClient::CopyErrorToString(result.error())));
+    log_->Append(base::Value("failed to copy bundle to cache: " +
+                             CopyBundleToCacheErrorToString(result.error())));
   }
 
   // TODO(crbug.com/388727600): add UMA metrics for failed and successful copy
@@ -376,23 +376,9 @@
   // TODO: crbug.com/306638108 - In the time it took to download everything, the
   // app might have already been installed by other means.
 
-  IwaSourceBundleProdFileOp bundle_source_operation =
-      IwaSourceBundleProdFileOp::kMove;
-
-#if BUILDFLAG(IS_CHROMEOS)
-  if (IsIwaBundleCacheEnabled()) {
-    // Bundle should be copied to the cache later, so we should not move it
-    // during the installation.
-    // Note: `bundle_` will be deleted later when this class is destroyed, since
-    // it is `ScopedTempWebBundleFile`, no need to delete it manually after the
-    // copying.
-    bundle_source_operation = IwaSourceBundleProdFileOp::kCopy;
-  }
-#endif  // BUILDFLAG(IS_CHROMEOS)
-
   install_command_wrapper_->Install(
       GetIsolatedWebAppInstallSource(install_source_type_, bundle_.path(),
-                                     bundle_source_operation),
+                                     IwaSourceBundleProdFileOp::kMove),
       url_info, expected_version,
       base::BindOnce(&IwaInstaller::OnIwaInstalledFromInternet,
                      weak_factory_.GetWeakPtr(), expected_version));
@@ -413,13 +399,19 @@
   if (IsIwaBundleCacheEnabled()) {
     // Successfully installed bundles should be copied to cache, so next time
     // the installation will happen from the cache.
-    log_->Append(
-        base::Value("start copying bundle to cache after successful "
-                    "installation from the Internet"));
-    cache_client_->CopyBundleToCache(
-        bundle_.path(), install_options_.web_bundle_id(), installed_version,
-        base::BindOnce(&IwaInstaller::OnBundleCopiedToCache,
-                       weak_factory_.GetWeakPtr()));
+    log_->Append(base::Value(
+        "start copying bundle: " + install_options_.web_bundle_id().id() +
+        " to cache after successful installation from the Internet"));
+
+    IsolatedWebAppUrlInfo url_info =
+        IsolatedWebAppUrlInfo::CreateFromSignedWebBundleId(
+            install_options_.web_bundle_id());
+    CHECK_DEREF(provider_.get())
+        .scheduler()
+        .CopyIsolatedWebAppBundleToCache(
+            url_info, IwaCacheClient::GetCurrentSessionType(),
+            base::BindOnce(&IwaInstaller::OnBundleCopiedToCache,
+                           weak_factory_.GetWeakPtr()));
     return;
   }
 #endif  // BUILDFLAG(IS_CHROMEOS)
@@ -466,7 +458,7 @@
             std::move(url_loader_factory),
             std::make_unique<IwaInstaller::IwaInstallCommandWrapperImpl>(
                 provider),
-            log, std::move(callback));
+            log, provider, std::move(callback));
       });
 }
 
diff --git a/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_installer.h b/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_installer.h
index 1f66fdba..3f3cecf 100644
--- a/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_installer.h
+++ b/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_installer.h
@@ -20,6 +20,7 @@
 #include "chrome/browser/web_applications/web_app_command_scheduler.h"
 
 #if BUILDFLAG(IS_CHROMEOS)
+#include "chrome/browser/web_applications/isolated_web_apps/commands/copy_bundle_to_cache_command.h"
 #include "chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_cache_client.h"
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
@@ -111,6 +112,7 @@
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
       std::unique_ptr<IwaInstallCommandWrapper> install_command_wrapper,
       base::Value::List& log,
+      WebAppProvider* provider,
       ResultCallback callback);
   ~IwaInstaller();
 
@@ -134,9 +136,7 @@
 
   // Bundle should be copied to cache after the successful installation from the
   // Internet.
-  void OnBundleCopiedToCache(
-      base::expected<IwaCacheClient::CopyBundleToCacheSuccess,
-                     IwaCacheClient::CopyBundleToCacheError> result);
+  void OnBundleCopiedToCache(CopyBundleToCacheResult result);
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
   void CreateTempFile(base::OnceClosure next_step_callback);
@@ -177,6 +177,7 @@
   scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
   std::unique_ptr<IwaInstallCommandWrapper> install_command_wrapper_;
   raw_ref<base::Value::List> log_;
+  const raw_ptr<web_app::WebAppProvider> provider_;
   ResultCallback callback_;
 
   ScopedTempWebBundleFile bundle_;
diff --git a/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_policy_manager_ash_browsertest.cc b/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_policy_manager_ash_browsertest.cc
index eb8bbb18..a0b8b756 100644
--- a/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_policy_manager_ash_browsertest.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_policy_manager_ash_browsertest.cc
@@ -59,8 +59,8 @@
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/pref_names.h"
 #include "chromeos/ash/components/dbus/session_manager/fake_session_manager_client.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "components/policy/core/common/cloud/test/policy_builder.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "components/policy/core/common/mock_configuration_policy_provider.h"
 #include "components/policy/core/common/policy_namespace.h"
 #include "components/policy/policy_constants.h"
diff --git a/chrome/browser/web_applications/isolated_web_apps/test/test_iwa_installer_factory.cc b/chrome/browser/web_applications/isolated_web_apps/test/test_iwa_installer_factory.cc
index 7fdb5b7..d5507f4 100644
--- a/chrome/browser/web_applications/isolated_web_apps/test/test_iwa_installer_factory.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/test/test_iwa_installer_factory.cc
@@ -70,6 +70,7 @@
   return std::make_unique<IwaInstaller>(
       std::move(install_options), install_source_type,
       std::move(url_loader_factory), std::move(install_command_wrapper), log,
+      provider,
       base::BindOnce(
           [](TestIwaInstallerFactory* installer_instance,
              IwaInstaller::ResultCallback callback, webapps::AppId app_id,
diff --git a/chrome/browser/web_applications/web_app_command_scheduler.cc b/chrome/browser/web_applications/web_app_command_scheduler.cc
index b50a575..4a3cc14 100644
--- a/chrome/browser/web_applications/web_app_command_scheduler.cc
+++ b/chrome/browser/web_applications/web_app_command_scheduler.cc
@@ -89,6 +89,7 @@
 
 #if BUILDFLAG(IS_CHROMEOS)
 #include "chrome/browser/web_applications/isolated_web_apps/commands/cleanup_bundle_cache_command.h"
+#include "chrome/browser/web_applications/isolated_web_apps/commands/copy_bundle_to_cache_command.h"
 #include "chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_cache_client.h"
 #else  // !BUILDFLAG(IS_CHROMEOS)
 #include "chrome/browser/web_applications/jobs/link_capturing.h"
@@ -374,6 +375,17 @@
 }
 
 #if BUILDFLAG(IS_CHROMEOS)
+void WebAppCommandScheduler::CopyIsolatedWebAppBundleToCache(
+    const IsolatedWebAppUrlInfo& url_info,
+    IwaCacheClient::SessionType session_type,
+    base::OnceCallback<void(CopyBundleToCacheResult)> callback,
+    const base::Location& call_location) {
+  provider_->command_manager().ScheduleCommand(
+      std::make_unique<CopyBundleToCacheCommand>(
+          url_info, session_type, *profile_, std::move(callback)),
+      call_location);
+}
+
 void WebAppCommandScheduler::CleanupIsolatedWebAppBundleCache(
     const std::vector<web_package::SignedWebBundleId>& iwas_to_keep_in_cache,
     IwaCacheClient::SessionType session_type,
diff --git a/chrome/browser/web_applications/web_app_command_scheduler.h b/chrome/browser/web_applications/web_app_command_scheduler.h
index 2affcee..0be38c4 100644
--- a/chrome/browser/web_applications/web_app_command_scheduler.h
+++ b/chrome/browser/web_applications/web_app_command_scheduler.h
@@ -80,6 +80,8 @@
 #if BUILDFLAG(IS_CHROMEOS)
 class CleanupBundleCacheSuccess;
 class CleanupBundleCacheError;
+class CopyBundleToCacheSuccess;
+enum class CopyBundleToCacheError;
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
 // The command scheduler is the main API to access the web app system. The
@@ -298,6 +300,14 @@
       const base::Location& call_location = FROM_HERE);
 
 #if BUILDFLAG(IS_CHROMEOS)
+  //  Copies IWA bundle file to the cache for `session_type`.
+  void CopyIsolatedWebAppBundleToCache(
+      const IsolatedWebAppUrlInfo& url_info,
+      IwaCacheClient::SessionType session_type,
+      base::OnceCallback<void(base::expected<CopyBundleToCacheSuccess,
+                                             CopyBundleToCacheError>)> callback,
+      const base::Location& call_location = FROM_HERE);
+
   // Cleans all IWA cached bundles for `session_type` which are not in the
   // `iwas_to_keep_in_cache`.
   void CleanupIsolatedWebAppBundleCache(
diff --git a/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc b/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
index 24ef987..d64a8613 100644
--- a/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
+++ b/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
@@ -49,6 +49,7 @@
 #include "chrome/browser/webauthn/cablev2_devices.h"
 #include "chrome/browser/webauthn/enclave_manager.h"
 #include "chrome/browser/webauthn/gpm_enclave_controller.h"
+#include "chrome/browser/webauthn/immediate_request_rate_limiter_factory.h"
 #include "chrome/browser/webauthn/passkey_model_factory.h"
 #include "chrome/browser/webauthn/webauthn_metrics_util.h"
 #include "chrome/browser/webauthn/webauthn_pref_names.h"
@@ -64,6 +65,7 @@
 #include "components/sync/service/sync_service.h"
 #include "components/trusted_vault/frontend_trusted_vault_connection.h"
 #include "components/user_prefs/user_prefs.h"
+#include "components/webauthn/core/browser/immediate_request_rate_limiter.h"
 #include "components/webauthn/core/browser/passkey_model.h"
 #include "content/public/browser/authenticator_request_client_delegate.h"
 #include "content/public/browser/browser_context.h"
@@ -945,11 +947,21 @@
     return false;
   }
 
-  // Always return not found immediate in incognito.
+  // Always return not allowed immediate in incognito.
   if (profile()->IsOffTheRecord()) {
     return true;
   }
 
+  if (auto* rate_limiter =
+          ImmediateRequestRateLimiterFactory::GetForProfile(profile())) {
+    const url::Origin origin = GetRenderFrameHost()->GetLastCommittedOrigin();
+    if (!rate_limiter->IsRequestAllowed(origin)) {
+      FIDO_LOG(ERROR)
+          << "Immediate request rate limit exceeded for the origin.";
+      return true;
+    }
+  }
+
   if (data.recognized_credentials.size() + passwords.size() == 0) {
     return true;
   }
diff --git a/chrome/browser/webauthn/chrome_authenticator_request_delegate_unittest.cc b/chrome/browser/webauthn/chrome_authenticator_request_delegate_unittest.cc
index 5790af8..7672d48 100644
--- a/chrome/browser/webauthn/chrome_authenticator_request_delegate_unittest.cc
+++ b/chrome/browser/webauthn/chrome_authenticator_request_delegate_unittest.cc
@@ -27,6 +27,7 @@
 #include "chrome/browser/sync/sync_service_factory.h"
 #include "chrome/browser/webauthn/authenticator_request_dialog_model.h"
 #include "chrome/browser/webauthn/chrome_web_authentication_delegate.h"
+#include "chrome/browser/webauthn/immediate_request_rate_limiter_factory.h"
 #include "chrome/browser/webauthn/passkey_model_factory.h"
 #include "chrome/browser/webauthn/password_credential_controller.h"
 #include "chrome/browser/webauthn/webauthn_pref_names.h"
@@ -40,6 +41,7 @@
 #include "components/sync/base/user_selectable_type.h"
 #include "components/sync/protocol/webauthn_credential_specifics.pb.h"
 #include "components/sync/test/test_sync_service.h"
+#include "components/webauthn/core/browser/immediate_request_rate_limiter.h"
 #include "components/webauthn/core/browser/passkey_model.h"
 #include "components/webauthn/core/browser/test_passkey_model.h"
 #include "content/public/browser/authenticator_request_client_delegate.h"
@@ -148,6 +150,10 @@
 class ChromeAuthenticatorRequestDelegateTest
     : public ChromeRenderViewHostTestHarness {
  public:
+  ChromeAuthenticatorRequestDelegateTest()
+      : ChromeRenderViewHostTestHarness(
+            base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
+
   void SetUp() override {
     ChromeRenderViewHostTestHarness::SetUp();
     PasskeyModelFactory::GetInstance()->SetTestingFactoryAndUse(
@@ -157,6 +163,12 @@
               return std::make_unique<webauthn::TestPasskeyModel>();
             }));
     ChromeAuthenticatorRequestDelegate::SetGlobalObserverForTesting(&observer_);
+
+    ImmediateRequestRateLimiterFactory::GetInstance()->SetTestingFactoryAndUse(
+        profile(), base::BindRepeating([](content::BrowserContext* context)
+                                           -> std::unique_ptr<KeyedService> {
+          return std::make_unique<webauthn::ImmediateRequestRateLimiter>();
+        }));
   }
 
   void TearDown() override {
@@ -1019,6 +1031,78 @@
   std::move(callback).Run({});
 }
 
+TEST_F(ChromeAuthenticatorRequestDelegateTest, ImmediateMediationRateLimit) {
+  constexpr base::TimeDelta kWindowSize = base::Minutes(1);
+  constexpr int kMaxRequestsPerWindow = 2;
+
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeatureWithParameters(
+      device::kWebAuthnImmediateRequestRateLimit,
+      {{"max_requests", base::NumberToString(kMaxRequestsPerWindow)},
+       {"window_seconds", "60"}});
+  // Navigate to commit the origin.
+  content::WebContentsTester::For(web_contents())
+      ->NavigateAndCommit(GURL(kOrigin));
+  ChromeAuthenticatorRequestDelegate delegate(main_rfh());
+  delegate.SetRelyingPartyId(kRpId);
+  delegate.SetUIPresentation(UIPresentation::kModalImmediate);
+
+  // Register a mock callback for the immediate_not_found case.
+  // This is called when MaybeHandleImmediateMediation returns true (e.g., rate
+  // limited).
+  base::MockCallback<base::OnceClosure> mock_immediate_not_found_callback;
+  delegate.RegisterActionCallbacks(
+      /*cancel_callback=*/base::DoNothing(),
+      mock_immediate_not_found_callback.Get(),
+      /*start_over_callback=*/base::DoNothing(),
+      /*account_preselected_callback=*/base::DoNothing(),
+      /*password_selected_callback=*/base::DoNothing(),
+      /*request_callback=*/base::DoNothing(),
+      /*bluetooth_adapter_power_on_callback=*/base::DoNothing(),
+      /*bluetooth_query_status_callback=*/base::DoNothing());
+
+  TransportAvailabilityInfo transports_info;
+  transports_info.request_type = device::FidoRequestType::kGetAssertion;
+  transports_info.recognized_credentials = {
+      device::DiscoverableCredentialMetadata(
+          device::AuthenticatorType::kEnclave, kRpId, {},
+          device::PublicKeyCredentialUserEntity(),
+          /*provider_name=*/std::nullopt)};
+
+  for (int i = 0; i < kMaxRequestsPerWindow; ++i) {
+    SCOPED_TRACE(testing::Message() << "Request " << i + 1);
+    EXPECT_CALL(mock_immediate_not_found_callback, Run).Times(0);
+    // Need to pass a copy as OnTransportAvailabilityEnumerated takes by value.
+    TransportAvailabilityInfo info_copy = transports_info;
+    delegate.OnTransportAvailabilityEnumerated(std::move(info_copy));
+    testing::Mock::VerifyAndClearExpectations(
+        &mock_immediate_not_found_callback);
+  }
+
+  // The next request should be rate-limited (callback is called).
+  {
+    SCOPED_TRACE(testing::Message() << "Request " << kMaxRequestsPerWindow + 1);
+    EXPECT_CALL(mock_immediate_not_found_callback, Run).Times(1);
+    TransportAvailabilityInfo info_copy = transports_info;
+    delegate.OnTransportAvailabilityEnumerated(std::move(info_copy));
+    testing::Mock::VerifyAndClearExpectations(
+        &mock_immediate_not_found_callback);
+  }
+
+  // Advance time beyond the window.
+  task_environment()->FastForwardBy(kWindowSize + base::Seconds(1));
+
+  // The next request should be allowed again (callback not called).
+  {
+    SCOPED_TRACE(testing::Message() << "Request after time window");
+    EXPECT_CALL(mock_immediate_not_found_callback, Run).Times(0);
+    TransportAvailabilityInfo info_copy = transports_info;
+    delegate.OnTransportAvailabilityEnumerated(std::move(info_copy));
+    testing::Mock::VerifyAndClearExpectations(
+        &mock_immediate_not_found_callback);
+  }
+}
+
 }  // namespace
 
 #if BUILDFLAG(IS_MAC)
diff --git a/chrome/browser/webauthn/immediate_request_rate_limiter_factory.cc b/chrome/browser/webauthn/immediate_request_rate_limiter_factory.cc
new file mode 100644
index 0000000..2b7f16c
--- /dev/null
+++ b/chrome/browser/webauthn/immediate_request_rate_limiter_factory.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 "chrome/browser/webauthn/immediate_request_rate_limiter_factory.h"
+
+#include "chrome/browser/profiles/profile.h"
+#include "components/webauthn/core/browser/immediate_request_rate_limiter.h"
+
+// static
+webauthn::ImmediateRequestRateLimiter*
+ImmediateRequestRateLimiterFactory::GetForProfile(
+    content::BrowserContext* context) {
+  return static_cast<webauthn::ImmediateRequestRateLimiter*>(
+      GetInstance()->GetServiceForBrowserContext(context, /*create=*/true));
+}
+
+// static
+ImmediateRequestRateLimiterFactory*
+ImmediateRequestRateLimiterFactory::GetInstance() {
+  static base::NoDestructor<ImmediateRequestRateLimiterFactory> instance;
+  return instance.get();
+}
+
+ImmediateRequestRateLimiterFactory::ImmediateRequestRateLimiterFactory()
+    : ProfileKeyedServiceFactory(
+          "ImmediateRequestRateLimiter",
+          ProfileSelections::BuildForRegularProfile()) {}
+
+ImmediateRequestRateLimiterFactory::~ImmediateRequestRateLimiterFactory() =
+    default;
+
+std::unique_ptr<KeyedService>
+ImmediateRequestRateLimiterFactory::BuildServiceInstanceForBrowserContext(
+    content::BrowserContext* context) const {
+  return std::make_unique<webauthn::ImmediateRequestRateLimiter>();
+}
diff --git a/chrome/browser/webauthn/immediate_request_rate_limiter_factory.h b/chrome/browser/webauthn/immediate_request_rate_limiter_factory.h
new file mode 100644
index 0000000..2ddc44c
--- /dev/null
+++ b/chrome/browser/webauthn/immediate_request_rate_limiter_factory.h
@@ -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.
+
+#ifndef CHROME_BROWSER_WEBAUTHN_IMMEDIATE_REQUEST_RATE_LIMITER_FACTORY_H_
+#define CHROME_BROWSER_WEBAUTHN_IMMEDIATE_REQUEST_RATE_LIMITER_FACTORY_H_
+
+#include "base/no_destructor.h"
+#include "chrome/browser/profiles/profile_keyed_service_factory.h"
+
+namespace content {
+class BrowserContext;
+}
+
+namespace webauthn {
+class ImmediateRequestRateLimiter;
+}
+
+class ImmediateRequestRateLimiterFactory : public ProfileKeyedServiceFactory {
+ public:
+  static webauthn::ImmediateRequestRateLimiter* GetForProfile(
+      content::BrowserContext* context);
+  static ImmediateRequestRateLimiterFactory* GetInstance();
+
+  ImmediateRequestRateLimiterFactory(
+      const ImmediateRequestRateLimiterFactory&) = delete;
+  ImmediateRequestRateLimiterFactory& operator=(
+      const ImmediateRequestRateLimiterFactory&) = delete;
+
+ private:
+  friend class base::NoDestructor<ImmediateRequestRateLimiterFactory>;
+
+  ImmediateRequestRateLimiterFactory();
+  ~ImmediateRequestRateLimiterFactory() override;
+
+  // BrowserContextKeyedServiceFactory:
+  std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
+      content::BrowserContext* context) const override;
+};
+
+#endif  // CHROME_BROWSER_WEBAUTHN_IMMEDIATE_REQUEST_RATE_LIMITER_FACTORY_H_
diff --git a/chrome/browser/win/BUILD.gn b/chrome/browser/win/BUILD.gn
index 3663e50..94ff012 100644
--- a/chrome/browser/win/BUILD.gn
+++ b/chrome/browser/win/BUILD.gn
@@ -13,3 +13,14 @@
     "//ui/native_theme",
   ]
 }
+
+source_set("cloud_synced_folder_checker") {
+  sources = [
+    "cloud_synced_folder_checker.cc",
+    "cloud_synced_folder_checker.h",
+  ]
+  deps = [
+    "//base",
+    "//chrome/common:constants",
+  ]
+}
diff --git a/chrome/build/android-arm32.pgo.txt b/chrome/build/android-arm32.pgo.txt
index bf98545..26cc3c53 100644
--- a/chrome/build/android-arm32.pgo.txt
+++ b/chrome/build/android-arm32.pgo.txt
@@ -1 +1 @@
-chrome-android32-main-1746552233-58978cccbebf037a10c3dd2b5fd4cbc833b76c25-30165321783ced200f97bf7d0630e468c6dfecd0.profdata
+chrome-android32-main-1746596669-ef8f8aa2940bd8e3489db976acee09a6c6c0bd5c-f962687515b7bc001bf4380904b6da50ba8bce93.profdata
diff --git a/chrome/build/android-arm64.pgo.txt b/chrome/build/android-arm64.pgo.txt
index 2163d93..b15487c 100644
--- a/chrome/build/android-arm64.pgo.txt
+++ b/chrome/build/android-arm64.pgo.txt
@@ -1 +1 @@
-chrome-android64-main-1746551424-0a92db44a4b5d6fb6dab47c5b2b8e6f331774811-0e99b96cdd6f28cdae67586273c5116547bc7c08.profdata
+chrome-android64-main-1746610755-301f078e07fffda94a9613609e7b8e51d71e59e8-cd85033399f79ffaf7515e015ebc8e4efd6e7946.profdata
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 80e163e..b155f474 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1746532755-db4a6f3a971a04bbb04b92f04f08c9f1f214bfcb-494b8e90e23862cde023bbf172195f22d227a0f6.profdata
+chrome-linux-main-1746596669-47b28193f25eb9fc6cde369697a0eead37c766dc-f962687515b7bc001bf4380904b6da50ba8bce93.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index b97b8a32..f8e42e4 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1746552233-e3b77a96e6271f28332dc33ccb437ecc4584dda8-30165321783ced200f97bf7d0630e468c6dfecd0.profdata
+chrome-mac-arm-main-1746611847-a37b7979e47bc6116f862351c4c6942108e07406-796016156828373f03c583de4d0cea2e8338a96e.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index f24f15e..945449c 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1746532755-7c187b0b569358e12914392f48696317e7b01a9a-494b8e90e23862cde023bbf172195f22d227a0f6.profdata
+chrome-mac-main-1746596669-a41951026aaaa090d4e91390aba9925bceb0d77b-f962687515b7bc001bf4380904b6da50ba8bce93.profdata
diff --git a/chrome/build/win-arm64.pgo.txt b/chrome/build/win-arm64.pgo.txt
index 35d19c78f..ccc92e09 100644
--- a/chrome/build/win-arm64.pgo.txt
+++ b/chrome/build/win-arm64.pgo.txt
@@ -1 +1 @@
-chrome-win-arm64-main-1746532755-6cdd90db30eeca99b8a7ee87af7d271881097780-494b8e90e23862cde023bbf172195f22d227a0f6.profdata
+chrome-win-arm64-main-1746596669-f63224fa8420683ad9dbd4186e6b4f4157627982-f962687515b7bc001bf4380904b6da50ba8bce93.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 1b9e1c1..42c5814 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1746532755-5ffa733f407730a139990d436115757e25f2f1ef-494b8e90e23862cde023bbf172195f22d227a0f6.profdata
+chrome-win32-main-1746596669-5d433969caa89462c395d3b15c51a26d637b1aff-f962687515b7bc001bf4380904b6da50ba8bce93.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index f29635e..6942990 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1746510390-3d3d31f355983ee63806e7b06169f5f00494da46-d34d80d0e5df7e43587a2fb485d60228efe44aec.profdata
+chrome-win64-main-1746575735-abdf678ab1bebcfdc0d37b724b37f63e82d7e6c1-3e7a899eb30f7f372790ede75edc6f47fbedf024.profdata
diff --git a/chrome/common/extensions/api/storage/storage_schema_manifest_handler_unittest.cc b/chrome/common/extensions/api/storage/storage_schema_manifest_handler_unittest.cc
index ff74e7e5..ec44b8c 100644
--- a/chrome/common/extensions/api/storage/storage_schema_manifest_handler_unittest.cc
+++ b/chrome/common/extensions/api/storage/storage_schema_manifest_handler_unittest.cc
@@ -12,12 +12,15 @@
 #include "base/files/scoped_temp_dir.h"
 #include "base/values.h"
 #include "components/version_info/version_info.h"
+#include "extensions/buildflags/buildflags.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/features/feature_channel.h"
 #include "extensions/common/file_util.h"
 #include "extensions/common/manifest.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
+
 namespace extensions {
 
 class StorageSchemaManifestHandlerTest : public testing::Test {
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 1764e0d2..b897c99f 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1505,6 +1505,7 @@
       "../browser/extensions/api/runtime/runtime_apitest.cc",
       "../browser/extensions/api/scripting/scripting_apitest.cc",
       "../browser/extensions/api/settings_overrides/settings_overrides_browsertest.cc",
+      "../browser/extensions/api/storage/settings_apitest.cc",
       "../browser/extensions/api/system_network/system_network_apitest.cc",
       "../browser/extensions/api/test/apitest_apitest.cc",
       "../browser/extensions/api/web_accessible_resources_apitest.cc",
@@ -2245,10 +2246,6 @@
       "//chrome/browser/preloading/search_preload:browser_tests",
       "//chrome/browser/privacy_budget:browser_tests",
       "//chrome/browser/privacy_sandbox:browser_tests",
-
-      # TODO(413315837): Remove this dep when privacy_sandbox_countries_browsertest.cc
-      # gets build off of c/b/privacy_sandbox/BUILD.gn.
-      "//chrome/browser/privacy_sandbox:headers",
       "//chrome/browser/profiles:profile_util",
       "//chrome/browser/profiling_host:profiling_browsertests",
       "//chrome/browser/promos:utils",
@@ -2323,6 +2320,7 @@
       "//chrome/browser/ui/tabs:tab_menu",
       "//chrome/browser/ui/tabs:tab_model",
       "//chrome/browser/ui/tabs:tabs_public",
+      "//chrome/browser/ui/tabs/tab_strip_api:browser_tests",
       "//chrome/browser/ui/toasts",
       "//chrome/browser/ui/toolbar/cast",
       "//chrome/browser/ui/user_education",
@@ -4336,7 +4334,6 @@
         "../browser/extensions/api/settings_private/settings_private_apitest.cc",
         "../browser/extensions/api/side_panel/side_panel_apitest.cc",
         "../browser/extensions/api/socket/socket_apitest.cc",
-        "../browser/extensions/api/storage/settings_apitest.cc",
         "../browser/extensions/api/system_display/system_display_extension_apitest.cc",
         "../browser/extensions/api/system_private/system_private_apitest.cc",
         "../browser/extensions/api/tab_capture/tab_capture_apitest.cc",
@@ -5317,7 +5314,6 @@
         "//chromeos/ash/experiences/arc:prefs",
         "//chromeos/ash/experiences/arc/session:arc_base_enums",
         "//chromeos/ash/experiences/system_web_apps/types",
-        "//chromeos/ash/services/assistant:lib",
         "//chromeos/ash/services/assistant/public/cpp",
         "//chromeos/ash/services/assistant/public/proto",
         "//chromeos/ash/services/auth_factor_config",
@@ -6386,7 +6382,6 @@
       "../browser/page_load_metrics/observers/non_tab_webui_page_load_metrics_observer_unittest.cc",
       "../browser/performance_manager/metrics/metrics_provider_desktop_unittest.cc",
       "../browser/policy/messaging_layer/util/reporting_server_connector_unittest.cc",
-      "../browser/privacy_sandbox/privacy_sandbox_survey_desktop_controller_unittest.cc",
       "../browser/screen_ai/screen_ai_install_state_unittest.cc",
       "../browser/segmentation_platform/client_util/local_tab_handler_unittest.cc",
       "../browser/site_protection/site_protection_metrics_observer_unittest.cc",
@@ -6404,10 +6399,6 @@
     ]
   }
 
-  if (is_android) {
-    sources += [ "../browser/privacy_sandbox/privacy_sandbox_activity_types_service_unittest.cc" ]
-  }
-
   if (is_mac) {
     sources +=
         [ "../browser/metrics/google_update_metrics_provider_mac_unittest.cc" ]
@@ -7468,7 +7459,7 @@
       "../browser/autofill/android/save_update_address_profile_message_controller_unittest.cc",
       "../browser/autofill/android/save_update_address_profile_prompt_controller_unittest.cc",
       "../browser/autofill/autofill_save_card_infobar_delegate_mobile_unittest.cc",
-      "../browser/auxiliary_search/auxiliary_search_provider_unittest.cc",
+      "../browser/auxiliary_search/auxiliary_search_top_site_provider_bridge_unittest.cc",
       "../browser/bookmarks//android/bookmark_bridge_unittest.cc",
       "../browser/commerce/merchant_viewer/merchant_viewer_data_manager_unittest.cc",
       "../browser/device_reauth/android/device_authenticator_android_unittest.cc",
@@ -8579,6 +8570,7 @@
       "//ash/webui/recorder_app_ui",
       "//ash/webui/scanning",
       "//ash/webui/settings/public/constants:mojom",
+      "//chrome/browser:global_features",
       "//chrome/browser/accessibility:test_support",
       "//chrome/browser/accessibility/media_app/test:test_support",
       "//chrome/browser/apps/app_service/promise_apps/proto",
@@ -8706,6 +8698,7 @@
       "//chrome/services/sharing/nearby/decoder",
       "//chrome/services/sharing/public/cpp",
       "//chrome/services/speech:lib",
+      "//chrome/test:test_support",
       "//chromeos/ash/components/assistant:buildflags",
       "//chromeos/ash/components/audio",
       "//chromeos/ash/components/browser_context_helper",
@@ -8785,6 +8778,7 @@
       "//chromeos/ui/frame:test_support",
       "//chromeos/ui/wm",
       "//components/app_constants",
+      "//components/application_locale_storage",
       "//components/cross_device/nearby:nearby",
       "//components/metrics/structured:structured_events",
       "//components/metrics/structured:structured_metrics_features",
@@ -8927,6 +8921,7 @@
       "../browser/extensions/api/runtime/chrome_runtime_api_delegate_unittest.cc",
       "../browser/extensions/api/storage/managed_value_store_cache_unittest.cc",
       "../browser/extensions/api/storage/policy_value_store_unittest.cc",
+      "../browser/extensions/api/storage/settings_sync_unittest.cc",
       "../browser/extensions/api/storage/storage_session_unittest.cc",
       "../browser/extensions/api/webstore_private/extension_install_status_unittest.cc",
       "../browser/extensions/api/webstore_private/webstore_private_unittest.cc",
@@ -9091,7 +9086,6 @@
       "../browser/extensions/api/socket/tls_socket_unittest.cc",
       "../browser/extensions/api/socket/udp_socket_unittest.cc",
       "../browser/extensions/api/sockets_tcp_server/sockets_tcp_server_api_unittest.cc",
-      "../browser/extensions/api/storage/settings_sync_unittest.cc",
       "../browser/extensions/api/streams_private/streams_private_manifest_unittest.cc",
       "../browser/extensions/api/tab_groups/tab_groups_api_unittest.cc",
       "../browser/extensions/api/tabs/tabs_api_unittest.cc",
@@ -11608,7 +11602,6 @@
         "//chromeos/ui/frame",
         "//chromeos/ui/frame:test_support",
         "//chromeos/ui/frame:test_support",
-        "//components/exo/wayland:test_controller_stub",
         "//components/exo/wayland:ui_controls_protocol_stub",
         "//components/file_access:test_support",
         "//components/reporting/client:report_queue",
diff --git a/chrome/test/android/BUILD.gn b/chrome/test/android/BUILD.gn
index 8775212..8bb3cc986 100644
--- a/chrome/test/android/BUILD.gn
+++ b/chrome/test/android/BUILD.gn
@@ -243,6 +243,7 @@
     "javatests/src/org/chromium/chrome/test/transit/omnibox/FakeOmniboxSuggestions.java",
     "javatests/src/org/chromium/chrome/test/transit/omnibox/OmniboxEnteredTextFacility.java",
     "javatests/src/org/chromium/chrome/test/transit/omnibox/OmniboxFacility.java",
+    "javatests/src/org/chromium/chrome/test/transit/page/BasePageStation.java",
     "javatests/src/org/chromium/chrome/test/transit/page/IncognitoWebPageAppMenuFacility.java",
     "javatests/src/org/chromium/chrome/test/transit/page/NativePageCondition.java",
     "javatests/src/org/chromium/chrome/test/transit/page/PageAppMenuFacility.java",
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/AppMenuFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/AppMenuFacility.java
index 1a2ce95..39c611b 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/AppMenuFacility.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/AppMenuFacility.java
@@ -127,6 +127,7 @@
 
     public static final @IdRes int NEW_TAB_ID = R.id.new_tab_menu_id;
     public static final @IdRes int NEW_INCOGNITO_TAB_ID = R.id.new_incognito_tab_menu_id;
+    public static final @IdRes int NEW_WINDOW_ID = R.id.new_window_menu_id;
     public static final @IdRes int HISTORY_ID = R.id.open_history_menu_id;
     public static final @IdRes int DELETE_BROWSING_DATA_ID = R.id.quick_delete_menu_id;
     public static final @IdRes int DOWNLOADS_ID = R.id.downloads_menu_id;
@@ -172,6 +173,11 @@
                 .build();
     }
 
+    /** Default behavior for "Open new window". */
+    protected RegularNewTabPageStation createNewWindowStation() {
+        return RegularNewTabPageStation.newBuilder().withEntryPoint().build();
+    }
+
     /** Default behavior for "Delete browsing data". */
     protected QuickDeleteDialogFacility createQuickDeleteDialogFacility() {
         return new QuickDeleteDialogFacility();
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/AutoResetCtaTransitTestRule.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/AutoResetCtaTransitTestRule.java
index bdf462e..4fbba9a8 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/AutoResetCtaTransitTestRule.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/AutoResetCtaTransitTestRule.java
@@ -10,7 +10,6 @@
 import org.junit.runners.model.Statement;
 
 import org.chromium.base.test.transit.BatchedPublicTransitRule;
-import org.chromium.base.test.transit.EntryPointSentinelStation;
 import org.chromium.base.test.transit.Station;
 import org.chromium.base.test.transit.TrafficControl;
 import org.chromium.build.annotations.NullMarked;
@@ -20,6 +19,8 @@
 import org.chromium.chrome.test.transit.page.WebPageStation;
 import org.chromium.components.embedder_support.util.UrlConstants;
 
+import java.util.List;
+
 /**
  * Rule for integration tests that reuse a ChromeTabbedActivity but reset tab state between cases.
  *
@@ -54,18 +55,23 @@
      * <p>From the second test onwards, state was reset by {@link BlankCTATabInitialStateRule}.
      */
     public WebPageStation startOnBlankPage() {
-        // Null in the first test, non-null from the second test onwards.
-        Station<?> homeStation = TrafficControl.getActiveStation();
-        if (homeStation == null) {
-            EntryPointSentinelStation entryPoint = new EntryPointSentinelStation();
-            entryPoint.setAsEntryPoint();
-            homeStation = entryPoint;
+        // Empty in the first test, should be size 1 from the second test onwards.
+        List<Station<?>> activeStations = TrafficControl.getActiveStations();
+        if (activeStations.size() > 1) {
+            throw new IllegalStateException(
+                    String.format(
+                            "Expected at most one active station, found %d",
+                            activeStations.size()));
         }
 
+        // Remove the last station of the previous test from |activeStations| to go to an entry
+        // point again.
+        TrafficControl.hopOffPublicTransit();
+
         WebPageStation entryPageStation = WebPageStation.newBuilder().withEntryPoint().build();
 
         // Wait for the Conditions to be met to return an active PageStation.
-        return homeStation.travelToSync(entryPageStation, /* trigger= */ null);
+        return Station.spawnSync(entryPageStation, /* trigger= */ null);
     }
 
     /**
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ntp/IncognitoNewTabPageAppMenuFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ntp/IncognitoNewTabPageAppMenuFacility.java
index 961ebdfa..8425d5ce 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ntp/IncognitoNewTabPageAppMenuFacility.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ntp/IncognitoNewTabPageAppMenuFacility.java
@@ -15,6 +15,7 @@
         mNewIncognitoTab =
                 declareMenuItemToStation(
                         items, NEW_INCOGNITO_TAB_ID, this::createIncognitoNewTabPageStation);
+        mNewWindow = declarePossibleMenuItem(items, NEW_WINDOW_ID, this::handleOpenNewWindow);
 
         declareStubMenuItem(items, HISTORY_ID);
         declareAbsentMenuItem(items, DELETE_BROWSING_DATA_ID);
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ntp/RegularNewTabPageAppMenuFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ntp/RegularNewTabPageAppMenuFacility.java
index 4c5fa54..a384686 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ntp/RegularNewTabPageAppMenuFacility.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ntp/RegularNewTabPageAppMenuFacility.java
@@ -18,6 +18,7 @@
         mNewIncognitoTab =
                 declareMenuItemToStation(
                         items, NEW_INCOGNITO_TAB_ID, this::createIncognitoNewTabPageStation);
+        mNewWindow = declarePossibleMenuItem(items, NEW_WINDOW_ID, this::handleOpenNewWindow);
 
         declareStubMenuItem(items, HISTORY_ID);
         mQuickDelete =
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ntp/RegularNewTabPageStation.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ntp/RegularNewTabPageStation.java
index caae0ace..7dc84bb 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ntp/RegularNewTabPageStation.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ntp/RegularNewTabPageStation.java
@@ -37,7 +37,6 @@
     public ViewElement<View> searchBoxElement;
     public ViewElement<UrlBar> urlBarElement;
     public ViewElement<View> logoElement;
-    public ViewElement<View> mvtsContainerElement;
     public Element<NewTabPage> nativePageElement;
 
     protected <T extends RegularNewTabPageStation> RegularNewTabPageStation(Builder<T> builder) {
@@ -64,7 +63,6 @@
 
         logoElement = elements.declareView(viewSpec(withId(R.id.search_provider_logo)));
         searchBoxElement = elements.declareView(viewSpec(withId(R.id.search_box)));
-        mvtsContainerElement = elements.declareView(viewSpec(withId(R.id.mv_tiles_container)));
 
         nativePageElement =
                 elements.declareEnterConditionAsElement(
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/BasePageStation.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/BasePageStation.java
new file mode 100644
index 0000000..1835437
--- /dev/null
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/BasePageStation.java
@@ -0,0 +1,491 @@
+// 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.test.transit.page;
+
+import static org.chromium.base.test.transit.Condition.whether;
+
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.supplier.Supplier;
+import org.chromium.base.test.transit.CallbackCondition;
+import org.chromium.base.test.transit.Condition;
+import org.chromium.base.test.transit.ConditionStatus;
+import org.chromium.base.test.transit.ConditionStatusWithResult;
+import org.chromium.base.test.transit.ConditionWithResult;
+import org.chromium.base.test.transit.Element;
+import org.chromium.base.test.transit.Elements;
+import org.chromium.base.test.transit.Facility;
+import org.chromium.base.test.transit.Station;
+import org.chromium.base.test.transit.Transition;
+import org.chromium.base.test.transit.Transition.Trigger;
+import org.chromium.chrome.browser.app.ChromeActivity;
+import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tabmodel.TabModel;
+import org.chromium.chrome.browser.tabmodel.TabModelObserver;
+import org.chromium.content_public.browser.LoadUrlParams;
+import org.chromium.ui.base.PageTransition;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Function;
+
+/**
+ * Base class for the screen that shows a web or native page in ChromeActivity. {@link PageStation}
+ * subclasses this for ChromeTabbedActivity and {@link CctPageStation} for CustomTabActivity.
+ *
+ * <p>Contains extra configurable Conditions such as waiting for a tab to be created, selected, have
+ * the expected title, etc.
+ *
+ * @param <HostActivity> The type of activity this station is associate to.
+ */
+public class BasePageStation<HostActivity extends ChromeActivity> extends Station<HostActivity> {
+
+    /**
+     * Basic builder for all BasePageStation subclasses.
+     *
+     * @param <ActivityT> the subclass of ChromeActivity on which the BasePageStation being built is
+     *     based upon.
+     * @param <PageT> the subclass of BasePageStation to build.
+     * @param <BuilderT> the subclass of this Builder.
+     */
+    public static class Builder<
+            ActivityT extends ChromeActivity,
+            PageT extends BasePageStation<ActivityT>,
+            BuilderT extends Builder<ActivityT, PageT, BuilderT>> {
+        protected final Function<BuilderT, PageT> mFactoryMethod;
+        protected boolean mIsEntryPoint;
+        protected Boolean mIncognito;
+        protected Integer mNumTabsBeingOpened;
+        protected Integer mNumTabsBeingSelected;
+        protected Tab mTabAlreadySelected;
+        protected String mExpectedUrlSubstring;
+        protected String mExpectedTitle;
+        protected List<Facility<PageT>> mFacilities;
+
+        public Builder(Function<BuilderT, PageT> factoryMethod) {
+            mFactoryMethod = factoryMethod;
+        }
+
+        public BuilderT self() {
+            return (BuilderT) this;
+        }
+
+        public BuilderT withIncognito(boolean incognito) {
+            mIncognito = incognito;
+            return self();
+        }
+
+        public BuilderT withIsOpeningTabs(int numTabsBeingOpened) {
+            assert numTabsBeingOpened >= 0;
+            mNumTabsBeingOpened = numTabsBeingOpened;
+            return self();
+        }
+
+        public BuilderT withTabAlreadySelected(Tab currentTab) {
+            mTabAlreadySelected = currentTab;
+            mNumTabsBeingSelected = 0;
+            return self();
+        }
+
+        public BuilderT withIsSelectingTabs(int numTabsBeingSelected) {
+            assert numTabsBeingSelected > 0
+                    : "Use withIsSelectingTab() if the PageStation is still in the current tab";
+            mNumTabsBeingSelected = numTabsBeingSelected;
+            // Commonly already set via initFrom().
+            mTabAlreadySelected = null;
+            return self();
+        }
+
+        public BuilderT withEntryPoint() {
+            mNumTabsBeingOpened = 0;
+            mNumTabsBeingSelected = 0;
+            mIsEntryPoint = true;
+            return self();
+        }
+
+        public BuilderT withExpectedUrlSubstring(String value) {
+            mExpectedUrlSubstring = value;
+            return self();
+        }
+
+        public BuilderT withExpectedTitle(String title) {
+            mExpectedTitle = title;
+            return self();
+        }
+
+        public BuilderT withFacility(Facility<PageT> facility) {
+            if (mFacilities == null) {
+                mFacilities = new ArrayList<>();
+            }
+            mFacilities.add(facility);
+            return self();
+        }
+
+        public BuilderT initFrom(BasePageStation<ActivityT> previousStation) {
+            if (mIncognito == null) {
+                mIncognito = previousStation.mIncognito;
+            }
+            if (mNumTabsBeingOpened == null) {
+                mNumTabsBeingOpened = 0;
+            }
+            if (mNumTabsBeingSelected == null) {
+                mNumTabsBeingSelected = 0;
+            }
+            if (mTabAlreadySelected == null && mNumTabsBeingSelected == 0) {
+                mTabAlreadySelected = previousStation.loadedTabElement.getFromPast();
+            }
+            // Cannot copy over facilities because we have no way to clone them. It's also not
+            // obvious that we should...
+            return self();
+        }
+
+        public PageT build() {
+            return mFactoryMethod.apply(self());
+        }
+    }
+
+    protected final boolean mIncognito;
+    protected final boolean mIsEntryPoint;
+    protected final int mNumTabsBeingOpened;
+    protected final int mNumTabsBeingSelected;
+    protected final Tab mTabAlreadySelected;
+    protected final String mExpectedUrlSubstring;
+    protected final String mExpectedTitle;
+    public Element<Tab> activityTabElement;
+    protected Supplier<Tab> mSelectedTabSupplier;
+    public Element<Tab> loadedTabElement;
+
+    protected <T extends BasePageStation<HostActivity>> BasePageStation(
+            Class<HostActivity> activityClass, Builder<HostActivity, T, ?> builder) {
+        super(activityClass);
+
+        // incognito is optional and defaults to false
+        mIncognito = builder.mIncognito == null ? false : builder.mIncognito;
+
+        // isEntryPoint is optional and defaults to false
+        mIsEntryPoint = builder.mIsEntryPoint;
+
+        // mNumTabsBeingOpened is required
+        assert builder.mNumTabsBeingOpened != null
+                : "PageStation.Builder needs withIsOpeningTabs() or initFrom()";
+        mNumTabsBeingOpened = builder.mNumTabsBeingOpened;
+
+        // mNumTabsBeingSelected is required
+        assert builder.mNumTabsBeingSelected != null
+                : "PageStation.Builder needs withIsSelectingTabs(), withTabAlreadySelected() or"
+                        + " initFrom()";
+        mNumTabsBeingSelected = builder.mNumTabsBeingSelected;
+
+        // Pages must have an already selected tab, or be selecting a tab.
+        mTabAlreadySelected = builder.mTabAlreadySelected;
+        assert mIsEntryPoint || (mTabAlreadySelected != null) != (mNumTabsBeingSelected != 0)
+                : String.format(
+                        "mTabAlreadySelected=%s mNumTabsBeingSelected=%s",
+                        mTabAlreadySelected, mNumTabsBeingSelected);
+
+        // URL substring is optional.
+        mExpectedUrlSubstring = builder.mExpectedUrlSubstring;
+
+        // title is optional
+        mExpectedTitle = builder.mExpectedTitle;
+
+        if (builder.mFacilities != null) {
+            for (Facility<T> facility : builder.mFacilities) {
+                addInitialFacility(facility);
+            }
+        }
+    }
+
+    @Override
+    public void declareElements(Elements.Builder elements) {
+        super.declareElements(elements);
+
+        if (mNumTabsBeingOpened > 0) {
+            elements.declareEnterCondition(
+                    new TabAddedCondition(mNumTabsBeingOpened, mActivityElement));
+        }
+
+        if (mIsEntryPoint) {
+            // In entry points we just match the first ActivityTab we see, instead of waiting for
+            // callbacks.
+            activityTabElement =
+                    elements.declareEnterConditionAsElement(
+                            new AnyActivityTabCondition(mActivityElement));
+        } else {
+            if (mNumTabsBeingSelected > 0) {
+                // The last tab of N opened is the Tab that mSelectedTabSupplier will supply.
+                TabSelectedCondition tabSelectedCondition =
+                        new TabSelectedCondition(mNumTabsBeingSelected, mActivityElement);
+                elements.declareEnterCondition(tabSelectedCondition);
+                mSelectedTabSupplier = tabSelectedCondition;
+            } else {
+                // The Tab already created and provided to the constructor is the one that is
+                // expected to be the activityTab.
+                mSelectedTabSupplier = () -> mTabAlreadySelected;
+            }
+            // Only returns the tab when it is the activityTab.
+            activityTabElement =
+                    elements.declareEnterConditionAsElement(
+                            new CorrectActivityTabCondition(
+                                    mActivityElement, mSelectedTabSupplier));
+        }
+        loadedTabElement =
+                elements.declareEnterConditionAsElement(
+                        new PageLoadedCondition(activityTabElement, mIncognito));
+
+        elements.declareEnterCondition(new PageInteractableOrHiddenCondition(loadedTabElement));
+
+        if (mExpectedUrlSubstring != null) {
+            elements.declareEnterCondition(
+                    new PageUrlContainsCondition(mExpectedUrlSubstring, loadedTabElement));
+        }
+
+        if (mExpectedTitle != null) {
+            elements.declareEnterCondition(
+                    new PageTitleCondition(mExpectedTitle, loadedTabElement));
+        }
+    }
+
+    public boolean isIncognito() {
+        return mIncognito;
+    }
+
+    /** Loads a |url| in the same tab and waits to transition. */
+    public <DestinationT extends BasePageStation<HostActivity>>
+            DestinationT loadPageProgrammatically(
+                    String url, Builder<HostActivity, DestinationT, ?> builder) {
+        builder.initFrom(this);
+        if (builder.mExpectedUrlSubstring == null) {
+            builder.mExpectedUrlSubstring = url;
+        }
+
+        DestinationT destination = builder.build();
+        Trigger trigger =
+                () -> {
+                    @PageTransition
+                    int transitionType = PageTransition.TYPED | PageTransition.FROM_ADDRESS_BAR;
+                    loadedTabElement.get().loadUrl(new LoadUrlParams(url, transitionType));
+                };
+        Transition.TransitionOptions options =
+                Transition.newOptions()
+                        .withCondition(new PageLoadCallbackCondition(loadedTabElement.get()))
+                        .withTimeout(10000)
+                        .withPossiblyAlreadyFulfilled()
+                        .withRunTriggerOnUiThread()
+                        .build();
+        return travelToSync(destination, options, trigger);
+    }
+
+    /** Condition to check the page url contains a certain substring. */
+    public static class PageUrlContainsCondition extends Condition {
+        private final String mExpectedUrlPiece;
+        private final Supplier<Tab> mLoadedTabSupplier;
+
+        public PageUrlContainsCondition(String expectedUrl, Supplier<Tab> loadedTabSupplier) {
+            super(/* isRunOnUiThread= */ true);
+            mExpectedUrlPiece = expectedUrl;
+            mLoadedTabSupplier = dependOnSupplier(loadedTabSupplier, "LoadedTab");
+        }
+
+        @Override
+        protected ConditionStatus checkWithSuppliers() throws Exception {
+            String url = mLoadedTabSupplier.get().getUrl().getSpec();
+            return whether(url.contains(mExpectedUrlPiece), "ActivityTab url: \"%s\"", url);
+        }
+
+        @Override
+        public String buildDescription() {
+            return String.format("URL of activity tab contains \"%s\"", mExpectedUrlPiece);
+        }
+    }
+
+    /** Condition to check the page title. */
+    public static class PageTitleCondition extends Condition {
+        private final String mExpectedTitle;
+        private final Supplier<Tab> mLoadedTabSupplier;
+
+        public PageTitleCondition(String expectedTitle, Supplier<Tab> loadedTabSupplier) {
+            super(/* isRunOnUiThread= */ true);
+            mExpectedTitle = expectedTitle;
+            mLoadedTabSupplier = dependOnSupplier(loadedTabSupplier, "LoadedTab");
+        }
+
+        @Override
+        protected ConditionStatus checkWithSuppliers() throws Exception {
+            String title = mLoadedTabSupplier.get().getTitle();
+            return whether(mExpectedTitle.equals(title), "ActivityTab title: \"%s\"", title);
+        }
+
+        @Override
+        public String buildDescription() {
+            return String.format("Title of activity tab is \"%s\"", mExpectedTitle);
+        }
+    }
+
+    private class TabAddedCondition<ActivityT extends ChromeActivity> extends CallbackCondition
+            implements TabModelObserver {
+        private TabModel mTabModel;
+        private Supplier<ActivityT> mActivitySupplier;
+
+        protected TabAddedCondition(int numTabsBeingOpened, Supplier<ActivityT> activitySupplier) {
+            super("didAddTab", numTabsBeingOpened);
+            mActivitySupplier = dependOnSupplier(activitySupplier, "ChromeActivity");
+        }
+
+        @Override
+        public void didAddTab(Tab tab, int type, int creationState, boolean markedForSelection) {
+            notifyCalled();
+        }
+
+        @Override
+        public void onStartMonitoring() {
+            super.onStartMonitoring();
+            ThreadUtils.runOnUiThreadBlocking(
+                    () -> {
+                        mTabModel =
+                                mActivitySupplier
+                                        .get()
+                                        .getTabModelSelector()
+                                        .getModel(isIncognito());
+                        mTabModel.addObserver(this);
+                    });
+        }
+
+        @Override
+        public void onStopMonitoring() {
+            super.onStopMonitoring();
+            ThreadUtils.runOnUiThreadBlocking(
+                    () -> {
+                        mTabModel.removeObserver(this);
+                    });
+        }
+    }
+
+    private class TabSelectedCondition<ActivityT extends ChromeActivity> extends CallbackCondition
+            implements TabModelObserver, Supplier<Tab> {
+        private final List<Tab> mTabsSelected = new ArrayList<>();
+        private TabModel mTabModel;
+        private Supplier<ActivityT> mActivitySupplier;
+
+        private TabSelectedCondition(
+                int numTabsBeingSelected, Supplier<ActivityT> activitySupplier) {
+            super("didSelectTab", numTabsBeingSelected);
+            mActivitySupplier = dependOnSupplier(activitySupplier, "ChromeActivity");
+        }
+
+        @Override
+        public void didSelectTab(Tab tab, int type, int lastId) {
+            if (mTabsSelected.contains(tab)) {
+                // We get multiple (2-3 depending on the case) didSelectTab when selecting a Tab, so
+                // filter out redundant callbacks to make sure we wait for different Tabs.
+                return;
+            }
+            mTabsSelected.add(tab);
+            notifyCalled();
+        }
+
+        @Override
+        public void onStartMonitoring() {
+            super.onStartMonitoring();
+            ThreadUtils.runOnUiThreadBlocking(
+                    () -> {
+                        mTabModel =
+                                mActivitySupplier
+                                        .get()
+                                        .getTabModelSelector()
+                                        .getModel(isIncognito());
+                        mTabModel.addObserver(this);
+                    });
+        }
+
+        @Override
+        public void onStopMonitoring() {
+            super.onStopMonitoring();
+            ThreadUtils.runOnUiThreadBlocking(
+                    () -> {
+                        mTabModel.removeObserver(this);
+                    });
+        }
+
+        @Override
+        public Tab get() {
+            if (mTabsSelected.isEmpty()) {
+                return null;
+            }
+            return mTabsSelected.get(mTabsSelected.size() - 1);
+        }
+
+        @Override
+        public boolean hasValue() {
+            return !mTabsSelected.isEmpty();
+        }
+    }
+
+    private static class CorrectActivityTabCondition<ActivityT extends ChromeActivity>
+            extends ConditionWithResult<Tab> {
+
+        private final Supplier<ActivityT> mActivitySupplier;
+        private final Supplier<Tab> mExpectedTab;
+
+        private CorrectActivityTabCondition(
+                Supplier<ActivityT> activitySupplier, Supplier<Tab> expectedTabSupplier) {
+            super(/* isRunOnUiThread= */ false);
+            mActivitySupplier = dependOnSupplier(activitySupplier, "ChromeActivity");
+            mExpectedTab = dependOnSupplier(expectedTabSupplier, "ExpectedTab");
+        }
+
+        @Override
+        protected ConditionStatusWithResult<Tab> resolveWithSuppliers() {
+            Tab currentActivityTab = mActivitySupplier.get().getActivityTab();
+            if (currentActivityTab == null) {
+                return notFulfilled("null activityTab").withoutResult();
+            }
+
+            Tab expectedTab = mExpectedTab.get();
+            if (currentActivityTab == expectedTab) {
+                return fulfilled("matched expected activityTab: " + currentActivityTab)
+                        .withResult(currentActivityTab);
+            } else {
+                return notFulfilled(
+                                "activityTab is "
+                                        + currentActivityTab
+                                        + ", expected "
+                                        + expectedTab)
+                        .withoutResult();
+            }
+        }
+
+        @Override
+        public String buildDescription() {
+            return "Activity tab is the expected one";
+        }
+    }
+
+    private static class AnyActivityTabCondition<ActivityT extends ChromeActivity>
+            extends ConditionWithResult<Tab> {
+
+        private final Supplier<ActivityT> mActivitySupplier;
+
+        private AnyActivityTabCondition(Supplier<ActivityT> activitySupplier) {
+            super(/* isRunOnUiThread= */ false);
+            mActivitySupplier = dependOnSupplier(activitySupplier, "ChromeActivity");
+        }
+
+        @Override
+        protected ConditionStatusWithResult<Tab> resolveWithSuppliers() {
+            Tab currentActivityTab = mActivitySupplier.get().getActivityTab();
+            if (currentActivityTab == null) {
+                return notFulfilled("null activityTab").withoutResult();
+            } else {
+                return fulfilled("found activityTab " + currentActivityTab)
+                        .withResult(currentActivityTab);
+            }
+        }
+
+        @Override
+        public String buildDescription() {
+            return "Activity has an activityTab";
+        }
+    }
+}
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/IncognitoWebPageAppMenuFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/IncognitoWebPageAppMenuFacility.java
index 4fbfc0d..9600e67 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/IncognitoWebPageAppMenuFacility.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/IncognitoWebPageAppMenuFacility.java
@@ -12,6 +12,7 @@
         mNewIncognitoTab =
                 declareMenuItemToStation(
                         items, NEW_INCOGNITO_TAB_ID, this::createIncognitoNewTabPageStation);
+        mNewWindow = declarePossibleMenuItem(items, NEW_WINDOW_ID, this::handleOpenNewWindow);
 
         declareStubMenuItem(items, HISTORY_ID);
         declareAbsentMenuItem(items, DELETE_BROWSING_DATA_ID);
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 313dcc7..11a6bc51 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
@@ -7,6 +7,8 @@
 import androidx.annotation.CallSuper;
 
 import org.chromium.base.test.transit.Elements;
+import org.chromium.base.test.transit.Station;
+import org.chromium.chrome.browser.tabbed_mode.TabbedAppMenuPropertiesDelegate;
 import org.chromium.chrome.test.transit.CtaAppMenuFacility;
 import org.chromium.chrome.test.transit.ntp.IncognitoNewTabPageAppMenuFacility;
 import org.chromium.chrome.test.transit.ntp.IncognitoNewTabPageStation;
@@ -33,6 +35,7 @@
 
     protected Item<RegularNewTabPageStation> mNewTab;
     protected Item<IncognitoNewTabPageStation> mNewIncognitoTab;
+    protected Item<RegularNewTabPageStation> mNewWindow;
     protected Item<SettingsStation> mSettings;
 
     @Override
@@ -64,8 +67,35 @@
         return mNewIncognitoTab.scrollToAndSelect();
     }
 
+    /** Select "New window" from the app menu. */
+    public RegularNewTabPageStation openNewWindow() {
+        TabbedAppMenuPropertiesDelegate delegate = getTabbedAppMenuPropertiesDelegate();
+        assert delegate.shouldShowNewWindow() : "App menu is not expected to show 'New window'";
+        return mNewWindow.scrollToAndSelect();
+    }
+
+    private TabbedAppMenuPropertiesDelegate getTabbedAppMenuPropertiesDelegate() {
+        return (TabbedAppMenuPropertiesDelegate)
+                mHostStation
+                        .getActivity()
+                        .getRootUiCoordinatorForTesting()
+                        .getAppMenuCoordinatorForTesting()
+                        .getAppMenuPropertiesDelegate();
+    }
+
     /** Select "Settings" from the app menu. */
     public SettingsStation openSettings() {
         return mSettings.scrollToAndSelect();
     }
+
+    /**
+     * Use as lambda from subclasses to handle selecting |mNewWindow|.
+     *
+     * <p>Called from {@link #openNewWindow()} after scrolling to the item.
+     */
+    protected RegularNewTabPageStation handleOpenNewWindow(
+            ItemOnScreenFacility<RegularNewTabPageStation> itemOnScreen) {
+        return Station.spawnSync(
+                createNewWindowStation(), itemOnScreen.viewElement.getClickTrigger());
+    }
 }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/PageStation.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/PageStation.java
index b7a70a9..51d8407 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/PageStation.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/PageStation.java
@@ -6,7 +6,6 @@
 
 import static androidx.test.espresso.matcher.ViewMatchers.withId;
 
-import static org.chromium.base.test.transit.Condition.whether;
 import static org.chromium.base.test.transit.ViewSpec.viewSpec;
 
 import android.app.Activity;
@@ -16,17 +15,8 @@
 
 import androidx.test.platform.app.InstrumentationRegistry;
 
-import org.chromium.base.ThreadUtils;
 import org.chromium.base.supplier.Supplier;
-import org.chromium.base.test.transit.CallbackCondition;
-import org.chromium.base.test.transit.Condition;
-import org.chromium.base.test.transit.ConditionStatus;
-import org.chromium.base.test.transit.ConditionStatusWithResult;
-import org.chromium.base.test.transit.ConditionWithResult;
-import org.chromium.base.test.transit.Element;
 import org.chromium.base.test.transit.Elements;
-import org.chromium.base.test.transit.Facility;
-import org.chromium.base.test.transit.Station;
 import org.chromium.base.test.transit.Transition;
 import org.chromium.base.test.transit.Transition.Trigger;
 import org.chromium.base.test.transit.ViewElement;
@@ -38,8 +28,6 @@
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabLaunchType;
 import org.chromium.chrome.browser.tab.TabSelectionType;
-import org.chromium.chrome.browser.tabmodel.TabModel;
-import org.chromium.chrome.browser.tabmodel.TabModelObserver;
 import org.chromium.chrome.browser.tabmodel.TabModelUtils;
 import org.chromium.chrome.browser.toolbar.home_button.HomeButton;
 import org.chromium.chrome.browser.toolbar.top.ToggleTabStackButton;
@@ -50,12 +38,8 @@
 import org.chromium.chrome.test.transit.ntp.IncognitoNewTabPageStation;
 import org.chromium.chrome.test.transit.ntp.RegularNewTabPageStation;
 import org.chromium.chrome.test.util.MenuUtils;
-import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.content_public.browser.test.util.TouchCommon;
-import org.chromium.ui.base.PageTransition;
 
-import java.util.ArrayList;
-import java.util.List;
 import java.util.function.Function;
 
 /**
@@ -64,113 +48,21 @@
  * <p>Contains extra configurable Conditions such as waiting for a tab to be created, selected, have
  * the expected title, etc.
  */
-public class PageStation extends Station<ChromeTabbedActivity> {
+public class PageStation extends BasePageStation<ChromeTabbedActivity> {
 
     /**
      * Builder for all PageStation subclasses.
      *
      * @param <T> the subclass of PageStation to build.
      */
-    public static class Builder<T extends PageStation> {
-        private final Function<Builder<T>, T> mFactoryMethod;
-        private boolean mIsEntryPoint;
-        private Boolean mIncognito;
-        private Integer mNumTabsBeingOpened;
-        private Integer mNumTabsBeingSelected;
-        private Tab mTabAlreadySelected;
-        private String mExpectedUrlSubstring;
-        private String mExpectedTitle;
-        private List<Facility<T>> mFacilities;
-
+    public static class Builder<T extends PageStation>
+            extends BasePageStation.Builder<ChromeTabbedActivity, T, Builder<T>> {
         public Builder(Function<Builder<T>, T> factoryMethod) {
-            mFactoryMethod = factoryMethod;
-        }
-
-        public Builder<T> withIncognito(boolean incognito) {
-            mIncognito = incognito;
-            return this;
-        }
-
-        public Builder<T> withIsOpeningTabs(int numTabsBeingOpened) {
-            assert numTabsBeingOpened >= 0;
-            mNumTabsBeingOpened = numTabsBeingOpened;
-            return this;
-        }
-
-        public Builder<T> withTabAlreadySelected(Tab currentTab) {
-            mTabAlreadySelected = currentTab;
-            mNumTabsBeingSelected = 0;
-            return this;
-        }
-
-        public Builder<T> withIsSelectingTabs(int numTabsBeingSelected) {
-            assert numTabsBeingSelected > 0
-                    : "Use withIsSelectingTab() if the PageStation is still in the current tab";
-            mNumTabsBeingSelected = numTabsBeingSelected;
-            // Commonly already set via initFrom().
-            mTabAlreadySelected = null;
-            return this;
-        }
-
-        public Builder<T> withEntryPoint() {
-            mNumTabsBeingOpened = 0;
-            mNumTabsBeingSelected = 0;
-            mIsEntryPoint = true;
-            return this;
-        }
-
-        public Builder<T> withExpectedUrlSubstring(String value) {
-            mExpectedUrlSubstring = value;
-            return this;
-        }
-
-        public Builder<T> withExpectedTitle(String title) {
-            mExpectedTitle = title;
-            return this;
-        }
-
-        public Builder<T> withFacility(Facility<T> facility) {
-            if (mFacilities == null) {
-                mFacilities = new ArrayList<>();
-            }
-            mFacilities.add(facility);
-            return this;
-        }
-
-        public Builder<T> initFrom(PageStation previousStation) {
-            if (mIncognito == null) {
-                mIncognito = previousStation.mIncognito;
-            }
-            if (mNumTabsBeingOpened == null) {
-                mNumTabsBeingOpened = 0;
-            }
-            if (mNumTabsBeingSelected == null) {
-                mNumTabsBeingSelected = 0;
-            }
-            if (mTabAlreadySelected == null && mNumTabsBeingSelected == 0) {
-                mTabAlreadySelected = previousStation.loadedTabElement.getFromPast();
-            }
-            // Cannot copy over facilities because we have no way to clone them. It's also not
-            // obvious that we should...
-            return this;
-        }
-
-        public T build() {
-            return mFactoryMethod.apply(this);
+            super(factoryMethod);
         }
     }
 
-    protected final boolean mIncognito;
-    protected final boolean mIsEntryPoint;
-    protected final int mNumTabsBeingOpened;
-    protected final int mNumTabsBeingSelected;
-    protected final Tab mTabAlreadySelected;
-    protected final String mExpectedUrlSubstring;
-    protected final String mExpectedTitle;
     public static final ViewSpec<UrlBar> URL_BAR = viewSpec(UrlBar.class, withId(R.id.url_bar));
-    public Element<Tab> activityTabElement;
-    protected Supplier<Tab> mSelectedTabSupplier;
-    public Element<Tab> loadedTabElement;
     public ViewElement<ToolbarControlContainer> toolbarElement;
     public ViewElement<ToggleTabStackButton> tabSwitcherButtonElement;
     public ViewElement<ImageButton> menuButtonElement;
@@ -182,42 +74,7 @@
 
     /** Use the PageStation's subclass |newBuilder()|. */
     protected <T extends PageStation> PageStation(Builder<T> builder) {
-        // incognito is optional and defaults to false
-        super(ChromeTabbedActivity.class);
-        mIncognito = builder.mIncognito == null ? false : builder.mIncognito;
-
-        // isEntryPoint is optional and defaults to false
-        mIsEntryPoint = builder.mIsEntryPoint;
-
-        // isOpeningTab is required
-        assert builder.mNumTabsBeingOpened != null
-                : "PageStation.Builder needs withIsOpeningTabs() or initFrom()";
-        mNumTabsBeingOpened = builder.mNumTabsBeingOpened;
-
-        // mNumTabsBeingSelected is required
-        assert builder.mNumTabsBeingSelected != null
-                : "PageStation.Builder needs withIsSelectingTabs(), withTabAlreadySelected() or"
-                        + " initFrom()";
-        mNumTabsBeingSelected = builder.mNumTabsBeingSelected;
-
-        // Pages must have an already selected tab, or be selecting a tab.
-        mTabAlreadySelected = builder.mTabAlreadySelected;
-        assert mIsEntryPoint || (mTabAlreadySelected != null) != (mNumTabsBeingSelected != 0)
-                : String.format(
-                        "mTabAlreadySelected=%s mNumTabsBeingSelected=%s",
-                        mTabAlreadySelected, mNumTabsBeingSelected);
-
-        // URL substring is optional.
-        mExpectedUrlSubstring = builder.mExpectedUrlSubstring;
-
-        // title is optional
-        mExpectedTitle = builder.mExpectedTitle;
-
-        if (builder.mFacilities != null) {
-            for (Facility<T> facility : builder.mFacilities) {
-                addInitialFacility(facility);
-            }
-        }
+        super(ChromeTabbedActivity.class, builder);
     }
 
     @Override
@@ -245,100 +102,6 @@
                 elements.declareView(
                         viewSpec(ImageButton.class, withId(R.id.menu_button)),
                         ViewElement.unscopedOption());
-
-        if (mNumTabsBeingOpened > 0) {
-            elements.declareEnterCondition(
-                    new TabAddedCondition(mNumTabsBeingOpened, mActivityElement));
-        }
-
-        if (mIsEntryPoint) {
-            // In entry points we just match the first ActivityTab we see, instead of waiting for
-            // callbacks.
-            activityTabElement =
-                    elements.declareEnterConditionAsElement(
-                            new AnyActivityTabCondition(mActivityElement));
-        } else {
-            if (mNumTabsBeingSelected > 0) {
-                // The last tab of N opened is the Tab that mSelectedTabSupplier will supply.
-                TabSelectedCondition tabSelectedCondition =
-                        new TabSelectedCondition(mNumTabsBeingSelected, mActivityElement);
-                elements.declareEnterCondition(tabSelectedCondition);
-                mSelectedTabSupplier = tabSelectedCondition;
-            } else {
-                // The Tab already created and provided to the constructor is the one that is
-                // expected to be the activityTab.
-                mSelectedTabSupplier = () -> mTabAlreadySelected;
-            }
-            // Only returns the tab when it is the activityTab.
-            activityTabElement =
-                    elements.declareEnterConditionAsElement(
-                            new CorrectActivityTabCondition(
-                                    mActivityElement, mSelectedTabSupplier));
-        }
-        loadedTabElement =
-                elements.declareEnterConditionAsElement(
-                        new PageLoadedCondition(activityTabElement, mIncognito));
-
-        elements.declareEnterCondition(new PageInteractableOrHiddenCondition(loadedTabElement));
-
-        if (mExpectedTitle != null) {
-            elements.declareEnterCondition(
-                    new PageTitleCondition(mExpectedTitle, loadedTabElement));
-        }
-        if (mExpectedUrlSubstring != null) {
-            elements.declareEnterCondition(
-                    new PageUrlContainsCondition(mExpectedUrlSubstring, loadedTabElement));
-        }
-    }
-
-    public boolean isIncognito() {
-        return mIncognito;
-    }
-
-    /** Condition to check the page title. */
-    public static class PageTitleCondition extends Condition {
-        private final String mExpectedTitle;
-        private final Supplier<Tab> mLoadedTabSupplier;
-
-        public PageTitleCondition(String expectedTitle, Supplier<Tab> loadedTabSupplier) {
-            super(/* isRunOnUiThread= */ true);
-            mExpectedTitle = expectedTitle;
-            mLoadedTabSupplier = dependOnSupplier(loadedTabSupplier, "LoadedTab");
-        }
-
-        @Override
-        protected ConditionStatus checkWithSuppliers() throws Exception {
-            String title = mLoadedTabSupplier.get().getTitle();
-            return whether(mExpectedTitle.equals(title), "ActivityTab title: \"%s\"", title);
-        }
-
-        @Override
-        public String buildDescription() {
-            return String.format("Title of activity tab is \"%s\"", mExpectedTitle);
-        }
-    }
-
-    /** Condition to check the page url contains a certain substring. */
-    public static class PageUrlContainsCondition extends Condition {
-        private final String mExpectedUrlPiece;
-        private final Supplier<Tab> mLoadedTabSupplier;
-
-        public PageUrlContainsCondition(String expectedUrl, Supplier<Tab> loadedTabSupplier) {
-            super(/* isRunOnUiThread= */ true);
-            mExpectedUrlPiece = expectedUrl;
-            mLoadedTabSupplier = dependOnSupplier(loadedTabSupplier, "LoadedTab");
-        }
-
-        @Override
-        protected ConditionStatus checkWithSuppliers() throws Exception {
-            String url = mLoadedTabSupplier.get().getUrl().getSpec();
-            return whether(url.contains(mExpectedUrlPiece), "ActivityTab url: \"%s\"", url);
-        }
-
-        @Override
-        public String buildDescription() {
-            return String.format("URL of activity tab contains \"%s\"", mExpectedUrlPiece);
-        }
     }
 
     /** Long presses the tab switcher button to open the action menu. */
@@ -422,30 +185,6 @@
                 tabSwitcherButtonElement.getClickTrigger());
     }
 
-    /** Loads a |url| in the same tab and waits to transition. */
-    public <T extends PageStation> T loadPageProgrammatically(String url, Builder<T> builder) {
-        builder.initFrom(this);
-        if (builder.mExpectedUrlSubstring == null) {
-            builder.mExpectedUrlSubstring = url;
-        }
-
-        T destination = builder.build();
-        Trigger trigger =
-                () -> {
-                    @PageTransition
-                    int transitionType = PageTransition.TYPED | PageTransition.FROM_ADDRESS_BAR;
-                    loadedTabElement.get().loadUrl(new LoadUrlParams(url, transitionType));
-                };
-        Transition.TransitionOptions options =
-                Transition.newOptions()
-                        .withCondition(new PageLoadCallbackCondition(loadedTabElement.get()))
-                        .withTimeout(10000)
-                        .withPossiblyAlreadyFulfilled()
-                        .withRunTriggerOnUiThread()
-                        .build();
-        return travelToSync(destination, options, trigger);
-    }
-
     /** Loads a |url| leading to a web page in the same tab and waits to transition. */
     public WebPageStation loadWebPageProgrammatically(String url) {
         return loadPageProgrammatically(url, WebPageStation.newBuilder());
@@ -561,169 +300,4 @@
             this.mY = toolbarPos[1] + height / 2;
         }
     }
-
-    private class TabAddedCondition extends CallbackCondition implements TabModelObserver {
-        private TabModel mTabModel;
-        private Supplier<ChromeTabbedActivity> mActivitySupplier;
-
-        protected TabAddedCondition(
-                int numTabsBeingOpened, Supplier<ChromeTabbedActivity> activitySupplier) {
-            super("didAddTab", numTabsBeingOpened);
-            mActivitySupplier = dependOnSupplier(activitySupplier, "ChromeTabbedActivity");
-        }
-
-        @Override
-        public void didAddTab(Tab tab, int type, int creationState, boolean markedForSelection) {
-            notifyCalled();
-        }
-
-        @Override
-        public void onStartMonitoring() {
-            super.onStartMonitoring();
-            ThreadUtils.runOnUiThreadBlocking(
-                    () -> {
-                        mTabModel =
-                                mActivitySupplier
-                                        .get()
-                                        .getTabModelSelector()
-                                        .getModel(isIncognito());
-                        mTabModel.addObserver(this);
-                    });
-        }
-
-        @Override
-        public void onStopMonitoring() {
-            super.onStopMonitoring();
-            ThreadUtils.runOnUiThreadBlocking(
-                    () -> {
-                        mTabModel.removeObserver(this);
-                    });
-        }
-    }
-
-    private class TabSelectedCondition extends CallbackCondition
-            implements TabModelObserver, Supplier<Tab> {
-        private final List<Tab> mTabsSelected = new ArrayList<>();
-        private TabModel mTabModel;
-        private Supplier<ChromeTabbedActivity> mActivitySupplier;
-
-        private TabSelectedCondition(
-                int numTabsBeingSelected, Supplier<ChromeTabbedActivity> activitySupplier) {
-            super("didSelectTab", numTabsBeingSelected);
-            mActivitySupplier = dependOnSupplier(activitySupplier, "ChromeTabbedActivity");
-        }
-
-        @Override
-        public void didSelectTab(Tab tab, int type, int lastId) {
-            if (mTabsSelected.contains(tab)) {
-                // We get multiple (2-3 depending on the case) didSelectTab when selecting a Tab, so
-                // filter out redundant callbacks to make sure we wait for different Tabs.
-                return;
-            }
-            mTabsSelected.add(tab);
-            notifyCalled();
-        }
-
-        @Override
-        public void onStartMonitoring() {
-            super.onStartMonitoring();
-            ThreadUtils.runOnUiThreadBlocking(
-                    () -> {
-                        mTabModel =
-                                mActivitySupplier
-                                        .get()
-                                        .getTabModelSelector()
-                                        .getModel(isIncognito());
-                        mTabModel.addObserver(this);
-                    });
-        }
-
-        @Override
-        public void onStopMonitoring() {
-            super.onStopMonitoring();
-            ThreadUtils.runOnUiThreadBlocking(
-                    () -> {
-                        mTabModel.removeObserver(this);
-                    });
-        }
-
-        @Override
-        public Tab get() {
-            if (mTabsSelected.isEmpty()) {
-                return null;
-            }
-            return mTabsSelected.get(mTabsSelected.size() - 1);
-        }
-
-        @Override
-        public boolean hasValue() {
-            return !mTabsSelected.isEmpty();
-        }
-    }
-
-    private static class CorrectActivityTabCondition extends ConditionWithResult<Tab> {
-
-        private final Supplier<ChromeTabbedActivity> mActivitySupplier;
-        private final Supplier<Tab> mExpectedTab;
-
-        private CorrectActivityTabCondition(
-                Supplier<ChromeTabbedActivity> activitySupplier,
-                Supplier<Tab> expectedTabSupplier) {
-            super(/* isRunOnUiThread= */ false);
-            mActivitySupplier = dependOnSupplier(activitySupplier, "ChromeTabbedActivity");
-            mExpectedTab = dependOnSupplier(expectedTabSupplier, "ExpectedTab");
-        }
-
-        @Override
-        protected ConditionStatusWithResult<Tab> resolveWithSuppliers() {
-            Tab currentActivityTab = mActivitySupplier.get().getActivityTab();
-            if (currentActivityTab == null) {
-                return notFulfilled("null activityTab").withoutResult();
-            }
-
-            Tab expectedTab = mExpectedTab.get();
-            if (currentActivityTab == expectedTab) {
-                return fulfilled("matched expected activityTab: " + currentActivityTab)
-                        .withResult(currentActivityTab);
-            } else {
-                return notFulfilled(
-                                "activityTab is "
-                                        + currentActivityTab
-                                        + ", expected "
-                                        + expectedTab)
-                        .withoutResult();
-            }
-        }
-
-        @Override
-        public String buildDescription() {
-            return "Activity tab is the expected one";
-        }
-    }
-
-    private static class AnyActivityTabCondition extends ConditionWithResult<Tab> {
-
-        private final Supplier<ChromeTabbedActivity> mActivitySupplier;
-
-        private AnyActivityTabCondition(Supplier<ChromeTabbedActivity> activitySupplier) {
-            super(/* isRunOnUiThread= */ false);
-            mActivitySupplier = dependOnSupplier(activitySupplier, "ChromeTabbedActivity");
-        }
-
-        @Override
-        protected ConditionStatusWithResult<Tab> resolveWithSuppliers() {
-            Tab currentActivityTab = mActivitySupplier.get().getActivityTab();
-            if (currentActivityTab == null) {
-                return notFulfilled("null activityTab").withoutResult();
-            } else {
-                return fulfilled("found activityTab " + currentActivityTab)
-                        .withResult(currentActivityTab);
-            }
-        }
-
-        @Override
-        public String buildDescription() {
-            return "Activity has an activityTab";
-        }
-    }
 }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/RegularWebPageAppMenuFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/RegularWebPageAppMenuFacility.java
index 0aa4562..71cdf7b 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/RegularWebPageAppMenuFacility.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/RegularWebPageAppMenuFacility.java
@@ -16,6 +16,7 @@
         mNewIncognitoTab =
                 declareMenuItemToStation(
                         items, NEW_INCOGNITO_TAB_ID, this::createIncognitoNewTabPageStation);
+        mNewWindow = declarePossibleMenuItem(items, NEW_WINDOW_ID, this::handleOpenNewWindow);
 
         declareStubMenuItem(items, HISTORY_ID);
         mQuickDelete =
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/tabmodel/TabThumbnailsCapturedCarryOn.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/tabmodel/TabThumbnailsCapturedCarryOn.java
index c1ae7cc..d3c2674 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/tabmodel/TabThumbnailsCapturedCarryOn.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/tabmodel/TabThumbnailsCapturedCarryOn.java
@@ -4,41 +4,20 @@
 
 package org.chromium.chrome.test.transit.tabmodel;
 
-import org.chromium.base.supplier.Supplier;
 import org.chromium.base.test.transit.CarryOn;
-import org.chromium.base.test.transit.Element;
-import org.chromium.base.test.transit.Elements;
-import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 
 /** CarryOn to check for existence of all tab thumbnails on disk for a TabModel. */
 public class TabThumbnailsCapturedCarryOn extends CarryOn {
-    private final boolean mIsIncognito;
-
-    public TabThumbnailsCapturedCarryOn(boolean isIncognito) {
-        mIsIncognito = isIncognito;
-    }
-
-    @Override
-    public void declareElements(Elements.Builder elements) {
-        Supplier<ChromeTabbedActivity> activitySupplier =
-                elements.declareActivity(ChromeTabbedActivity.class);
-        Element<TabModelSelector> tabModelSelectorElement =
-                elements.declareEnterConditionAsElement(
-                        new TabModelSelectorCondition(activitySupplier));
-        elements.declareElementFactory(
-                tabModelSelectorElement,
-                delayedElements -> {
-                    TabModelSelector tabModelSelector = tabModelSelectorElement.get();
-                    TabModel tabModel = tabModelSelector.getModel(mIsIncognito);
-                    int tabCount = tabModel.getCount();
-                    for (int i = 0; i < tabCount; i++) {
-                        delayedElements.declareEnterCondition(
-                                TabThumbnailCondition.etc1(tabModelSelector, tabModel.getTabAt(i)));
-                        delayedElements.declareEnterCondition(
-                                TabThumbnailCondition.jpeg(tabModelSelector, tabModel.getTabAt(i)));
-                    }
-                });
+    public TabThumbnailsCapturedCarryOn(TabModelSelector tabModelSelector, boolean isIncognito) {
+        TabModel tabModel = tabModelSelector.getModel(isIncognito);
+        int tabCount = tabModel.getCount();
+        for (int i = 0; i < tabCount; i++) {
+            declareEnterCondition(
+                    TabThumbnailCondition.etc1(tabModelSelector, tabModel.getTabAt(i)));
+            declareEnterCondition(
+                    TabThumbnailCondition.jpeg(tabModelSelector, tabModel.getTabAt(i)));
+        }
     }
 }
diff --git a/chrome/test/chromedriver/test/run_py_tests.pydeps b/chrome/test/chromedriver/test/run_py_tests.pydeps
index af399c7c..a3d6407b 100644
--- a/chrome/test/chromedriver/test/run_py_tests.pydeps
+++ b/chrome/test/chromedriver/test/run_py_tests.pydeps
@@ -53,6 +53,7 @@
 ../../../../third_party/catapult/devil/devil/devil_env.py
 ../../../../third_party/catapult/devil/devil/utils/__init__.py
 ../../../../third_party/catapult/devil/devil/utils/cmd_helper.py
+../../../../third_party/catapult/devil/devil/utils/host_utils.py
 ../../../../third_party/catapult/devil/devil/utils/lazy/__init__.py
 ../../../../third_party/catapult/devil/devil/utils/lazy/weak_constant.py
 ../../../../third_party/catapult/devil/devil/utils/logging_common.py
diff --git a/chrome/test/data/actor/target_blank_links.html b/chrome/test/data/actor/target_blank_links.html
new file mode 100644
index 0000000..933272b
--- /dev/null
+++ b/chrome/test/data/actor/target_blank_links.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>Links Opening in New Tab</title>
+    <meta name="viewport" content="width=device-width, minimum-scale=1.0">
+  </head>
+  <body>
+    <h1>Links Opening in New Tab</h1>
+    <div><a id="anchorTarget" href="blank.html" target="_blank">Open link in new tab</a></div>
+    <div><a id="scriptOpen" href="#">Open link in new tab by script</a></div>
+    <script>
+      document.getElementById('scriptOpen').addEventListener('click', (e) => {
+        e.preventDefault();
+        window.open('blank.html', '_blank');
+      });
+    </script>
+  </body>
+</html>
diff --git a/chrome/test/data/webui/glic/api_test.ts b/chrome/test/data/webui/glic/api_test.ts
index 4991dc17..1e0015e 100644
--- a/chrome/test/data/webui/glic/api_test.ts
+++ b/chrome/test/data/webui/glic/api_test.ts
@@ -725,7 +725,7 @@
 
   async testResizeWindowTooLarge() {
     assertTrue(!!this.host.resizeWindow);
-    await this.host.resizeWindow(2000, 2000);
+    await this.host.resizeWindow(20000, 20000);
   }
 
   async testResizeWindowWithinBounds() {
diff --git a/chrome/test/data/webui/glic/test_client/index.html b/chrome/test/data/webui/glic/test_client/index.html
index c23e37f..4d7510f 100644
--- a/chrome/test/data/webui/glic/test_client/index.html
+++ b/chrome/test/data/webui/glic/test_client/index.html
@@ -99,8 +99,24 @@
     border-color: red;
   }
 
+  #screenWakeLockStatus::before {
+    content: "Can sleep normally.";
+  }
+  #screenWakeLockStatus[lockStatus*="acquired"]::before {
+    content: "Locked in awake state!";
+    background-color: fuchsia;
+  }
+  #screenWakeLockStatus[lockStatus*="unexpectedRelease"]::before {
+    content: "Awake lock was released unexpectedly.";
+    background-color: yellow;
+  }
+  #screenWakeLockStatus[lockStatus*="error"]::before {
+    content: "Error acquiring awake lock! (can still sleep)";
+    background-color: red;
+  }
+
   #successUI {
-      border-color: green;
+    border-color: green;
   }
 
   .fitWindow html {
@@ -410,6 +426,14 @@
   <div id="audioStatus"></div>
 </div>
 <div class="section">
+  <h1>Screen Wake Lock</h1>
+  <label for="screenWakeLockSwitch">Keep screen awake:
+    <input type="checkbox" id="screenWakeLockSwitch" />
+  </label>
+  <br />
+  <label id="screenWakeLockStatus" lockStatus="released"/>
+</div>
+<div class="section">
   <h1>Desktop Screenshot</h1>
   <button id="desktopScreenshot">Capture</button>
   <button id="panelScreenshot">Capture Panel</button>
diff --git a/chrome/test/data/webui/glic/test_client/page_element_types.ts b/chrome/test/data/webui/glic/test_client/page_element_types.ts
index 717adb4..6953c37 100644
--- a/chrome/test/data/webui/glic/test_client/page_element_types.ts
+++ b/chrome/test/data/webui/glic/test_client/page_element_types.ts
@@ -82,6 +82,8 @@
   fileDropList: HTMLDivElement;
   showDirectoryPicker: HTMLButtonElement;
   failInitializationCheckbox: HTMLInputElement;
+  screenWakeLockSwitch: HTMLInputElement;
+  screenWakeLockStatus: HTMLLabelElement;
   setExperiment: HTMLButtonElement;
   trialName: HTMLInputElement;
   groupName: HTMLInputElement;
diff --git a/chrome/test/data/webui/glic/test_client/serve.py b/chrome/test/data/webui/glic/test_client/serve.py
index 4ae0c3f..b497209d 100755
--- a/chrome/test/data/webui/glic/test_client/serve.py
+++ b/chrome/test/data/webui/glic/test_client/serve.py
@@ -98,6 +98,10 @@
                         help="Alternates between 200 and" +
                         " 404 responses, every minute",
                         action="store_true")
+    parser.add_argument('--bind-all-interfaces',
+                        help='Serves on all interfaces' +
+                        ' (by default serves only localhost)',
+                        action='store_true')
     args = parser.parse_args()
 
     RequestHandler.directory = f'{args.outdir}/gen/chrome/test/data/webui/glic'
@@ -123,7 +127,9 @@
         os.path.join(args.outdir, 'pyproto', 'components',
                      'optimization_guide', 'proto', 'features'))
 
-    with socketserver.ThreadingTCPServer(("", args.port),
+    server_addr = '' if args.bind_all_interfaces else '127.0.0.1'
+
+    with socketserver.ThreadingTCPServer((server_addr, args.port),
                                          RequestHandler) as httpd:
         print("Server started at localhost:" + str(args.port))
         httpd.serve_forever()
diff --git a/chrome/test/data/webui/glic/test_client/test_client.ts b/chrome/test/data/webui/glic/test_client/test_client.ts
index 66726782..269fbd6 100644
--- a/chrome/test/data/webui/glic/test_client/test_client.ts
+++ b/chrome/test/data/webui/glic/test_client/test_client.ts
@@ -246,3 +246,47 @@
     localStorage.removeItem('test-init-failure');
   }
 });
+
+$.screenWakeLockSwitch.addEventListener('click', async () => {
+  if ($.screenWakeLockSwitch.checked) {
+    await acquireScreenWakeLock();
+  } else {
+    await releaseScreenWakeLock();
+  }
+});
+
+let screenWakeLock: WakeLockSentinel|null = null;
+async function acquireScreenWakeLock(): Promise<void> {
+  if (screenWakeLock) {
+    console.warn('Screen wake lock was not released before! Releasing...');
+    await screenWakeLock.release();
+  }
+  try {
+    screenWakeLock = await navigator.wakeLock.request('screen');
+    screenWakeLock.onrelease = () => {
+      if (screenWakeLock) {
+        $.screenWakeLockStatus.setAttribute('lockStatus', 'unexpectedRelease');
+        $.screenWakeLockSwitch.checked = false;
+        screenWakeLock = null;
+        console.warn('Unexpected screen wake lock release.');
+      }
+    };
+    $.screenWakeLockStatus.setAttribute('lockStatus', 'acquired');
+  } catch (err) {
+    $.screenWakeLockStatus.setAttribute('lockStatus', 'error');
+    $.screenWakeLockSwitch.checked = false;
+    screenWakeLock = null;
+    console.error('Failed to acquire screen wake lock', err);
+  }
+}
+async function releaseScreenWakeLock(): Promise<void> {
+  const wakeLock = screenWakeLock;
+  if (!wakeLock) {
+    return;
+  }
+  screenWakeLock = null;
+  if (!wakeLock.released) {
+    await wakeLock.release();
+    $.screenWakeLockStatus.setAttribute('lockStatus', 'released');
+  }
+}
diff --git a/chrome/test/data/webui/lens/side_panel/feedback_toast_test.ts b/chrome/test/data/webui/lens/side_panel/feedback_toast_test.ts
index 68484d0..c0bbc1d 100644
--- a/chrome/test/data/webui/lens/side_panel/feedback_toast_test.ts
+++ b/chrome/test/data/webui/lens/side_panel/feedback_toast_test.ts
@@ -7,6 +7,8 @@
 import type {LensSidePanelPageRemote} from 'chrome-untrusted://lens-overlay/lens_side_panel.mojom-webui.js';
 import type {LensSidePanelAppElement} from 'chrome-untrusted://lens/side_panel/side_panel_app.js';
 import {SidePanelBrowserProxyImpl} from 'chrome-untrusted://lens/side_panel/side_panel_browser_proxy.js';
+import type {CrButtonElement} from 'chrome-untrusted://resources/cr_elements/cr_button/cr_button.js';
+import type {CrToastElement} from 'chrome-untrusted://resources/cr_elements/cr_toast/cr_toast.js';
 import {loadTimeData} from 'chrome-untrusted://resources/js/load_time_data.js';
 import {assertFalse, assertTrue} from 'chrome-untrusted://webui-test/chai_assert.js';
 import {waitAfterNextRender} from 'chrome-untrusted://webui-test/polymer_test_util.js';
@@ -25,6 +27,11 @@
     return isVisible(el) && getComputedStyle(el).visibility !== 'hidden';
   }
 
+  function getFeedbackToast(): CrToastElement {
+    return lensSidePanelElement.$.feedbackToast.shadowRoot.querySelector(
+        'cr-toast')!;
+  }
+
   setup(() => {
     testBrowserProxy = new TestLensSidePanelBrowserProxy();
     SidePanelBrowserProxyImpl.setInstance(testBrowserProxy);
@@ -44,18 +51,18 @@
     callbackRouterRemote.setIsLoadingResults(false);
     await waitAfterNextRender(lensSidePanelElement);
 
-    assertFalse(isRendered(lensSidePanelElement.$.feedbackToast));
+    assertFalse(isRendered(getFeedbackToast()));
   });
 
   test('FeedbackToastNotVisbleOnInitialization', () => {
-    assertFalse(isRendered(lensSidePanelElement.$.feedbackToast));
+    assertFalse(isRendered(getFeedbackToast()));
   });
 
   test('ShowFeedbackToastOnLoadFinished', async () => {
     callbackRouterRemote.setIsLoadingResults(false);
     await waitAfterNextRender(lensSidePanelElement);
 
-    assertTrue(isRendered(lensSidePanelElement.$.feedbackToast));
+    assertTrue(isRendered(getFeedbackToast()));
   });
 
 
@@ -63,21 +70,44 @@
     callbackRouterRemote.setIsLoadingResults(false);
     await waitAfterNextRender(lensSidePanelElement);
 
-    assertTrue(isRendered(lensSidePanelElement.$.feedbackToast));
+    assertTrue(isRendered(getFeedbackToast()));
 
     callbackRouterRemote.setIsLoadingResults(true);
     await waitAfterNextRender(lensSidePanelElement);
-    assertFalse(isRendered(lensSidePanelElement.$.feedbackToast));
+    assertFalse(isRendered(getFeedbackToast()));
   });
 
   test('HideFeedbackToastOnCloseButtonClick', async () => {
     callbackRouterRemote.setIsLoadingResults(false);
     await waitAfterNextRender(lensSidePanelElement);
-    assertTrue(isRendered(lensSidePanelElement.$.feedbackToast));
+    assertTrue(isRendered(getFeedbackToast()));
 
-    lensSidePanelElement.$.closeFeedbackToastButton.click();
+    // Click the close button, which should hide the feedback toast.
+    const closeButton =
+        lensSidePanelElement.$.feedbackToast.shadowRoot.querySelector(
+            'cr-icon-button');
+    assertTrue(closeButton !== null);
+    closeButton.click();
 
     await waitAfterNextRender(lensSidePanelElement);
-    assertFalse(isRendered(lensSidePanelElement.$.feedbackToast));
+    assertFalse(isRendered(getFeedbackToast()));
+  });
+
+  test('SendFeedbackButtonClickCallsHandler', async () => {
+    // Show the toast first.
+    callbackRouterRemote.setIsLoadingResults(false);
+    await waitAfterNextRender(lensSidePanelElement);
+    assertTrue(isRendered(getFeedbackToast()));
+
+    // Click the send feedback button.
+    const sendFeedbackButton =
+        lensSidePanelElement.$.feedbackToast.shadowRoot
+            .querySelector<CrButtonElement>('#sendFeedbackButton');
+    assertTrue(sendFeedbackButton !== null);
+    sendFeedbackButton.click();
+
+    // Verify the handler method was called and toast was hidden.
+    await testBrowserProxy.handler.whenCalled('requestSendFeedback');
+    assertFalse(isRendered(getFeedbackToast()));
   });
 });
diff --git a/chrome/test/data/webui/lens/side_panel/test_side_panel_browser_proxy.ts b/chrome/test/data/webui/lens/side_panel/test_side_panel_browser_proxy.ts
index 9446846..53fdeda 100644
--- a/chrome/test/data/webui/lens/side_panel/test_side_panel_browser_proxy.ts
+++ b/chrome/test/data/webui/lens/side_panel/test_side_panel_browser_proxy.ts
@@ -17,6 +17,7 @@
       'popAndLoadQueryFromHistory',
       'getIsContextualSearchbox',
       'onScrollToMessage',
+      'requestSendFeedback',
     ]);
   }
 
@@ -36,6 +37,10 @@
         pdfPageNumber,
     );
   }
+
+  requestSendFeedback() {
+    this.methodCalled('requestSendFeedback');
+  }
 }
 
 /**
diff --git a/chrome/test/data/webui/settings/glic_page_test.ts b/chrome/test/data/webui/settings/glic_page_test.ts
index 46bdcf4..0c322ed 100644
--- a/chrome/test/data/webui/settings/glic_page_test.ts
+++ b/chrome/test/data/webui/settings/glic_page_test.ts
@@ -408,6 +408,36 @@
       assertFalse(infoCard.opened);
     });
 
+    test('ClosedCaptionsToggleEnabled', () => {
+      page.setPrefValue(PrefName.CLOSED_CAPTIONS_ENABLED, true);
+
+      assertTrue(
+          $<SettingsToggleButtonElement>('closedCaptionsToggle')!.checked);
+    });
+
+    test('ClosedCaptionsToggleDisabled', () => {
+      page.setPrefValue(PrefName.CLOSED_CAPTIONS_ENABLED, false);
+
+      assertFalse(
+          $<SettingsToggleButtonElement>('closedCaptionsToggle')!.checked);
+    });
+
+    test('ClosedCaptionsToggleChanged', () => {
+      page.setPrefValue(PrefName.CLOSED_CAPTIONS_ENABLED, false);
+
+      const closedCaptionsToggle =
+          $<SettingsToggleButtonElement>('closedCaptionsToggle')!;
+      assertTrue(!!closedCaptionsToggle);
+
+      closedCaptionsToggle.click();
+      assertTrue(page.getPref(PrefName.CLOSED_CAPTIONS_ENABLED).value);
+      assertTrue(closedCaptionsToggle.checked);
+
+      closedCaptionsToggle.click();
+      assertFalse(page.getPref(PrefName.CLOSED_CAPTIONS_ENABLED).value);
+      assertFalse(closedCaptionsToggle.checked);
+    });
+
     suite('Metrics', () => {
       async function verifyUserAction(userAction: string) {
         const userActions = await metricsBrowserProxy.getArgs('recordAction');
@@ -457,6 +487,20 @@
         tabAccessToggle.click();
         await verifyUserAction('Glic.Settings.TabContext.Disabled');
       });
+
+      test('ClosedCaptionsToggle', async () => {
+        page.setPrefValue(PrefName.CLOSED_CAPTIONS_ENABLED, false);
+
+        const closedCaptionsToggle =
+            $<SettingsToggleButtonElement>('closedCaptionsToggle')!;
+        assertTrue(!!closedCaptionsToggle);
+
+        closedCaptionsToggle.click();
+        await verifyUserAction('Glic.Settings.ClosedCaptions.Enabled');
+
+        closedCaptionsToggle.click();
+        await verifyUserAction('Glic.Settings.ClosedCaptions.Disabled');
+      });
     });
 
     test('keyboardShortcutLearnMoreHidden', () => {
diff --git a/chrome/test/data/webui/side_panel/read_anything/voice_pack_controller_test.ts b/chrome/test/data/webui/side_panel/read_anything/voice_pack_controller_test.ts
index cb05383..9a86d35 100644
--- a/chrome/test/data/webui/side_panel/read_anything/voice_pack_controller_test.ts
+++ b/chrome/test/data/webui/side_panel/read_anything/voice_pack_controller_test.ts
@@ -5,7 +5,7 @@
 import 'chrome-untrusted://read-anything-side-panel.top-chrome/read_anything.js';
 
 import {BrowserProxy, EXTENSION_RESPONSE_TIMEOUT_MS, mojoVoicePackStatusToVoicePackStatusEnum, NotificationType, SpeechBrowserProxyImpl, VoiceClientSideStatusCode, VoiceNotificationManager, VoicePackController} from 'chrome-untrusted://read-anything-side-panel.top-chrome/read_anything.js';
-import type {VoiceNotificationListener} from 'chrome-untrusted://read-anything-side-panel.top-chrome/read_anything.js';
+import type {VoiceLanguageListener, VoiceNotificationListener} from 'chrome-untrusted://read-anything-side-panel.top-chrome/read_anything.js';
 import {assertArrayEquals, assertEquals, assertFalse, assertTrue} from 'chrome-untrusted://webui-test/chai_assert.js';
 import {MockTimer} from 'chrome-untrusted://webui-test/mock_timer.js';
 
@@ -17,6 +17,9 @@
 suite('VoicePackController', () => {
   let speech: TestSpeechBrowserProxy;
   let voicePackController: VoicePackController;
+  let listener: VoiceLanguageListener;
+  let onEnabledLangsChange: boolean;
+  let onAvailableVoicesChange: boolean;
 
   setup(() => {
     // Clearing the DOM should always be done first.
@@ -27,6 +30,17 @@
     speech = new TestSpeechBrowserProxy();
     SpeechBrowserProxyImpl.setInstance(speech);
     voicePackController = new VoicePackController();
+    onEnabledLangsChange = false;
+    onAvailableVoicesChange = false;
+    listener = {
+      onEnabledLangsChange() {
+        onEnabledLangsChange = true;
+      },
+      onAvailableVoicesChange() {
+        onAvailableVoicesChange = true;
+      },
+    };
+    voicePackController.addListener(listener);
   });
 
   suite('setLocalStatus', () => {
@@ -99,24 +113,35 @@
   });
 
   test('disableLang', () => {
-    assertFalse(voicePackController.disableLang('no'));
-    assertFalse(voicePackController.disableLang(''));
-
     voicePackController.enableLang('vi');
-    assertTrue(voicePackController.disableLang('vi'));
+    onEnabledLangsChange = false;
+
+    voicePackController.disableLang('');
+    assertFalse(onEnabledLangsChange);
+    voicePackController.disableLang('no');
+    assertFalse(onEnabledLangsChange);
+
+    voicePackController.disableLang('vi');
+    assertTrue(onEnabledLangsChange);
     assertFalse(voicePackController.isLangEnabled('vi'));
     assertFalse(voicePackController.isLangEnabled('VI'));
   });
 
   test('enableLang', () => {
-    assertFalse(voicePackController.enableLang(''));
-    assertTrue(voicePackController.enableLang('no'));
-    assertFalse(voicePackController.enableLang('no'));
+    voicePackController.enableLang('');
+    assertFalse(onEnabledLangsChange);
+
+    voicePackController.enableLang('no');
+    assertTrue(onEnabledLangsChange);
+
+    onEnabledLangsChange = false;
+    voicePackController.enableLang('no');
+    assertFalse(onEnabledLangsChange);
     assertTrue(voicePackController.isLangEnabled('no'));
     assertTrue(voicePackController.isLangEnabled('NO'));
   });
 
-  test('getInitialListOfEnabledLanguages', () => {
+  test('restoreEnabledLanguagesFromPref', () => {
     const lang1 = 'en-gb';
     const lang2 = 'fr';
     const lang3 = 'bd';
@@ -129,15 +154,47 @@
       createSpeechSynthesisVoice({lang: lang2, name: 'Google Thomas'}),
       createSpeechSynthesisVoice({lang: lang3, name: 'Google Matt'}),
     ]);
-    voicePackController.refreshAvailableVoices();
+
+    voicePackController.restoreEnabledLanguagesFromPref();
 
     assertArrayEquals(
-        [lang1, lang2, lang3],
-        voicePackController.getInitialListOfEnabledLanguages());
-
+        [lang1, lang2, lang3], voicePackController.getEnabledLangs());
+    assertEquals('en', voicePackController.getCurrentLanguage());
     assertTrue(voicePackController.isLangEnabled(lang1));
     assertTrue(voicePackController.isLangEnabled(lang2));
     assertTrue(voicePackController.isLangEnabled(lang3));
+    assertTrue(onEnabledLangsChange);
+  });
+
+  test('refreshAvailableVoices', () => {
+    const voices = [
+      createSpeechSynthesisVoice({lang: 'en', name: 'Google Henry'}),
+      createSpeechSynthesisVoice({lang: 'en', name: 'Google Thomas'}),
+      createSpeechSynthesisVoice({lang: 'en', name: 'Google Matt'}),
+    ];
+    speech.setVoices(voices);
+
+    assertFalse(onAvailableVoicesChange);
+    assertArrayEquals([], voicePackController.getAvailableVoices());
+
+    voicePackController.refreshAvailableVoices();
+    assertTrue(onAvailableVoicesChange);
+    assertArrayEquals(voices, voicePackController.getAvailableVoices());
+
+    // If we already have voices and new voices come in, we only get those
+    // voices when we force a refresh.
+    onAvailableVoicesChange = false;
+    const newVoices = voices.concat(
+        createSpeechSynthesisVoice({lang: 'it', name: 'Google Charles'}));
+    speech.setVoices(newVoices);
+
+    voicePackController.refreshAvailableVoices();
+    assertFalse(onAvailableVoicesChange);
+    assertArrayEquals(voices, voicePackController.getAvailableVoices());
+
+    voicePackController.refreshAvailableVoices(true);
+    assertTrue(onAvailableVoicesChange);
+    assertArrayEquals(newVoices, voicePackController.getAvailableVoices());
   });
 
   // <if expr="is_chromeos">
@@ -157,10 +214,17 @@
           createSpeechSynthesisVoice({lang: lang1, name: 'Henry'}),
           createSpeechSynthesisVoice({lang: lang2, name: 'Google Thomas'}),
         ]);
+        onEnabledLangsChange = false;
 
-        assertTrue(voicePackController.disableLangIfNoVoices(lang1));
-        assertFalse(voicePackController.disableLangIfNoVoices(lang2));
-        assertTrue(voicePackController.disableLangIfNoVoices(lang3));
+        voicePackController.disableLangIfNoVoices(lang1);
+        assertTrue(onEnabledLangsChange);
+
+        onEnabledLangsChange = false;
+        voicePackController.disableLangIfNoVoices(lang2);
+        assertFalse(onEnabledLangsChange);
+
+        voicePackController.disableLangIfNoVoices(lang3);
+        assertTrue(onEnabledLangsChange);
 
         const langsInPrefs = chrome.readingMode.getLanguagesEnabledInPref();
         assertFalse(langsInPrefs.includes(lang1.toLowerCase()));
@@ -189,10 +253,14 @@
           createSpeechSynthesisVoice({lang: lang1, name: 'Henry'}),
           createSpeechSynthesisVoice({lang: lang2, name: 'Google Thomas'}),
         ]);
+        onEnabledLangsChange = false;
 
-        assertFalse(voicePackController.disableLangIfNoVoices(lang1));
-        assertFalse(voicePackController.disableLangIfNoVoices(lang2));
-        assertTrue(voicePackController.disableLangIfNoVoices(lang3));
+        voicePackController.disableLangIfNoVoices(lang1);
+        assertFalse(onEnabledLangsChange);
+        voicePackController.disableLangIfNoVoices(lang2);
+        assertFalse(onEnabledLangsChange);
+        voicePackController.disableLangIfNoVoices(lang3);
+        assertTrue(onEnabledLangsChange);
 
         const langsInPrefs = chrome.readingMode.getLanguagesEnabledInPref();
         assertTrue(langsInPrefs.includes(lang1.toLowerCase()));
@@ -215,9 +283,10 @@
       createSpeechSynthesisVoice({lang: lang1, name: 'Henry'}),
     ]);
     voicePackController.refreshAvailableVoices();
-    assertArrayEquals(
-        [lang1], voicePackController.getInitialListOfEnabledLanguages());
+    voicePackController.restoreEnabledLanguagesFromPref();
+    assertArrayEquals([lang1], voicePackController.getEnabledLangs());
     assertArrayEquals([], chrome.readingMode.getLanguagesEnabledInPref());
+    onEnabledLangsChange = false;
 
     speech.setVoices([
       createSpeechSynthesisVoice({lang: lang1, name: 'Henry'}),
@@ -228,14 +297,15 @@
     voicePackController.enableNowAvailableLangs();
 
     // After voices come in, we should enable those langs.
+    voicePackController.restoreEnabledLanguagesFromPref();
     assertArrayEquals(
-        [lang1, lang2, lang3],
-        voicePackController.getInitialListOfEnabledLanguages());
+        [lang1, lang2, lang3], voicePackController.getEnabledLangs());
     assertArrayEquals(
         [lang1, lang2, lang3], chrome.readingMode.getLanguagesEnabledInPref());
     assertTrue(voicePackController.isLangEnabled(lang1));
     assertTrue(voicePackController.isLangEnabled(lang2));
     assertTrue(voicePackController.isLangEnabled(lang3));
+    assertTrue(onEnabledLangsChange);
   });
   // </if>
 
diff --git a/chrome/test/data/webui/side_panel/read_anything/voice_pack_model_test.ts b/chrome/test/data/webui/side_panel/read_anything/voice_pack_model_test.ts
index 69cbfc58..4bf8428 100644
--- a/chrome/test/data/webui/side_panel/read_anything/voice_pack_model_test.ts
+++ b/chrome/test/data/webui/side_panel/read_anything/voice_pack_model_test.ts
@@ -44,9 +44,9 @@
     voicePackModel.enableLang(lang1);
     voicePackModel.enableLang(lang2);
     voicePackModel.enableLang(lang3);
-    assertTrue(voicePackModel.disableLang(lang1));
-    assertTrue(voicePackModel.disableLang(lang2));
-    assertFalse(voicePackModel.disableLang('random'));
+    voicePackModel.disableLang(lang1);
+    voicePackModel.disableLang(lang2);
+    voicePackModel.disableLang('random');
 
     assertEquals(1, voicePackModel.getEnabledLangs().size);
     assertTrue(voicePackModel.getEnabledLangs().has(lang3));
diff --git a/chromecast/browser/cast_content_browser_client.cc b/chromecast/browser/cast_content_browser_client.cc
index af91de2..67198ae 100644
--- a/chromecast/browser/cast_content_browser_client.cc
+++ b/chromecast/browser/cast_content_browser_client.cc
@@ -809,8 +809,7 @@
 }
 #endif  // BUILDFLAG(IS_ANDROID)
 
-std::vector<std::unique_ptr<content::NavigationThrottle>>
-CastContentBrowserClient::CreateThrottlesForNavigation(
+void CastContentBrowserClient::CreateThrottlesForNavigation(
     content::NavigationThrottleRegistry& registry) {
   if (chromecast::IsFeatureEnabled(kEnableGeneralAudienceBrowsing)) {
     registry.AddThrottle(
@@ -818,8 +817,6 @@
             &registry.GetNavigationHandle(),
             general_audience_browsing_service_.get()));
   }
-
-  return {};
 }
 
 void CastContentBrowserClient::RegisterNonNetworkSubresourceURLLoaderFactories(
diff --git a/chromecast/browser/cast_content_browser_client.h b/chromecast/browser/cast_content_browser_client.h
index bec64fa..e4e91e6 100644
--- a/chromecast/browser/cast_content_browser_client.h
+++ b/chromecast/browser/cast_content_browser_client.h
@@ -241,8 +241,7 @@
   std::unique_ptr<content::NavigationUIData> GetNavigationUIData(
       content::NavigationHandle* navigation_handle) override;
   bool ShouldEnableStrictSiteIsolation() override;
-  std::vector<std::unique_ptr<content::NavigationThrottle>>
-  CreateThrottlesForNavigation(
+  void CreateThrottlesForNavigation(
       content::NavigationThrottleRegistry& registry) override;
   void RegisterNonNetworkSubresourceURLLoaderFactories(
       int render_process_id,
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 9b100b3..dfae418 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-16275.0.0-1068679
\ No newline at end of file
+16276.0.0-1068685
\ No newline at end of file
diff --git a/chromeos/ash/components/audio/audio_device.cc b/chromeos/ash/components/audio/audio_device.cc
index e514342..0a59988a 100644
--- a/chromeos/ash/components/audio/audio_device.cc
+++ b/chromeos/ash/components/audio/audio_device.cc
@@ -27,11 +27,11 @@
     case AudioDeviceType::kUsb:
     case AudioDeviceType::kBluetooth:
       return 3;
+    case AudioDeviceType::kHdmi:
+      return 2;
     case AudioDeviceType::kInternalSpeaker:
     case AudioDeviceType::kInternalMic:
     case AudioDeviceType::kFrontMic:
-      return 2;
-    case AudioDeviceType::kHdmi:
       return 1;
     // Lower the priority of bluetooth mic to prevent unexpected bad eperience
     // to user because of bluetooth audio profile switching. Make priority to
diff --git a/chromeos/ash/components/audio/audio_device_selection_generated_unittest.cc b/chromeos/ash/components/audio/audio_device_selection_generated_unittest.cc
index 99ba780..99b94c39 100644
--- a/chromeos/ash/components/audio/audio_device_selection_generated_unittest.cc
+++ b/chromeos/ash/components/audio/audio_device_selection_generated_unittest.cc
@@ -582,13 +582,13 @@
   EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
 
   Plug(hdmi2);
-  // Devices: [internal1* hdmi2] usb3 headphone4
-  // List: internal1
-  EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
+  // Devices: [internal1 hdmi2*] usb3 headphone4
+  // List: internal1 < hdmi2
+  EXPECT_EQ(ActiveOutputNodeId(), hdmi2.id);
 
   Plug(usb3);
   // Devices: [internal1 hdmi2 usb3*] headphone4
-  // List: internal1 < usb3
+  // List: internal1 < hdmi2 < usb3
   EXPECT_EQ(ActiveOutputNodeId(), usb3.id);
 
   Select(hdmi2);
@@ -628,33 +628,38 @@
   EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
 
   Plug(hdmi3);
+  // Devices: [internal1 hdmi3*] headphone2
+  // List: internal1 < hdmi3 < headphone2
+  EXPECT_EQ(ActiveOutputNodeId(), hdmi3.id);
+
+  Select(internal1);
   // Devices: [internal1* hdmi3] headphone2
-  // List: internal1 < headphone2
+  // List: hdmi3 < internal1 < headphone2
   EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
 
   Unplug(hdmi3);
   // Devices: [internal1*] headphone2 hdmi3
-  // List: internal1 < headphone2
+  // List: hdmi3 < internal1 < headphone2
   EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
 
   Plug(headphone2);
   // Devices: [internal1 headphone2*] hdmi3
-  // List: internal1 < headphone2
+  // List: hdmi3 < internal1 < headphone2
   EXPECT_EQ(ActiveOutputNodeId(), headphone2.id);
 
   Unplug(headphone2);
   // Devices: [internal1*] headphone2 hdmi3
-  // List: internal1 < headphone2
+  // List: hdmi3 < internal1 < headphone2
   EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
 
   Plug(hdmi3);
   // Devices: [internal1* hdmi3] headphone2
-  // List: internal1 < headphone2
+  // List: hdmi3 < internal1 < headphone2
   EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
 
   Unplug(hdmi3);
   // Devices: [internal1*] headphone2 hdmi3
-  // List: internal1 < headphone2
+  // List: hdmi3 < internal1 < headphone2
   EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
 }
 
@@ -670,88 +675,88 @@
   EXPECT_EQ(ActiveOutputNodeId(), internal4.id);
 
   Plug(hdmi1);
-  // Devices: [hdmi1 internal4*] hdmi2 headphone3
-  // List: internal4
-  EXPECT_EQ(ActiveOutputNodeId(), internal4.id);
-
-  Plug(hdmi2);
-  // Devices: [hdmi1 hdmi2 internal4*] headphone3
-  // List: internal4
-  EXPECT_EQ(ActiveOutputNodeId(), internal4.id);
-
-  Select(hdmi1);
-  // Devices: [hdmi1* hdmi2 internal4] headphone3
-  // List: internal4 < hdmi1
-  EXPECT_EQ(ActiveOutputNodeId(), hdmi1.id);
-
-  Unplug(hdmi1);
-  // Devices: [hdmi2 internal4*] hdmi1 headphone3
-  // List: internal4 < hdmi1
-  EXPECT_EQ(ActiveOutputNodeId(), internal4.id);
-
-  Unplug(hdmi2);
-  // Devices: [internal4*] hdmi1 hdmi2 headphone3
-  // List: internal4 < hdmi1
-  EXPECT_EQ(ActiveOutputNodeId(), internal4.id);
-
-  Plug(headphone3);
-  // Devices: [headphone3* internal4] hdmi1 hdmi2
-  // List: internal4 < headphone3 < hdmi1
-  EXPECT_EQ(ActiveOutputNodeId(), headphone3.id);
-
-  Unplug(headphone3);
-  // Devices: [internal4*] hdmi1 hdmi2 headphone3
-  // List: internal4 < headphone3 < hdmi1
-  EXPECT_EQ(ActiveOutputNodeId(), internal4.id);
-
-  Plug(hdmi2);
-  // Devices: [hdmi2 internal4*] hdmi1 headphone3
-  // List: internal4 < headphone3 < hdmi1
-  EXPECT_EQ(ActiveOutputNodeId(), internal4.id);
-
-  Plug(hdmi1);
-  // Devices: [hdmi1* hdmi2 internal4] headphone3
-  // List: internal4 < headphone3 < hdmi1
-  EXPECT_EQ(ActiveOutputNodeId(), hdmi1.id);
-
-  Unplug(hdmi1);
-  // Devices: [hdmi2 internal4*] hdmi1 headphone3
-  // List: internal4 < headphone3 < hdmi1
-  EXPECT_EQ(ActiveOutputNodeId(), internal4.id);
-
-  Unplug(hdmi2);
-  // Devices: [internal4*] hdmi1 hdmi2 headphone3
-  // List: internal4 < headphone3 < hdmi1
-  EXPECT_EQ(ActiveOutputNodeId(), internal4.id);
-
-  Plug(hdmi1);
   // Devices: [hdmi1* internal4] hdmi2 headphone3
-  // List: internal4 < headphone3 < hdmi1
+  // List: internal4 < hdmi1
   EXPECT_EQ(ActiveOutputNodeId(), hdmi1.id);
 
   Plug(hdmi2);
   // Devices: [hdmi1 hdmi2* internal4] headphone3
-  // List: internal4 < headphone3 < hdmi1 < hdmi2
+  // List: internal4 < hdmi1 < hdmi2
   EXPECT_EQ(ActiveOutputNodeId(), hdmi2.id);
 
+  Select(hdmi1);
+  // Devices: [hdmi1* hdmi2 internal4] headphone3
+  // List: internal4 < hdmi2 < hdmi1
+  EXPECT_EQ(ActiveOutputNodeId(), hdmi1.id);
+
   Unplug(hdmi1);
   // Devices: [hdmi2* internal4] hdmi1 headphone3
-  // List: internal4 < headphone3 < hdmi1 < hdmi2
+  // List: internal4 < hdmi2 < hdmi1
   EXPECT_EQ(ActiveOutputNodeId(), hdmi2.id);
 
   Unplug(hdmi2);
   // Devices: [internal4*] hdmi1 hdmi2 headphone3
-  // List: internal4 < headphone3 < hdmi1 < hdmi2
+  // List: internal4 < hdmi2 < hdmi1
   EXPECT_EQ(ActiveOutputNodeId(), internal4.id);
 
   Plug(headphone3);
   // Devices: [headphone3* internal4] hdmi1 hdmi2
-  // List: internal4 < headphone3 < hdmi1 < hdmi2
+  // List: internal4 < headphone3 < hdmi2 < hdmi1
   EXPECT_EQ(ActiveOutputNodeId(), headphone3.id);
 
   Unplug(headphone3);
   // Devices: [internal4*] hdmi1 hdmi2 headphone3
-  // List: internal4 < headphone3 < hdmi1 < hdmi2
+  // List: internal4 < headphone3 < hdmi2 < hdmi1
+  EXPECT_EQ(ActiveOutputNodeId(), internal4.id);
+
+  Plug(hdmi2);
+  // Devices: [hdmi2* internal4] hdmi1 headphone3
+  // List: internal4 < headphone3 < hdmi2 < hdmi1
+  EXPECT_EQ(ActiveOutputNodeId(), hdmi2.id);
+
+  Plug(hdmi1);
+  // Devices: [hdmi1* hdmi2 internal4] headphone3
+  // List: internal4 < headphone3 < hdmi2 < hdmi1
+  EXPECT_EQ(ActiveOutputNodeId(), hdmi1.id);
+
+  Unplug(hdmi1);
+  // Devices: [hdmi2* internal4] hdmi1 headphone3
+  // List: internal4 < headphone3 < hdmi2 < hdmi1
+  EXPECT_EQ(ActiveOutputNodeId(), hdmi2.id);
+
+  Unplug(hdmi2);
+  // Devices: [internal4*] hdmi1 hdmi2 headphone3
+  // List: internal4 < headphone3 < hdmi2 < hdmi1
+  EXPECT_EQ(ActiveOutputNodeId(), internal4.id);
+
+  Plug(hdmi1);
+  // Devices: [hdmi1* internal4] hdmi2 headphone3
+  // List: internal4 < headphone3 < hdmi2 < hdmi1
+  EXPECT_EQ(ActiveOutputNodeId(), hdmi1.id);
+
+  Plug(hdmi2);
+  // Devices: [hdmi1* hdmi2 internal4] headphone3
+  // List: internal4 < headphone3 < hdmi2 < hdmi1
+  EXPECT_EQ(ActiveOutputNodeId(), hdmi1.id);
+
+  Unplug(hdmi1);
+  // Devices: [hdmi2* internal4] hdmi1 headphone3
+  // List: internal4 < headphone3 < hdmi2 < hdmi1
+  EXPECT_EQ(ActiveOutputNodeId(), hdmi2.id);
+
+  Unplug(hdmi2);
+  // Devices: [internal4*] hdmi1 hdmi2 headphone3
+  // List: internal4 < headphone3 < hdmi2 < hdmi1
+  EXPECT_EQ(ActiveOutputNodeId(), internal4.id);
+
+  Plug(headphone3);
+  // Devices: [headphone3* internal4] hdmi1 hdmi2
+  // List: internal4 < headphone3 < hdmi2 < hdmi1
+  EXPECT_EQ(ActiveOutputNodeId(), headphone3.id);
+
+  Unplug(headphone3);
+  // Devices: [internal4*] hdmi1 hdmi2 headphone3
+  // List: internal4 < headphone3 < hdmi2 < hdmi1
   EXPECT_EQ(ActiveOutputNodeId(), internal4.id);
 }
 
@@ -766,18 +771,23 @@
   EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
 
   Plug(hdmi2);
+  // Devices: [internal1 hdmi2*] headphone3
+  // List: internal1 < hdmi2
+  EXPECT_EQ(ActiveOutputNodeId(), hdmi2.id);
+
+  Select(internal1);
   // Devices: [internal1* hdmi2] headphone3
-  // List: internal1
+  // List: hdmi2 < internal1
   EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
 
   Plug(headphone3);
   // Devices: [internal1 hdmi2 headphone3*]
-  // List: internal1 < headphone3
+  // List: hdmi2 < internal1 < headphone3
   EXPECT_EQ(ActiveOutputNodeId(), headphone3.id);
 
   Unplug(headphone3);
   // Devices: [internal1* hdmi2] headphone3
-  // List: internal1 < headphone3
+  // List: hdmi2 < internal1 < headphone3
   EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
 }
 
@@ -793,13 +803,13 @@
   EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
 
   Plug(hdmi2);
-  // Devices: [internal1* hdmi2] headphone3 hdmi4
-  // List: internal1
-  EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
+  // Devices: [internal1 hdmi2*] headphone3 hdmi4
+  // List: internal1 < hdmi2
+  EXPECT_EQ(ActiveOutputNodeId(), hdmi2.id);
 
   Plug(headphone3);
   // Devices: [internal1 hdmi2 headphone3*] hdmi4
-  // List: internal1 < headphone3
+  // List: internal1 < hdmi2 < headphone3
   EXPECT_EQ(ActiveOutputNodeId(), headphone3.id);
 
   Select(hdmi2);
@@ -831,13 +841,13 @@
   EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
 
   Plug(hdmi2);
-  // Devices: [internal1* hdmi2] headphone3 hdmi4 hdmi5
-  // List: internal1
-  EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
+  // Devices: [internal1 hdmi2*] headphone3 hdmi4 hdmi5
+  // List: internal1 < hdmi2
+  EXPECT_EQ(ActiveOutputNodeId(), hdmi2.id);
 
   Plug(headphone3);
   // Devices: [internal1 hdmi2 headphone3*] hdmi4 hdmi5
-  // List: internal1 < headphone3
+  // List: internal1 < hdmi2 < headphone3
   EXPECT_EQ(ActiveOutputNodeId(), headphone3.id);
 
   Select(hdmi2);
@@ -888,33 +898,38 @@
   EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
 
   Plug(hdmi2);
-  // Devices: [internal1* hdmi2] hdmi3 hdmi4
-  // List: internal1
-  EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
+  // Devices: [internal1 hdmi2*] hdmi3 hdmi4
+  // List: internal1 < hdmi2
+  EXPECT_EQ(ActiveOutputNodeId(), hdmi2.id);
 
   Plug(hdmi4);
-  // Devices: [internal1* hdmi2 hdmi4] hdmi3
-  // List: internal1
-  EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
+  // Devices: [internal1 hdmi2 hdmi4*] hdmi3
+  // List: internal1 < hdmi2 < hdmi4
+  EXPECT_EQ(ActiveOutputNodeId(), hdmi4.id);
 
   Plug(hdmi3);
-  // Devices: [internal1* hdmi2 hdmi3 hdmi4]
-  // List: internal1
-  EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
+  // Devices: [internal1 hdmi2 hdmi3* hdmi4]
+  // List: internal1 < hdmi2 < hdmi4 < hdmi3
+  EXPECT_EQ(ActiveOutputNodeId(), hdmi3.id);
 
   Select(hdmi4);
   // Devices: [internal1 hdmi2 hdmi3 hdmi4*]
-  // List: internal1 < hdmi4
+  // List: internal1 < hdmi2 < hdmi3 < hdmi4
   EXPECT_EQ(ActiveOutputNodeId(), hdmi4.id);
 
   Unplug(hdmi4);
+  // Devices: [internal1 hdmi2 hdmi3*] hdmi4
+  // List: internal1 < hdmi2 < hdmi3 < hdmi4
+  EXPECT_EQ(ActiveOutputNodeId(), hdmi3.id);
+
+  Select(internal1);
   // Devices: [internal1* hdmi2 hdmi3] hdmi4
-  // List: internal1 < hdmi4
+  // List: hdmi2 < hdmi3 < internal1 < hdmi4
   EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
 
   Plug(hdmi4);
   // Devices: [internal1 hdmi2 hdmi3 hdmi4*]
-  // List: internal1 < hdmi4
+  // List: hdmi2 < hdmi3 < internal1 < hdmi4
   EXPECT_EQ(ActiveOutputNodeId(), hdmi4.id);
 }
 
@@ -985,23 +1000,28 @@
   EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
 
   Plug(hdmi2);
+  // Devices: [internal1 hdmi2*] usb3
+  // List: internal1 < hdmi2 < usb3
+  EXPECT_EQ(ActiveOutputNodeId(), hdmi2.id);
+
+  Select(internal1);
   // Devices: [internal1* hdmi2] usb3
-  // List: internal1 < usb3
+  // List: hdmi2 < internal1 < usb3
   EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
 
   Unplug(hdmi2);
   // Devices: [internal1*] hdmi2 usb3
-  // List: internal1 < usb3
+  // List: hdmi2 < internal1 < usb3
   EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
 
   Plug(usb3);
   // Devices: [internal1 usb3*] hdmi2
-  // List: internal1 < usb3
+  // List: hdmi2 < internal1 < usb3
   EXPECT_EQ(ActiveOutputNodeId(), usb3.id);
 
   Plug(hdmi2);
   // Devices: [internal1 hdmi2 usb3*]
-  // List: internal1 < usb3
+  // List: hdmi2 < internal1 < usb3
   EXPECT_EQ(ActiveOutputNodeId(), usb3.id);
 }
 
@@ -1017,28 +1037,23 @@
   EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
 
   Plug(hdmi2);
-  // Devices: [internal1* hdmi2] hdmi3 usb4
-  // List: internal1
-  EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
+  // Devices: [internal1 hdmi2*] hdmi3 usb4
+  // List: internal1 < hdmi2
+  EXPECT_EQ(ActiveOutputNodeId(), hdmi2.id);
 
   Plug(hdmi3);
-  // Devices: [internal1* hdmi2 hdmi3] usb4
-  // List: internal1
-  EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
-
-  Select(hdmi3);
   // Devices: [internal1 hdmi2 hdmi3*] usb4
-  // List: internal1 < hdmi3
+  // List: internal1 < hdmi2 < hdmi3
   EXPECT_EQ(ActiveOutputNodeId(), hdmi3.id);
 
   Plug(usb4);
   // Devices: [internal1 hdmi2 hdmi3 usb4*]
-  // List: internal1 < hdmi3 < usb4
+  // List: internal1 < hdmi2 < hdmi3 < usb4
   EXPECT_EQ(ActiveOutputNodeId(), usb4.id);
 
   Unplug(usb4);
   // Devices: [internal1 hdmi2 hdmi3*] usb4
-  // List: internal1 < hdmi3 < usb4
+  // List: internal1 < hdmi2 < hdmi3 < usb4
   EXPECT_EQ(ActiveOutputNodeId(), hdmi3.id);
 }
 
@@ -1054,48 +1069,48 @@
   EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
 
   Plug(hdmi2);
-  // Devices: [internal1* hdmi2] hdmi3 usb4
-  // List: internal1
-  EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
+  // Devices: [internal1 hdmi2*] hdmi3 usb4
+  // List: internal1 < hdmi2
+  EXPECT_EQ(ActiveOutputNodeId(), hdmi2.id);
 
   Plug(hdmi3);
-  // Devices: [internal1* hdmi2 hdmi3] usb4
-  // List: internal1
-  EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
+  // Devices: [internal1 hdmi2 hdmi3*] usb4
+  // List: internal1 < hdmi2 < hdmi3
+  EXPECT_EQ(ActiveOutputNodeId(), hdmi3.id);
 
   Plug(usb4);
   // Devices: [internal1 hdmi2 hdmi3 usb4*]
-  // List: internal1 < usb4
+  // List: internal1 < hdmi2 < hdmi3 < usb4
   EXPECT_EQ(ActiveOutputNodeId(), usb4.id);
 
   Unplug(hdmi2);
   // Devices: [internal1 hdmi3 usb4*] hdmi2
-  // List: internal1 < usb4
+  // List: internal1 < hdmi2 < hdmi3 < usb4
   EXPECT_EQ(ActiveOutputNodeId(), usb4.id);
 
   Unplug(hdmi3);
   // Devices: [internal1 usb4*] hdmi2 hdmi3
-  // List: internal1 < usb4
+  // List: internal1 < hdmi2 < hdmi3 < usb4
   EXPECT_EQ(ActiveOutputNodeId(), usb4.id);
 
   Unplug(usb4);
   // Devices: [internal1*] hdmi2 hdmi3 usb4
-  // List: internal1 < usb4
+  // List: internal1 < hdmi2 < hdmi3 < usb4
   EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
 
   Plug(hdmi2);
-  // Devices: [internal1* hdmi2] hdmi3 usb4
-  // List: internal1 < usb4
-  EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
+  // Devices: [internal1 hdmi2*] hdmi3 usb4
+  // List: internal1 < hdmi2 < hdmi3 < usb4
+  EXPECT_EQ(ActiveOutputNodeId(), hdmi2.id);
 
   Plug(hdmi3);
-  // Devices: [internal1* hdmi2 hdmi3] usb4
-  // List: internal1 < usb4
-  EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
+  // Devices: [internal1 hdmi2 hdmi3*] usb4
+  // List: internal1 < hdmi2 < hdmi3 < usb4
+  EXPECT_EQ(ActiveOutputNodeId(), hdmi3.id);
 
   Plug(usb4);
   // Devices: [internal1 hdmi2 hdmi3 usb4*]
-  // List: internal1 < usb4
+  // List: internal1 < hdmi2 < hdmi3 < usb4
   EXPECT_EQ(ActiveOutputNodeId(), usb4.id);
 }
 
@@ -1110,31 +1125,6 @@
   EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
 
   Plug(hdmi2);
-  // Devices: [internal1* hdmi2] hdmi3
-  // List: internal1
-  EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
-
-  Plug(hdmi3);
-  // Devices: [internal1* hdmi2 hdmi3]
-  // List: internal1
-  EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
-
-  Select(hdmi2);
-  // Devices: [internal1 hdmi2* hdmi3]
-  // List: internal1 < hdmi2
-  EXPECT_EQ(ActiveOutputNodeId(), hdmi2.id);
-
-  Unplug(hdmi2);
-  // Devices: [internal1* hdmi3] hdmi2
-  // List: internal1 < hdmi2
-  EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
-
-  Unplug(hdmi3);
-  // Devices: [internal1*] hdmi2 hdmi3
-  // List: internal1 < hdmi2
-  EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
-
-  Plug(hdmi2);
   // Devices: [internal1 hdmi2*] hdmi3
   // List: internal1 < hdmi2
   EXPECT_EQ(ActiveOutputNodeId(), hdmi2.id);
@@ -1143,6 +1133,31 @@
   // Devices: [internal1 hdmi2 hdmi3*]
   // List: internal1 < hdmi2 < hdmi3
   EXPECT_EQ(ActiveOutputNodeId(), hdmi3.id);
+
+  Select(hdmi2);
+  // Devices: [internal1 hdmi2* hdmi3]
+  // List: internal1 < hdmi3 < hdmi2
+  EXPECT_EQ(ActiveOutputNodeId(), hdmi2.id);
+
+  Unplug(hdmi2);
+  // Devices: [internal1 hdmi3*] hdmi2
+  // List: internal1 < hdmi3 < hdmi2
+  EXPECT_EQ(ActiveOutputNodeId(), hdmi3.id);
+
+  Unplug(hdmi3);
+  // Devices: [internal1*] hdmi2 hdmi3
+  // List: internal1 < hdmi3 < hdmi2
+  EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
+
+  Plug(hdmi2);
+  // Devices: [internal1 hdmi2*] hdmi3
+  // List: internal1 < hdmi3 < hdmi2
+  EXPECT_EQ(ActiveOutputNodeId(), hdmi2.id);
+
+  Plug(hdmi3);
+  // Devices: [internal1 hdmi2* hdmi3]
+  // List: internal1 < hdmi3 < hdmi2
+  EXPECT_EQ(ActiveOutputNodeId(), hdmi2.id);
 }
 
 TEST_F(AudioDeviceSelectionGeneratedTest, FeedbackComment8Output) {
@@ -1156,18 +1171,23 @@
   EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
 
   Plug(hdmi2);
+  // Devices: [internal1 hdmi2*] headphone3
+  // List: internal1 < hdmi2
+  EXPECT_EQ(ActiveOutputNodeId(), hdmi2.id);
+
+  Select(internal1);
   // Devices: [internal1* hdmi2] headphone3
-  // List: internal1
+  // List: hdmi2 < internal1
   EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
 
   Plug(headphone3);
   // Devices: [internal1 hdmi2 headphone3*]
-  // List: internal1 < headphone3
+  // List: hdmi2 < internal1 < headphone3
   EXPECT_EQ(ActiveOutputNodeId(), headphone3.id);
 
   Unplug(headphone3);
   // Devices: [internal1* hdmi2] headphone3
-  // List: internal1 < headphone3
+  // List: hdmi2 < internal1 < headphone3
   EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
 }
 
@@ -1181,18 +1201,23 @@
   EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
 
   Plug(hdmi2);
+  // Devices: [internal1 hdmi2*]
+  // List: internal1 < hdmi2
+  EXPECT_EQ(ActiveOutputNodeId(), hdmi2.id);
+
+  Select(internal1);
   // Devices: [internal1* hdmi2]
-  // List: internal1
+  // List: hdmi2 < internal1
   EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
 
   Unplug(hdmi2);
   // Devices: [internal1*] hdmi2
-  // List: internal1
+  // List: hdmi2 < internal1
   EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
 
   Plug(hdmi2);
   // Devices: [internal1* hdmi2]
-  // List: internal1
+  // List: hdmi2 < internal1
   EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
 }
 
@@ -1207,18 +1232,23 @@
   EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
 
   Plug(hdmi2);
+  // Devices: [internal1 hdmi2*] headphone3
+  // List: internal1 < hdmi2
+  EXPECT_EQ(ActiveOutputNodeId(), hdmi2.id);
+
+  Select(internal1);
   // Devices: [internal1* hdmi2] headphone3
-  // List: internal1
+  // List: hdmi2 < internal1
   EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
 
   Plug(headphone3);
   // Devices: [internal1 hdmi2 headphone3*]
-  // List: internal1 < headphone3
+  // List: hdmi2 < internal1 < headphone3
   EXPECT_EQ(ActiveOutputNodeId(), headphone3.id);
 
   Unplug(headphone3);
   // Devices: [internal1* hdmi2] headphone3
-  // List: internal1 < headphone3
+  // List: hdmi2 < internal1 < headphone3
   EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
 }
 
@@ -1233,14 +1263,14 @@
   EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
 
   Plug(hdmi2);
-  // Devices: [internal1* hdmi2] hdmi3
-  // List: internal1
-  EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
+  // Devices: [internal1 hdmi2*] hdmi3
+  // List: internal1 < hdmi2
+  EXPECT_EQ(ActiveOutputNodeId(), hdmi2.id);
 
   Plug(hdmi3);
-  // Devices: [internal1* hdmi2 hdmi3]
-  // List: internal1
-  EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
+  // Devices: [internal1 hdmi2 hdmi3*]
+  // List: internal1 < hdmi2 < hdmi3
+  EXPECT_EQ(ActiveOutputNodeId(), hdmi3.id);
 }
 
 TEST_F(AudioDeviceSelectionGeneratedTest, GreendocM3Output) {
@@ -1255,28 +1285,28 @@
   EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
 
   Plug(hdmi2);
-  // Devices: [internal1* hdmi2] hdmi3 headphone4
-  // List: internal1
-  EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
+  // Devices: [internal1 hdmi2*] hdmi3 headphone4
+  // List: internal1 < hdmi2
+  EXPECT_EQ(ActiveOutputNodeId(), hdmi2.id);
 
   Plug(hdmi3);
-  // Devices: [internal1* hdmi2 hdmi3] headphone4
-  // List: internal1
-  EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
+  // Devices: [internal1 hdmi2 hdmi3*] headphone4
+  // List: internal1 < hdmi2 < hdmi3
+  EXPECT_EQ(ActiveOutputNodeId(), hdmi3.id);
 
   Select(hdmi2);
   // Devices: [internal1 hdmi2* hdmi3] headphone4
-  // List: internal1 < hdmi2
+  // List: internal1 < hdmi3 < hdmi2
   EXPECT_EQ(ActiveOutputNodeId(), hdmi2.id);
 
   Plug(headphone4);
   // Devices: [internal1 hdmi2 hdmi3 headphone4*]
-  // List: internal1 < hdmi2 < headphone4
+  // List: internal1 < hdmi3 < hdmi2 < headphone4
   EXPECT_EQ(ActiveOutputNodeId(), headphone4.id);
 
   Unplug(headphone4);
   // Devices: [internal1 hdmi2* hdmi3] headphone4
-  // List: internal1 < hdmi2 < headphone4
+  // List: internal1 < hdmi3 < hdmi2 < headphone4
   EXPECT_EQ(ActiveOutputNodeId(), hdmi2.id);
 }
 
@@ -1291,33 +1321,33 @@
   EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
 
   Plug(hdmi2);
+  // Devices: [internal1 hdmi2*] hdmi3
+  // List: internal1 < hdmi2
+  EXPECT_EQ(ActiveOutputNodeId(), hdmi2.id);
+
+  Select(internal1);
   // Devices: [internal1* hdmi2] hdmi3
-  // List: internal1
+  // List: hdmi2 < internal1
   EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
 
   Unplug(hdmi2);
   // Devices: [internal1*] hdmi2 hdmi3
-  // List: internal1
+  // List: hdmi2 < internal1
   EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
 
   Plug(hdmi3);
-  // Devices: [internal1* hdmi3] hdmi2
-  // List: internal1
-  EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
-
-  Select(hdmi3);
   // Devices: [internal1 hdmi3*] hdmi2
-  // List: internal1 < hdmi3
+  // List: hdmi2 < internal1 < hdmi3
   EXPECT_EQ(ActiveOutputNodeId(), hdmi3.id);
 
   Unplug(hdmi3);
   // Devices: [internal1*] hdmi2 hdmi3
-  // List: internal1 < hdmi3
+  // List: hdmi2 < internal1 < hdmi3
   EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
 
   Plug(hdmi2);
   // Devices: [internal1* hdmi2] hdmi3
-  // List: internal1 < hdmi3
+  // List: hdmi2 < internal1 < hdmi3
   EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
 }
 
@@ -1332,28 +1362,33 @@
   EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
 
   Plug(hdmi2);
+  // Devices: [internal1 hdmi2*] headphone3
+  // List: internal1 < hdmi2
+  EXPECT_EQ(ActiveOutputNodeId(), hdmi2.id);
+
+  Select(internal1);
   // Devices: [internal1* hdmi2] headphone3
-  // List: internal1
+  // List: hdmi2 < internal1
   EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
 
   Unplug(hdmi2);
   // Devices: [internal1*] hdmi2 headphone3
-  // List: internal1
+  // List: hdmi2 < internal1
   EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
 
   Plug(headphone3);
   // Devices: [internal1 headphone3*] hdmi2
-  // List: internal1 < headphone3
+  // List: hdmi2 < internal1 < headphone3
   EXPECT_EQ(ActiveOutputNodeId(), headphone3.id);
 
   Unplug(headphone3);
   // Devices: [internal1*] hdmi2 headphone3
-  // List: internal1 < headphone3
+  // List: hdmi2 < internal1 < headphone3
   EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
 
   Plug(hdmi2);
   // Devices: [internal1* hdmi2] headphone3
-  // List: internal1 < headphone3
+  // List: hdmi2 < internal1 < headphone3
   EXPECT_EQ(ActiveOutputNodeId(), internal1.id);
 }
 
diff --git a/chromeos/ash/components/audio/cras_audio_handler_unittest.cc b/chromeos/ash/components/audio/cras_audio_handler_unittest.cc
index d8a0e87..7ea8961 100644
--- a/chromeos/ash/components/audio/cras_audio_handler_unittest.cc
+++ b/chromeos/ash/components/audio/cras_audio_handler_unittest.cc
@@ -1187,7 +1187,7 @@
   SetupAudioNodesAndExpectActiveNodes(
       /*initial_nodes=*/{kInternalSpeaker, kHDMIOutput},
       /*expected_active_input_node=*/nullptr,
-      /*expected_active_output_node=*/kInternalSpeaker,
+      /*expected_active_output_node=*/kHDMIOutput,
       /*expected_has_alternative_input=*/std::nullopt,
       /*expected_has_alternative_output=*/true);
 }
@@ -1219,21 +1219,16 @@
   cras_audio_handler_->GetAudioDevices(&audio_devices);
   EXPECT_EQ(2u, audio_devices.size());
 
-  // Verify the active output device is not switched to hdmi output, and
-  // ActiveOutputChanged event is not fired.
-  EXPECT_EQ(0, test_observer_->active_output_node_changed_count());
-  ExpectActiveDevice(/*is_input=*/false,
-                     /*expected_active_device=*/kInternalSpeaker,
+  // Verify the active output device is switched to hdmi output, and
+  // ActiveOutputChanged event is fired.
+  EXPECT_EQ(1, test_observer_->active_output_node_changed_count());
+  ExpectActiveDevice(/*is_input=*/false, /*expected_active_device=*/kHDMIOutput,
                      /*has_alternative_device=*/true);
   system_monitor_observer_.reset_count();
 
   // Disconnect hdmi headset.
   audio_nodes.clear();
-  {
-    AudioNode active_internal_speaker = GenerateAudioNode(kInternalSpeaker);
-    active_internal_speaker.active = true;
-    audio_nodes.push_back(active_internal_speaker);
-  }
+  audio_nodes.push_back(GenerateAudioNode(kInternalSpeaker));
   ChangeAudioNodes(audio_nodes);
 
   // Verify the AudioNodesChanged event is fired and one audio device is
@@ -1243,9 +1238,9 @@
   cras_audio_handler_->GetAudioDevices(&audio_devices);
   EXPECT_EQ(1u, audio_devices.size());
 
-  // Verify the active output device is still the internal speaker, and
-  // ActiveOutputChanged event is not fired.
-  EXPECT_EQ(0, test_observer_->active_output_node_changed_count());
+  // Verify the active output device is switched to internal speaker, and
+  // ActiveOutputChanged event is fired.
+  EXPECT_EQ(2, test_observer_->active_output_node_changed_count());
   ExpectActiveDevice(/*is_input=*/false,
                      /*expected_active_device=*/kInternalSpeaker,
                      /*has_alternative_device=*/false);
@@ -1344,11 +1339,10 @@
   cras_audio_handler_->GetAudioDevices(&audio_devices);
   EXPECT_EQ(2u, audio_devices.size());
 
-  // Verify the active output device is switched to internal speaker, and
+  // Verify the active output device is switched to HDMI output, and
   // ActiveOutputChanged event is fired.
   EXPECT_EQ(1, test_observer_->active_output_node_changed_count());
-  ExpectActiveDevice(/*is_input=*/false,
-                     /*expected_active_device=*/kInternalSpeaker,
+  ExpectActiveDevice(/*is_input=*/false, /*expected_active_device=*/kHDMIOutput,
                      /*has_alternative_device=*/true);
 }
 
@@ -5029,7 +5023,7 @@
 // priority
 // output devices already plugged and user has manually selected an active
 // output.
-TEST_P(CrasAudioHandlerTest, HotPlugHDMINotChangeActiveOutput) {
+TEST_P(CrasAudioHandlerTest, HotPlugHDMIChangeActiveOutput) {
   AudioNodeList audio_nodes;
   AudioNode internal_speaker = GenerateAudioNode(kInternalSpeaker);
   audio_nodes.push_back(internal_speaker);
@@ -5069,10 +5063,9 @@
   audio_nodes.push_back(hdmi);
   ChangeAudioNodes(audio_nodes);
 
-  // The active output does not change to hdmi as it has lower built-in priority
-  // than the internal speaker.
-  EXPECT_EQ(kInternalSpeakerId,
-            cras_audio_handler_->GetPrimaryActiveOutputNode());
+  // The active output change to hdmi as it has higher built-in priority than
+  // the internal speaker.
+  EXPECT_EQ(kHDMIOutputId, cras_audio_handler_->GetPrimaryActiveOutputNode());
 }
 
 TEST_P(CrasAudioHandlerTest,
@@ -5152,18 +5145,24 @@
 // if it has a higher priority than the current active node.
 // crbug.com/443014.
 TEST_P(CrasAudioHandlerTest, HDMIRemainInactiveAfterSuspendResume) {
-  // Verify the internal speaker is selected as the active output since it has a
-  // higher priority.
+  // Verify the hdmi is selected as the active output since it has a higher
+  // priority.
   SetupAudioNodesAndExpectActiveNodes(
       /*initial_nodes=*/{kInternalSpeaker, kHDMIOutput},
       /*expected_active_input_node=*/nullptr,
-      /*expected_active_output_node=*/kInternalSpeaker,
+      /*expected_active_output_node=*/kHDMIOutput,
       /*expected_has_alternative_input=*/std::nullopt,
       /*expected_has_alternative_output=*/true);
 
+  // Manually set the active output to internal speaker.
+  AudioNode internal_speaker = GenerateAudioNode(kInternalSpeaker);
+  cras_audio_handler_->SwitchToDevice(AudioDevice(internal_speaker), true,
+                                      DeviceActivateType::kActivateByUser);
+  EXPECT_EQ(internal_speaker.id,
+            cras_audio_handler_->GetPrimaryActiveOutputNode());
+
   // Simulate the suspend and resume of the device during mirror mode. The HDMI
   // node will be lost first.
-  AudioNode internal_speaker = GenerateAudioNode(kInternalSpeaker);
   AudioNodeList audio_nodes;
   internal_speaker.active = true;
   audio_nodes.push_back(internal_speaker);
@@ -5231,19 +5230,15 @@
 
 // This test HDMI output rediscovering case in crbug.com/503667.
 TEST_P(CrasAudioHandlerTest, HDMIOutputRediscover) {
-  // Prepare and switch to HDMI, and verify audio output is not muted.
+  // Verify the HDMI device has been selected as the active output, and audio
+  // output is not muted.
   SetupAudioNodesAndExpectActiveNodes(
       /*initial_nodes=*/{kInternalSpeaker, kHDMIOutput},
       /*expected_active_input_node=*/nullptr,
-      /*expected_active_output_node=*/kInternalSpeaker,
+      /*expected_active_output_node=*/kHDMIOutput,
       /*expected_has_alternative_input=*/std::nullopt,
       /*expected_has_alternative_output=*/true);
 
-  // Manually set the active output to HDMI.
-  AudioNode hdmi_output = GenerateAudioNode(kHDMIOutput);
-  cras_audio_handler_->SwitchToDevice(AudioDevice(hdmi_output), true,
-                                      DeviceActivateType::kActivateByUser);
-
   EXPECT_FALSE(cras_audio_handler_->IsOutputMuted());
 
   // Trigger HDMI rediscovering grace period, and remove the HDMI node.
@@ -5284,19 +5279,15 @@
 // This tests the case of output unmuting event is not notified after the hdmi
 // output re-discover grace period ends.
 TEST_P(CrasAudioHandlerTest, HDMIOutputUnplugDuringSuspension) {
-  // Prepare and switch to HDMI, and verify audio output is not muted.
+  // Verify the HDMI device has been selected as the active output, and audio
+  // output is not muted.
   SetupAudioNodesAndExpectActiveNodes(
       /*initial_nodes=*/{kInternalSpeaker, kHDMIOutput},
       /*expected_active_input_node=*/nullptr,
-      /*expected_active_output_node=*/kInternalSpeaker,
+      /*expected_active_output_node=*/kHDMIOutput,
       /*expected_has_alternative_input=*/std::nullopt,
       /*expected_has_alternative_output=*/true);
 
-  // Manually set the active output to HDMI.
-  AudioNode hdmi_output = GenerateAudioNode(kHDMIOutput);
-  cras_audio_handler_->SwitchToDevice(AudioDevice(hdmi_output), true,
-                                      DeviceActivateType::kActivateByUser);
-
   EXPECT_FALSE(cras_audio_handler_->IsOutputMuted());
 
   // Trigger HDMI rediscovering grace period, and remove the HDMI node.
@@ -6979,9 +6970,9 @@
 TEST_P(CrasAudioHandlerTest,
        AlternativeInputDeviceWithOneInternalAndOneExternalOutputDevice) {
   SetupAudioNodesAndExpectActiveNodes(
-      /*initial_nodes=*/{kInternalSpeaker, kUSBHeadphone1},
+      /*initial_nodes=*/{kInternalSpeaker, kHDMIOutput},
       /*expected_active_input_node=*/nullptr,
-      /*expected_active_output_node=*/kUSBHeadphone1,
+      /*expected_active_output_node=*/kHDMIOutput,
       /*expected_has_alternative_input=*/std::nullopt,
       /*expected_has_alternative_output=*/true);
 }
diff --git a/chromeos/ash/components/audio/device_selection_test_gen/dsp.py b/chromeos/ash/components/audio/device_selection_test_gen/dsp.py
index 6285032..95a3186 100644
--- a/chromeos/ash/components/audio/device_selection_test_gen/dsp.py
+++ b/chromeos/ash/components/audio/device_selection_test_gen/dsp.py
@@ -32,8 +32,8 @@
 class T:
     Headphone = Type("Headphone", 4, False)
     USB = Type("USB", 3, False)
-    Internal = Type("Internal", 2, True)
-    HDMI = Type("HDMI", 1, True)
+    HDMI = Type("HDMI", 2, True)
+    Internal = Type("Internal", 1, True)
 
 
 @dataclasses.dataclass(frozen=True)
diff --git a/chromeos/ash/components/kcer/kcer_nss/kcer_nss_fuzzer.cc b/chromeos/ash/components/kcer/kcer_nss/kcer_nss_fuzzer.cc
index a45b75f08..5ea7cb8 100644
--- a/chromeos/ash/components/kcer/kcer_nss/kcer_nss_fuzzer.cc
+++ b/chromeos/ash/components/kcer/kcer_nss/kcer_nss_fuzzer.cc
@@ -883,6 +883,12 @@
   if (!cert) {
     return;
   }
+  net::ScopedCERTCertificate nss_cert =
+      net::x509_util::CreateCERTCertificateFromX509Certificate(cert.get());
+  if (!nss_cert) {
+    // NSS doesn't consider the cert valid.
+    return;
+  }
 
   base::span<const uint8_t> cert_data = GetCertData(cert);
   CertDer cert_der(std::vector<uint8_t>(cert_data.begin(), cert_data.end()));
diff --git a/chromeos/ash/components/policy/device_local_account/BUILD.gn b/chromeos/ash/components/policy/device_local_account/BUILD.gn
new file mode 100644
index 0000000..c75baca
--- /dev/null
+++ b/chromeos/ash/components/policy/device_local_account/BUILD.gn
@@ -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.
+
+assert(is_chromeos, "Non-Chrome-OS builds must not depend on //chromeos")
+
+component("device_local_account") {
+  defines = [ "IS_CHROMEOS_ASH_COMPONENTS_POLICY_DEVICE_LOCAL_ACCOUNT_IMPL" ]
+
+  sources = [
+    "device_local_account_type.cc",
+    "device_local_account_type.h",
+  ]
+
+  deps = [
+    "//base",
+    "//google_apis",
+
+    # TODO(crbug.com/408460168): Only for a feature flag.
+    # Remove after the feature is released.
+    "//ash/constants",
+  ]
+}
diff --git a/chromeos/ash/components/policy/device_local_account/DEPS b/chromeos/ash/components/policy/device_local_account/DEPS
new file mode 100644
index 0000000..c1a9d257
--- /dev/null
+++ b/chromeos/ash/components/policy/device_local_account/DEPS
@@ -0,0 +1,6 @@
+include_rules = [
+  # This package must not depend on policy to avoid circular deps.
+  # We expect some of the policy implementation uses device local account
+  # concepts, instead. (i.e., dependency from other policy code to here).
+  "-components/policy",
+]
diff --git a/components/policy/core/common/device_local_account_type.cc b/chromeos/ash/components/policy/device_local_account/device_local_account_type.cc
similarity index 94%
rename from components/policy/core/common/device_local_account_type.cc
rename to chromeos/ash/components/policy/device_local_account/device_local_account_type.cc
index 1146819d..be2c0243 100644
--- a/components/policy/core/common/device_local_account_type.cc
+++ b/chromeos/ash/components/policy/device_local_account/device_local_account_type.cc
@@ -2,16 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/policy/core/common/device_local_account_type.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 
 #include <string_view>
 
+#include "ash/constants/ash_features.h"
 #include "base/containers/fixed_flat_map.h"
 #include "base/notreached.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
-#include "components/policy/core/common/features.h"
 #include "google_apis/gaia/gaia_auth_util.h"
 
 namespace policy {
@@ -25,7 +25,6 @@
         {DeviceLocalAccountType::kSamlPublicSession, "saml-public-accounts"},
         {DeviceLocalAccountType::kWebKioskApp, "web-kiosk-apps"},
         {DeviceLocalAccountType::kKioskIsolatedWebApp, "isolated-kiosk-apps"},
-
     });
 
 constexpr char kDeviceLocalAccountDomainSuffix[] = ".device-local.localhost";
@@ -41,7 +40,7 @@
     case DeviceLocalAccountType::kKioskIsolatedWebApp:
       return true;
     case DeviceLocalAccountType::kArcvmKioskApp:
-      return policy::features::IsHeliumArcvmKioskEnabled();
+      return ash::features::IsHeliumArcvmKioskEnabled();
   }
   return false;
 }
diff --git a/components/policy/core/common/device_local_account_type.h b/chromeos/ash/components/policy/device_local_account/device_local_account_type.h
similarity index 65%
rename from components/policy/core/common/device_local_account_type.h
rename to chromeos/ash/components/policy/device_local_account/device_local_account_type.h
index a62c153..c8e3052 100644
--- a/components/policy/core/common/device_local_account_type.h
+++ b/chromeos/ash/components/policy/device_local_account/device_local_account_type.h
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_POLICY_CORE_COMMON_DEVICE_LOCAL_ACCOUNT_TYPE_H_
-#define COMPONENTS_POLICY_CORE_COMMON_DEVICE_LOCAL_ACCOUNT_TYPE_H_
+#ifndef CHROMEOS_ASH_COMPONENTS_POLICY_DEVICE_LOCAL_ACCOUNT_DEVICE_LOCAL_ACCOUNT_TYPE_H_
+#define CHROMEOS_ASH_COMPONENTS_POLICY_DEVICE_LOCAL_ACCOUNT_DEVICE_LOCAL_ACCOUNT_TYPE_H_
 
 #include <string>
 #include <string_view>
 
+#include "base/component_export.h"
 #include "base/types/expected.h"
-#include "components/policy/policy_export.h"
 
 namespace policy {
 
@@ -42,11 +42,12 @@
 };
 
 // Returns whether the given value is valid DeviceLocalAccountType.
-POLICY_EXPORT bool IsValidDeviceLocalAccountType(int value);
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_POLICY_DEVICE_LOCAL_ACCOUNT)
+bool IsValidDeviceLocalAccountType(int value);
 
-POLICY_EXPORT std::string GenerateDeviceLocalAccountUserId(
-    std::string_view account_id,
-    DeviceLocalAccountType type);
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_POLICY_DEVICE_LOCAL_ACCOUNT)
+std::string GenerateDeviceLocalAccountUserId(std::string_view account_id,
+                                             DeviceLocalAccountType type);
 
 enum class GetDeviceLocalAccountTypeError {
   kNoDeviceLocalAccountUser,
@@ -54,15 +55,16 @@
 };
 
 // Returns the type of device-local account.
-POLICY_EXPORT
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_POLICY_DEVICE_LOCAL_ACCOUNT)
 base::expected<DeviceLocalAccountType, GetDeviceLocalAccountTypeError>
 GetDeviceLocalAccountType(std::string_view user_id);
 
 // Returns whether |user_id| belongs to a device-local account.
 // This is equivalent to that GetDeviceLocalAccountType does not return
 // kNoDeviceLocalAccountUser error.
-POLICY_EXPORT bool IsDeviceLocalAccountUser(std::string_view user_id);
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_POLICY_DEVICE_LOCAL_ACCOUNT)
+bool IsDeviceLocalAccountUser(std::string_view user_id);
 
 }  // namespace policy
 
-#endif  // COMPONENTS_POLICY_CORE_COMMON_DEVICE_LOCAL_ACCOUNT_TYPE_H_
+#endif  // CHROMEOS_ASH_COMPONENTS_POLICY_DEVICE_LOCAL_ACCOUNT_DEVICE_LOCAL_ACCOUNT_TYPE_H_
diff --git a/chromeos/ash/components/policy/system_features_disable_list/system_features_disable_list_policy_utils.cc b/chromeos/ash/components/policy/system_features_disable_list/system_features_disable_list_policy_utils.cc
index 4986140..4fe31b5c 100644
--- a/chromeos/ash/components/policy/system_features_disable_list/system_features_disable_list_policy_utils.cc
+++ b/chromeos/ash/components/policy/system_features_disable_list/system_features_disable_list_policy_utils.cc
@@ -13,6 +13,12 @@
 
 namespace policy {
 
+void RegisterDisabledSystemFeaturesPrefs(PrefRegistrySimple* registry) {
+  registry->RegisterListPref(policy_prefs::kSystemFeaturesDisableList);
+  registry->RegisterStringPref(policy_prefs::kSystemFeaturesDisableMode,
+                               kSystemFeaturesDisableModeBlocked);
+}
+
 bool IsDisabledAppsModeHidden(const PrefService& local_state) {
   const bool is_disabled_apps_mode_hidden_pref =
       local_state.GetString(policy::policy_prefs::kSystemFeaturesDisableMode) ==
diff --git a/chromeos/ash/components/policy/system_features_disable_list/system_features_disable_list_policy_utils.h b/chromeos/ash/components/policy/system_features_disable_list/system_features_disable_list_policy_utils.h
index b942f2f3..c8ae8232 100644
--- a/chromeos/ash/components/policy/system_features_disable_list/system_features_disable_list_policy_utils.h
+++ b/chromeos/ash/components/policy/system_features_disable_list/system_features_disable_list_policy_utils.h
@@ -6,10 +6,16 @@
 #define CHROMEOS_ASH_COMPONENTS_POLICY_SYSTEM_FEATURES_DISABLE_LIST_SYSTEM_FEATURES_DISABLE_LIST_POLICY_UTILS_H_
 
 #include "base/component_export.h"
+#include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 
 namespace policy {
 
+// Registers prefs corresponding to the SystemFeaturesDisableList and
+// SystemFeaturesDisableMode policies.
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_POLICY)
+void RegisterDisabledSystemFeaturesPrefs(PrefRegistrySimple* registry);
+
 // Whether the icons of apps disabled by the SystemFeaturesDisableList policy
 // are hidden or blocked.
 // In managed guest sessions (MGS), this is configured by policy
diff --git a/chromeos/ash/components/policy/system_features_disable_list/system_features_disable_list_policy_utils_unittest.cc b/chromeos/ash/components/policy/system_features_disable_list/system_features_disable_list_policy_utils_unittest.cc
index 7658617..979cbfd 100644
--- a/chromeos/ash/components/policy/system_features_disable_list/system_features_disable_list_policy_utils_unittest.cc
+++ b/chromeos/ash/components/policy/system_features_disable_list/system_features_disable_list_policy_utils_unittest.cc
@@ -8,11 +8,11 @@
 
 #include "base/functional/callback_helpers.h"
 #include "base/test/scoped_feature_list.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "chromeos/ash/components/settings/cros_settings.h"
 #include "chromeos/ash/components/settings/fake_cros_settings_provider.h"
 #include "chromeos/constants/chromeos_features.h"
 #include "components/account_id/account_id.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "components/policy/core/common/policy_pref_names.h"
 #include "components/policy/core/common/system_features_disable_list_constants.h"
 #include "components/prefs/pref_registry_simple.h"
@@ -73,13 +73,7 @@
     cros_settings_->AddSettingsProvider(std::move(provider));
 
     user_manager::UserManagerImpl::RegisterPrefs(local_state_.registry());
-    // TODO(414768321): Share pref registration with prod code in
-    // SystemFeaturesDisableListPolicyHandler.
-    local_state_.registry()->RegisterListPref(
-        policy_prefs::kSystemFeaturesDisableList);
-    local_state_.registry()->RegisterStringPref(
-        policy_prefs::kSystemFeaturesDisableMode,
-        kSystemFeaturesDisableModeBlocked);
+    RegisterDisabledSystemFeaturesPrefs(local_state_.registry());
 
     // Register users
     const auto user_account_id =
diff --git a/chromeos/ash/experiences/arc/arc_export.h b/chromeos/ash/experiences/arc/arc_export.h
index 12a58a9..4dd237d 100644
--- a/chromeos/ash/experiences/arc/arc_export.h
+++ b/chromeos/ash/experiences/arc/arc_export.h
@@ -9,10 +9,6 @@
 
 static_assert(BUILDFLAG(IS_CHROMEOS), "ARC can be built only for ChromeOS.");
 
-#if defined(COMPONENT_BUILD) && defined(ARC_IMPLEMENTATION)
 #define ARC_EXPORT __attribute__((visibility("default")))
-#else  // !defined(COMPONENT_BUILD) || !defined(ARC_IMPLEMENTATION)
-#define ARC_EXPORT
-#endif
 
 #endif  // CHROMEOS_ASH_EXPERIENCES_ARC_ARC_EXPORT_H_
diff --git a/chromeos/ash/services/BUILD.gn b/chromeos/ash/services/BUILD.gn
index dae95a9..4049f4e 100644
--- a/chromeos/ash/services/BUILD.gn
+++ b/chromeos/ash/services/BUILD.gn
@@ -17,7 +17,6 @@
   testonly = true
   deps = [
     "//ash:test_support",
-    "//chromeos/ash/services/assistant:tests",
     "//chromeos/ash/services/bluetooth_config:unit_tests",
     "//chromeos/ash/services/boca/babelorca/cpp:unit_tests",
     "//chromeos/ash/services/cellular_setup:unit_tests",
@@ -40,11 +39,4 @@
     "//chromeos/ash/services/wifi_direct:unit_tests",
     "//chromeos/services/machine_learning/public/cpp:ash_unit_tests",
   ]
-
-  if (enable_cros_libassistant) {
-    deps += [
-      "//chromeos/ash/services/libassistant:unit_tests",
-      "//chromeos/ash/services/libassistant/grpc:unit_tests",
-    ]
-  }
 }
diff --git a/chromeos/ash/services/assistant/BUILD.gn b/chromeos/ash/services/assistant/BUILD.gn
index d1ce8ce..c9570e0d 100644
--- a/chromeos/ash/services/assistant/BUILD.gn
+++ b/chromeos/ash/services/assistant/BUILD.gn
@@ -6,168 +6,14 @@
 
 assert(is_chromeos)
 
-component("lib") {
-  output_name = "assistant_service"
-
-  friend = [
-    ":tests",
-    ":test_support",
-  ]
-
-  defines = [ "IS_ASSISTANT_SERVICE_IMPL" ]
-
-  sources = [
-    "assistant_host.cc",
-    "assistant_host.h",
-    "assistant_interaction_logger.cc",
-    "assistant_interaction_logger.h",
-    "assistant_manager_service.cc",
-    "assistant_manager_service.h",
-    "assistant_manager_service_impl.cc",
-    "assistant_manager_service_impl.h",
-    "assistant_settings_impl.cc",
-    "assistant_settings_impl.h",
-    "device_settings_host.cc",
-    "device_settings_host.h",
-    "libassistant_service_host.h",
-    "libassistant_service_host_impl.cc",
-    "libassistant_service_host_impl.h",
-    "media_host.cc",
-    "media_host.h",
-    "media_session/assistant_media_session.cc",
-    "media_session/assistant_media_session.h",
-    "platform/audio_devices.cc",
-    "platform/audio_devices.h",
-    "platform/audio_input_host.h",
-    "platform/audio_input_host_impl.cc",
-    "platform/audio_input_host_impl.h",
-    "platform/audio_output_delegate_impl.cc",
-    "platform/audio_output_delegate_impl.h",
-    "platform/platform_delegate_impl.cc",
-    "platform/platform_delegate_impl.h",
-    "service.cc",
-    "service.h",
-    "service_context.h",
-    "timer_host.cc",
-    "timer_host.h",
-  ]
-
-  deps = [
-    "//ash/constants",
-    "//base",
-    "//chromeos/ash/components/assistant:buildflags",
-    "//chromeos/ash/components/audio",
-    "//chromeos/ash/components/dbus",
-    "//chromeos/ash/services/assistant/public/proto",
-    "//chromeos/ash/services/libassistant/public/cpp:loader",
-    "//chromeos/ash/services/libassistant/public/mojom",
-    "//chromeos/dbus/power",
-    "//chromeos/dbus/power:power_manager_proto",
-    "//chromeos/strings",
-    "//chromeos/version",
-    "//components/account_id",
-    "//components/prefs",
-    "//components/signin/public/identity_manager",
-    "//components/user_manager",
-    "//media/mojo/mojom",
-    "//services/media_session/public/cpp",
-    "//ui/accessibility:ax_assistant",
-  ]
-
-  if (enable_cros_libassistant) {
-    deps += [
-      "//chromeos/ash/services/assistant/public/cpp",
-      "//chromeos/ash/services/libassistant",
-      "//chromeos/ash/services/libassistant:constants",
-      "//chromeos/ash/services/libassistant:loader",
-    ]
-  } else {
-    sources +=
-        [ "//chromeos/ash/services/assistant/libassistant_loader_stub.cc" ]
-  }
-
-  public_deps = [
-    "//ash/public/cpp:cpp",
-    "//chromeos/ash/services/assistant/public/cpp",
-    "//chromeos/ash/services/assistant/public/mojom",
-    "//chromeos/services/assistant/public/shared",
-    "//mojo/public/cpp/bindings",
-    "//services/audio/public/cpp",
-  ]
-}
-
-source_set("tests") {
-  testonly = true
-  deps = [
-    ":lib",
-    ":test_support",
-    "//ash/constants",
-    "//ash/public/cpp/assistant/test_support",
-    "//base",
-    "//base/test:test_support",
-    "//chromeos/ash/components/assistant:buildflags",
-    "//chromeos/ash/components/assistant/test_support",
-    "//chromeos/ash/components/audio",
-    "//chromeos/ash/components/dbus:test_support",
-    "//chromeos/ash/components/dbus/audio",
-    "//chromeos/ash/services/assistant/public/cpp",
-    "//chromeos/ash/services/assistant/public/mojom",
-    "//chromeos/ash/services/libassistant/public/mojom",
-    "//chromeos/dbus/power",
-    "//components/prefs:test_support",
-    "//components/signin/public/identity_manager",
-    "//components/signin/public/identity_manager:test_support",
-    "//mojo/public/cpp/bindings",
-    "//services/device/public/mojom",
-    "//services/media_session/public/cpp/test:test_support",
-    "//services/media_session/public/mojom",
-    "//services/network:test_support",
-    "//testing/gmock",
-    "//testing/gtest",
-  ]
-
-  sources = [
-    "assistant_manager_service_impl_unittest.cc",
-    "media_host_unittest.cc",
-    "media_session/assistant_media_session_unittest.cc",
-    "platform/audio_devices_unittest.cc",
-    "platform/audio_input_host_unittest.cc",
-    "service_unittest.cc",
-    "test_support/fake_service_context.cc",
-    "test_support/fake_service_context.h",
-    "test_support/scoped_device_actions.cc",
-    "test_support/scoped_device_actions.h",
-  ]
-
-  if (enable_cros_libassistant) {
-    deps += [ "//chromeos/assistant/internal:tests" ]
-  }
-}
-
 static_library("test_support") {
   testonly = true
   sources = [
-    "test_support/fake_assistant_manager_service_impl.cc",
-    "test_support/fake_assistant_manager_service_impl.h",
-    "test_support/fake_assistant_settings_impl.cc",
-    "test_support/fake_assistant_settings_impl.h",
-    "test_support/fake_libassistant_service.cc",
-    "test_support/fake_libassistant_service.h",
-    "test_support/fake_service_controller.cc",
-    "test_support/fake_service_controller.h",
-    "test_support/fully_initialized_assistant_state.cc",
-    "test_support/fully_initialized_assistant_state.h",
-    "test_support/libassistant_media_controller_mock.cc",
-    "test_support/libassistant_media_controller_mock.h",
-    "test_support/mock_assistant.cc",
-    "test_support/mock_assistant.h",
-    "test_support/mock_assistant_interaction_subscriber.cc",
-    "test_support/mock_assistant_interaction_subscriber.h",
     "test_support/scoped_assistant_browser_delegate.cc",
     "test_support/scoped_assistant_browser_delegate.h",
   ]
   deps = [
-    ":lib",
+    "//ash/public/cpp:cpp",
     "//ash/public/cpp/resources:ash_public_unscaled_resources",
     "//base",
     "//chromeos/ash/components/assistant:buildflags",
diff --git a/chromeos/ash/services/assistant/assistant_host.cc b/chromeos/ash/services/assistant/assistant_host.cc
deleted file mode 100644
index 23a961b..0000000
--- a/chromeos/ash/services/assistant/assistant_host.cc
+++ /dev/null
@@ -1,258 +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.
-
-#include "chromeos/ash/services/assistant/assistant_host.h"
-
-#include <memory>
-
-#include "base/check.h"
-#include "base/functional/bind.h"
-#include "base/task/single_thread_task_runner.h"
-#include "chromeos/ash/services/assistant/assistant_manager_service_impl.h"
-#include "chromeos/ash/services/assistant/libassistant_service_host.h"
-#include "chromeos/ash/services/assistant/public/cpp/features.h"
-#include "chromeos/ash/services/libassistant/public/mojom/service_controller.mojom.h"
-
-namespace ash::assistant {
-
-AssistantHost::AssistantHost(AssistantManagerServiceImpl* service)
-    : service_(service) {
-  if (!assistant::features::IsLibAssistantSandboxEnabled()) {
-    background_thread_.Start();
-  }
-}
-
-AssistantHost::~AssistantHost() {
-  if (libassistant_service_) {
-    StopLibassistantService();
-  }
-}
-
-void AssistantHost::StartLibassistantService(LibassistantServiceHost* host) {
-  DCHECK(host);
-
-  libassistant_service_host_ = host;
-  LaunchLibassistantService();
-
-  BindControllers();
-}
-
-void AssistantHost::LaunchLibassistantService() {
-  if (assistant::features::IsLibAssistantSandboxEnabled()) {
-    libassistant_service_host_->Launch(
-        libassistant_service_.BindNewPipeAndPassReceiver());
-  } else {
-    // A Mojom service runs on the thread where its receiver was bound.
-    // So to make |libassistant_service_| run on the background thread, we must
-    // create it on the background thread, as it binds its receiver in its
-    // constructor.
-    background_task_runner()->PostTask(
-        FROM_HERE,
-        base::BindOnce(
-            &AssistantHost::LaunchLibassistantServiceOnBackgroundThread,
-            // This is safe because we own the background thread,
-            // so when we're deleted the background thread is stopped.
-            base::Unretained(this),
-            // |libassistant_service_| runs on the current thread, so must
-            // be bound here and not on the background thread.
-            libassistant_service_.BindNewPipeAndPassReceiver()));
-  }
-
-  libassistant_service_.set_disconnect_handler(base::BindOnce(
-      &AssistantHost::OnRemoteDisconnected, base::Unretained(this)));
-}
-
-void AssistantHost::LaunchLibassistantServiceOnBackgroundThread(
-    mojo::PendingReceiver<libassistant::mojom::LibassistantService> client) {
-  DCHECK(background_task_runner()->BelongsToCurrentThread());
-  DCHECK(libassistant_service_host_);
-  libassistant_service_host_->Launch(std::move(client));
-}
-
-void AssistantHost::StopLibassistantService() {
-  ResetRemote();
-
-  if (assistant::features::IsLibAssistantSandboxEnabled()) {
-    libassistant_service_host_->Stop();
-  } else {
-    // |libassistant_service_| is launched on the background thread, so we have
-    // to stop it there as well.
-    background_task_runner()->PostTask(
-        FROM_HERE,
-        base::BindOnce(
-            &AssistantHost::StopLibassistantServiceOnBackgroundThread,
-            base::Unretained(this)));
-  }
-}
-
-void AssistantHost::StopLibassistantServiceOnBackgroundThread() {
-  DCHECK(background_task_runner()->BelongsToCurrentThread());
-  libassistant_service_host_->Stop();
-}
-
-void AssistantHost::OnRemoteDisconnected() {
-  ResetRemote();
-  service_->OnStateChanged(libassistant::mojom::ServiceState::kDisconnected);
-}
-
-void AssistantHost::BindControllers() {
-  mojo::PendingRemote<libassistant::mojom::AudioInputController>
-      pending_audio_input_controller_remote;
-  mojo::PendingRemote<libassistant::mojom::AudioOutputDelegate>
-      pending_audio_output_delegate_remote;
-  mojo::PendingRemote<libassistant::mojom::DeviceSettingsDelegate>
-      pending_device_settings_delegate_remote;
-  mojo::PendingRemote<libassistant::mojom::MediaDelegate>
-      pending_media_delegate_remote;
-  mojo::PendingRemote<libassistant::mojom::NotificationDelegate>
-      pending_notification_delegate_remote;
-  mojo::PendingRemote<libassistant::mojom::PlatformDelegate>
-      pending_platform_delegate_remote;
-  mojo::PendingRemote<libassistant::mojom::SpeakerIdEnrollmentController>
-      pending_speaker_id_enrollment_controller_remote;
-  mojo::PendingRemote<libassistant::mojom::TimerDelegate>
-      pending_timer_delegate_remote;
-
-  media_delegate_ =
-      pending_media_delegate_remote.InitWithNewPipeAndPassReceiver();
-  notification_delegate_ =
-      pending_notification_delegate_remote.InitWithNewPipeAndPassReceiver();
-  platform_delegate_ =
-      pending_platform_delegate_remote.InitWithNewPipeAndPassReceiver();
-  timer_delegate_ =
-      pending_timer_delegate_remote.InitWithNewPipeAndPassReceiver();
-
-  pending_audio_output_delegate_receiver_ =
-      pending_audio_output_delegate_remote.InitWithNewPipeAndPassReceiver();
-  pending_device_settings_delegate_receiver_ =
-      pending_device_settings_delegate_remote.InitWithNewPipeAndPassReceiver();
-  libassistant_service_->Bind(
-      pending_audio_input_controller_remote.InitWithNewPipeAndPassReceiver(),
-      conversation_controller_.BindNewPipeAndPassReceiver(),
-      display_controller_.BindNewPipeAndPassReceiver(),
-      media_controller_.BindNewPipeAndPassReceiver(),
-      service_controller_.BindNewPipeAndPassReceiver(),
-      settings_controller_.BindNewPipeAndPassReceiver(),
-      pending_speaker_id_enrollment_controller_remote
-          .InitWithNewPipeAndPassReceiver(),
-      timer_controller_.BindNewPipeAndPassReceiver(),
-      std::move(pending_audio_output_delegate_remote),
-      std::move(pending_device_settings_delegate_remote),
-      std::move(pending_media_delegate_remote),
-      std::move(pending_notification_delegate_remote),
-      std::move(pending_platform_delegate_remote),
-      std::move(pending_timer_delegate_remote));
-
-  audio_input_controller_ = std::move(pending_audio_input_controller_remote);
-  speaker_id_enrollment_controller_ =
-      std::move(pending_speaker_id_enrollment_controller_remote);
-}
-
-scoped_refptr<base::SingleThreadTaskRunner>
-AssistantHost::background_task_runner() {
-  return background_thread_.task_runner();
-}
-
-mojo::PendingRemote<libassistant::mojom::AudioInputController>
-AssistantHost::ExtractAudioInputController() {
-  DCHECK(audio_input_controller_.is_valid());
-  return std::move(audio_input_controller_);
-}
-
-mojo::PendingReceiver<libassistant::mojom::AudioOutputDelegate>
-AssistantHost::ExtractAudioOutputDelegate() {
-  DCHECK(pending_audio_output_delegate_receiver_.is_valid());
-  return std::move(pending_audio_output_delegate_receiver_);
-}
-
-mojo::PendingReceiver<libassistant::mojom::DeviceSettingsDelegate>
-AssistantHost::ExtractDeviceSettingsDelegate() {
-  DCHECK(pending_device_settings_delegate_receiver_.is_valid());
-  return std::move(pending_device_settings_delegate_receiver_);
-}
-
-mojo::PendingReceiver<libassistant::mojom::MediaDelegate>
-AssistantHost::ExtractMediaDelegate() {
-  DCHECK(media_delegate_.is_valid());
-  return std::move(media_delegate_);
-}
-
-mojo::PendingReceiver<libassistant::mojom::NotificationDelegate>
-AssistantHost::ExtractNotificationDelegate() {
-  DCHECK(notification_delegate_.is_valid());
-  return std::move(notification_delegate_);
-}
-
-mojo::PendingReceiver<libassistant::mojom::PlatformDelegate>
-AssistantHost::ExtractPlatformDelegate() {
-  DCHECK(platform_delegate_.is_valid());
-  return std::move(platform_delegate_);
-}
-
-mojo::PendingRemote<libassistant::mojom::SpeakerIdEnrollmentController>
-AssistantHost::ExtractSpeakerIdEnrollmentController() {
-  DCHECK(speaker_id_enrollment_controller_.is_valid());
-  return std::move(speaker_id_enrollment_controller_);
-}
-
-mojo::PendingReceiver<libassistant::mojom::TimerDelegate>
-AssistantHost::ExtractTimerDelegate() {
-  DCHECK(timer_delegate_.is_valid());
-  return std::move(timer_delegate_);
-}
-
-libassistant::mojom::ConversationController&
-AssistantHost::conversation_controller() {
-  DCHECK(conversation_controller_.is_bound());
-  return *conversation_controller_;
-}
-
-libassistant::mojom::DisplayController& AssistantHost::display_controller() {
-  DCHECK(display_controller_.is_bound());
-  return *display_controller_.get();
-}
-
-libassistant::mojom::ServiceController& AssistantHost::service_controller() {
-  DCHECK(service_controller_.is_bound());
-  return *service_controller_.get();
-}
-
-libassistant::mojom::MediaController& AssistantHost::media_controller() {
-  DCHECK(media_controller_.is_bound());
-  return *media_controller_.get();
-}
-
-libassistant::mojom::SettingsController& AssistantHost::settings_controller() {
-  DCHECK(settings_controller_.is_bound());
-  return *settings_controller_;
-}
-
-libassistant::mojom::TimerController& AssistantHost::timer_controller() {
-  DCHECK(timer_controller_.is_bound());
-  return *timer_controller_.get();
-}
-
-void AssistantHost::AddSpeechRecognitionObserver(
-    mojo::PendingRemote<libassistant::mojom::SpeechRecognitionObserver>
-        observer) {
-  libassistant_service_->AddSpeechRecognitionObserver(std::move(observer));
-}
-
-void AssistantHost::AddAuthenticationStateObserver(
-    mojo::PendingRemote<libassistant::mojom::AuthenticationStateObserver>
-        observer) {
-  libassistant_service_->AddAuthenticationStateObserver(std::move(observer));
-}
-
-void AssistantHost::ResetRemote() {
-  libassistant_service_.reset();
-  conversation_controller_.reset();
-  display_controller_.reset();
-  media_controller_.reset();
-  service_controller_.reset();
-  settings_controller_.reset();
-  timer_controller_.reset();
-}
-
-}  // namespace ash::assistant
diff --git a/chromeos/ash/services/assistant/assistant_host.h b/chromeos/ash/services/assistant/assistant_host.h
deleted file mode 100644
index 9ca5ab8..0000000
--- a/chromeos/ash/services/assistant/assistant_host.h
+++ /dev/null
@@ -1,158 +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.
-
-#ifndef CHROMEOS_ASH_SERVICES_ASSISTANT_ASSISTANT_HOST_H_
-#define CHROMEOS_ASH_SERVICES_ASSISTANT_ASSISTANT_HOST_H_
-
-#include "base/memory/raw_ptr.h"
-#include "base/task/single_thread_task_runner.h"
-#include "base/threading/thread.h"
-#include "base/time/time.h"
-#include "chromeos/ash/services/libassistant/public/mojom/audio_input_controller.mojom.h"
-#include "chromeos/ash/services/libassistant/public/mojom/conversation_controller.mojom.h"
-#include "chromeos/ash/services/libassistant/public/mojom/display_controller.mojom.h"
-#include "chromeos/ash/services/libassistant/public/mojom/media_controller.mojom.h"
-#include "chromeos/ash/services/libassistant/public/mojom/notification_delegate.mojom.h"
-#include "chromeos/ash/services/libassistant/public/mojom/platform_delegate.mojom.h"
-#include "chromeos/ash/services/libassistant/public/mojom/service.mojom.h"
-#include "chromeos/ash/services/libassistant/public/mojom/service_controller.mojom.h"
-#include "chromeos/ash/services/libassistant/public/mojom/speaker_id_enrollment_controller.mojom-forward.h"
-#include "chromeos/ash/services/libassistant/public/mojom/timer_controller.mojom.h"
-#include "mojo/public/cpp/bindings/pending_receiver.h"
-#include "mojo/public/cpp/bindings/pending_remote.h"
-#include "mojo/public/cpp/bindings/remote.h"
-
-namespace chromeos {
-namespace libassistant {
-class LibassistantService;
-}  // namespace libassistant
-}  // namespace chromeos
-
-namespace ash::assistant {
-
-class AssistantManagerServiceImpl;
-class LibassistantServiceHost;
-
-// The proxy to the Assistant service, which serves as the main
-// access point to the entire Assistant API.
-class AssistantHost {
- public:
-  explicit AssistantHost(AssistantManagerServiceImpl* service);
-  AssistantHost(AssistantHost&) = delete;
-  AssistantHost& operator=(AssistantHost&) = delete;
-  ~AssistantHost();
-
-  void StartLibassistantService(LibassistantServiceHost* host);
-  void StopLibassistantService();
-
-  // Returns the controller that manages conversations with Libassistant.
-  libassistant::mojom::ConversationController& conversation_controller();
-
-  // Returns the controller that manages display related settings.
-  libassistant::mojom::DisplayController& display_controller();
-
-  // Returns the controller that manages media related settings.
-  libassistant::mojom::MediaController& media_controller();
-
-  // Returns the controller that manages the lifetime of the service.
-  libassistant::mojom::ServiceController& service_controller();
-
-  // Returns the controller that manages Libassistant settings.
-  libassistant::mojom::SettingsController& settings_controller();
-
-  // Returns the controller that manages timers.
-  libassistant::mojom::TimerController& timer_controller();
-
-  // The background thread is temporary exposed until the entire Libassistant
-  // API is hidden behind this proxy API.
-  base::Thread& background_thread() { return background_thread_; }
-
-  // Add an observer that will be informed of all speech recognition related
-  // updates.
-  void AddSpeechRecognitionObserver(
-      mojo::PendingRemote<libassistant::mojom::SpeechRecognitionObserver>
-          observer);
-
-  void AddAuthenticationStateObserver(
-      mojo::PendingRemote<libassistant::mojom::AuthenticationStateObserver>
-          observer);
-
-  mojo::PendingRemote<libassistant::mojom::AudioInputController>
-  ExtractAudioInputController();
-  mojo::PendingReceiver<libassistant::mojom::AudioOutputDelegate>
-  ExtractAudioOutputDelegate();
-  mojo::PendingReceiver<libassistant::mojom::DeviceSettingsDelegate>
-  ExtractDeviceSettingsDelegate();
-  mojo::PendingReceiver<libassistant::mojom::MediaDelegate>
-  ExtractMediaDelegate();
-  mojo::PendingReceiver<libassistant::mojom::NotificationDelegate>
-  ExtractNotificationDelegate();
-  mojo::PendingReceiver<libassistant::mojom::PlatformDelegate>
-  ExtractPlatformDelegate();
-  mojo::PendingRemote<libassistant::mojom::SpeakerIdEnrollmentController>
-  ExtractSpeakerIdEnrollmentController();
-  mojo::PendingReceiver<libassistant::mojom::TimerDelegate>
-  ExtractTimerDelegate();
-
- private:
-  scoped_refptr<base::SingleThreadTaskRunner> background_task_runner();
-
-  void LaunchLibassistantService();
-  void LaunchLibassistantServiceOnBackgroundThread(
-      mojo::PendingReceiver<libassistant::mojom::LibassistantService>);
-  void StopLibassistantServiceOnBackgroundThread();
-
-  void BindControllers();
-
-  // Callback when `LibassistantService` has disconnected, e.g. process crashes.
-  void OnRemoteDisconnected();
-
-  // Reset remote controllers etc. for restarts.
-  void ResetRemote();
-
-  // Owned by |Service|.
-  raw_ptr<AssistantManagerServiceImpl> service_;
-
-  // Owned by |AssistantManagerServiceImpl|.
-  raw_ptr<LibassistantServiceHost> libassistant_service_host_;
-
-  mojo::Remote<libassistant::mojom::LibassistantService> libassistant_service_;
-
-  mojo::Remote<libassistant::mojom::ConversationController>
-      conversation_controller_;
-  mojo::Remote<libassistant::mojom::DisplayController> display_controller_;
-  mojo::Remote<libassistant::mojom::MediaController> media_controller_;
-  mojo::Remote<libassistant::mojom::ServiceController> service_controller_;
-  mojo::Remote<libassistant::mojom::SettingsController> settings_controller_;
-  mojo::Remote<libassistant::mojom::TimerController> timer_controller_;
-
-  // Will be unbound after they are extracted.
-  mojo::PendingRemote<libassistant::mojom::AudioInputController>
-      audio_input_controller_;
-  mojo::PendingReceiver<libassistant::mojom::AudioOutputDelegate>
-      pending_audio_output_delegate_receiver_;
-  mojo::PendingReceiver<libassistant::mojom::DeviceSettingsDelegate>
-      pending_device_settings_delegate_receiver_;
-  mojo::PendingReceiver<libassistant::mojom::MediaDelegate> media_delegate_;
-  mojo::PendingReceiver<libassistant::mojom::NotificationDelegate>
-      notification_delegate_;
-  mojo::PendingReceiver<libassistant::mojom::PlatformDelegate>
-      platform_delegate_;
-  mojo::PendingRemote<libassistant::mojom::SpeakerIdEnrollmentController>
-      speaker_id_enrollment_controller_;
-  mojo::PendingReceiver<libassistant::mojom::TimerDelegate> timer_delegate_;
-
-  // The thread on which the Libassistant service runs.
-  // Only used to run LibAssistant service without sandbox for development, e.g.
-  // with `--no-sandbox`. Background thread is needed because there are blocking
-  // calls when start LibAssistant service, e.g. creating directories.
-  // Warning: must be the last object, so it is destroyed (and flushed) first.
-  // This will prevent use-after-free issues where the background thread would
-  // access other member variables after they have been destroyed.
-  base::Thread background_thread_{"Assistant background thread"};
-};
-
-}  // namespace ash::assistant
-
-#endif  // CHROMEOS_ASH_SERVICES_ASSISTANT_ASSISTANT_HOST_H_
diff --git a/chromeos/ash/services/assistant/assistant_interaction_logger.cc b/chromeos/ash/services/assistant/assistant_interaction_logger.cc
deleted file mode 100644
index acd95e1..0000000
--- a/chromeos/ash/services/assistant/assistant_interaction_logger.cc
+++ /dev/null
@@ -1,128 +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.
-
-#include "chromeos/ash/services/assistant/assistant_interaction_logger.h"
-
-#include <utility>
-
-#include "chromeos/ash/services/assistant/public/cpp/features.h"
-
-namespace ash::assistant {
-
-namespace {
-
-std::string ResolutionToString(AssistantInteractionResolution resolution) {
-  std::stringstream result;
-  result << static_cast<int>(resolution);
-  return result.str();
-}
-
-bool IsPIILoggingAllowed() {
-  return features::IsAssistantDebuggingEnabled();
-}
-
-std::string HidePiiMaybe(const std::string& value) {
-  if (IsPIILoggingAllowed())
-    return "[PII](" + value + ")";
-  else
-    return "[Redacted PII]";
-}
-
-#define LOG_INTERACTION() \
-  LOG_INTERACTION_AT_LEVEL(AssistantInteractionLogger::kVLogLevel)
-
-#define LOG_INTERACTION_AT_LEVEL(_level) \
-  VLOG(_level) << "Assistant: " << __func__ << ": "
-
-}  // namespace
-
-bool AssistantInteractionLogger::IsLoggingEnabled() {
-  return VLOG_IS_ON(kVLogLevel);
-}
-
-AssistantInteractionLogger::AssistantInteractionLogger() = default;
-
-AssistantInteractionLogger::~AssistantInteractionLogger() = default;
-
-void AssistantInteractionLogger::OnInteractionStarted(
-    const AssistantInteractionMetadata& metadata) {
-  switch (metadata.type) {
-    case AssistantInteractionType::kText:
-      LOG_INTERACTION() << "Text interaction with query "
-                        << HidePiiMaybe(metadata.query);
-      break;
-    case AssistantInteractionType::kVoice:
-      LOG_INTERACTION() << "Voice interaction";
-      break;
-  }
-}
-
-void AssistantInteractionLogger::OnInteractionFinished(
-    AssistantInteractionResolution resolution) {
-  LOG_INTERACTION() << "with resolution " << ResolutionToString(resolution);
-}
-
-void AssistantInteractionLogger::OnHtmlResponse(const std::string& response,
-                                                const std::string& fallback) {
-  // Displaying fallback instead of the response as the response is filled with
-  // HTML tags and rather large.
-  LOG_INTERACTION() << "with fallback '" << fallback << "'";
-  // Display HTML at highest verbosity.
-  LOG_INTERACTION_AT_LEVEL(3) << "with HTML: " << HidePiiMaybe(response);
-}
-
-void AssistantInteractionLogger::OnSuggestionsResponse(
-    const std::vector<assistant::AssistantSuggestion>& response) {
-  std::stringstream suggestions;
-  for (const auto& suggestion : response)
-    suggestions << "'" << suggestion.text << "', ";
-  LOG_INTERACTION() << "{ " << suggestions.str() << " }";
-}
-
-void AssistantInteractionLogger::OnTextResponse(const std::string& response) {
-  LOG_INTERACTION() << HidePiiMaybe(response);
-}
-
-void AssistantInteractionLogger::OnOpenUrlResponse(const GURL& url,
-                                                   bool in_background) {
-  LOG_INTERACTION() << "with url '" << url.possibly_invalid_spec() << "'";
-}
-
-void AssistantInteractionLogger::OnOpenAppResponse(
-    const AndroidAppInfo& app_info) {
-  LOG_INTERACTION() << "with app '" << app_info.package_name << "'";
-}
-
-void AssistantInteractionLogger::OnSpeechRecognitionStarted() {
-  LOG_INTERACTION();
-}
-
-void AssistantInteractionLogger::OnSpeechRecognitionIntermediateResult(
-    const std::string& high_confidence_text,
-    const std::string& low_confidence_text) {
-  // Not logged until we have a use for this (and this might spam the log).
-}
-
-void AssistantInteractionLogger::OnSpeechRecognitionEndOfUtterance() {
-  LOG_INTERACTION();
-}
-
-void AssistantInteractionLogger::OnSpeechRecognitionFinalResult(
-    const std::string& final_result) {
-  LOG_INTERACTION() << "with final result '" << final_result << "'";
-}
-
-void AssistantInteractionLogger::OnSpeechLevelUpdated(float speech_level) {
-  // Not logged until we have a use for this and this might spam the log.
-}
-
-void AssistantInteractionLogger::OnTtsStarted(bool due_to_error) {
-  LOG_INTERACTION() << (due_to_error ? "not" : "") << "due to error";
-}
-
-void AssistantInteractionLogger::OnWaitStarted() {
-  LOG_INTERACTION();
-}
-
-}  // namespace ash::assistant
diff --git a/chromeos/ash/services/assistant/assistant_interaction_logger.h b/chromeos/ash/services/assistant/assistant_interaction_logger.h
deleted file mode 100644
index 9acf16b1a..0000000
--- a/chromeos/ash/services/assistant/assistant_interaction_logger.h
+++ /dev/null
@@ -1,70 +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.
-
-#ifndef CHROMEOS_ASH_SERVICES_ASSISTANT_ASSISTANT_INTERACTION_LOGGER_H_
-#define CHROMEOS_ASH_SERVICES_ASSISTANT_ASSISTANT_INTERACTION_LOGGER_H_
-
-#include <string>
-#include <vector>
-
-#include "chromeos/ash/services/assistant/public/cpp/assistant_service.h"
-
-namespace ash::assistant {
-
-// A subscriber that will log all Assistant interactions.
-// The interactions will be logged using
-//     VLOG(AssistantInteractionLogger::kVLogLevel)
-class AssistantInteractionLogger : public AssistantInteractionSubscriber {
- public:
-  // VLog level used for logging interactions.
-  constexpr static const int kVLogLevel = 1;
-
-  // Returns if the current logging level is high enough so that the traces
-  // will be printed. If not, there is no point in creating this class.
-  static bool IsLoggingEnabled();
-
-  AssistantInteractionLogger();
-  AssistantInteractionLogger(AssistantInteractionLogger&) = delete;
-  AssistantInteractionLogger& operator=(AssistantInteractionLogger&) = delete;
-  ~AssistantInteractionLogger() override;
-
-  // AssistantInteractionSubscriber implementation:
-  void OnInteractionStarted(
-      const AssistantInteractionMetadata& metadata) override;
-
-  void OnInteractionFinished(
-      AssistantInteractionResolution resolution) override;
-
-  void OnHtmlResponse(const std::string& response,
-                      const std::string& fallback) override;
-
-  void OnSuggestionsResponse(
-      const std::vector<AssistantSuggestion>& response) override;
-
-  void OnTextResponse(const std::string& response) override;
-
-  void OnOpenUrlResponse(const GURL& url, bool in_background) override;
-
-  void OnOpenAppResponse(const AndroidAppInfo& app_info) override;
-
-  void OnSpeechRecognitionStarted() override;
-
-  void OnSpeechRecognitionIntermediateResult(
-      const std::string& high_confidence_text,
-      const std::string& low_confidence_text) override;
-
-  void OnSpeechRecognitionEndOfUtterance() override;
-
-  void OnSpeechRecognitionFinalResult(const std::string& final_result) override;
-
-  void OnSpeechLevelUpdated(float speech_level) override;
-
-  void OnTtsStarted(bool due_to_error) override;
-
-  void OnWaitStarted() override;
-};
-
-}  // namespace ash::assistant
-
-#endif  // CHROMEOS_ASH_SERVICES_ASSISTANT_ASSISTANT_INTERACTION_LOGGER_H_
diff --git a/chromeos/ash/services/assistant/assistant_manager_service.cc b/chromeos/ash/services/assistant/assistant_manager_service.cc
deleted file mode 100644
index e0a578c6..0000000
--- a/chromeos/ash/services/assistant/assistant_manager_service.cc
+++ /dev/null
@@ -1,22 +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.
-
-#include "chromeos/ash/services/assistant/assistant_manager_service.h"
-
-namespace ash::assistant {
-
-AuthenticationStateObserver::AuthenticationStateObserver() = default;
-
-AuthenticationStateObserver::~AuthenticationStateObserver() = default;
-
-mojo::PendingRemote<libassistant::mojom::AuthenticationStateObserver>
-AuthenticationStateObserver::BindNewPipeAndPassRemote() {
-  return receiver_.BindNewPipeAndPassRemote();
-}
-
-void AuthenticationStateObserver::ResetAuthenticationStateObserver() {
-  receiver_.reset();
-}
-
-}  // namespace ash::assistant
diff --git a/chromeos/ash/services/assistant/assistant_manager_service.h b/chromeos/ash/services/assistant/assistant_manager_service.h
deleted file mode 100644
index 282fee6..0000000
--- a/chromeos/ash/services/assistant/assistant_manager_service.h
+++ /dev/null
@@ -1,144 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROMEOS_ASH_SERVICES_ASSISTANT_ASSISTANT_MANAGER_SERVICE_H_
-#define CHROMEOS_ASH_SERVICES_ASSISTANT_ASSISTANT_MANAGER_SERVICE_H_
-
-#include <memory>
-#include <string>
-
-#include "base/component_export.h"
-#include "chromeos/ash/services/assistant/public/cpp/assistant_service.h"
-#include "chromeos/ash/services/assistant/public/cpp/assistant_settings.h"
-#include "chromeos/ash/services/libassistant/public/mojom/authentication_state_observer.mojom.h"
-#include "google_apis/gaia/gaia_id.h"
-#include "services/media_session/public/mojom/media_session.mojom-shared.h"
-
-namespace ash::assistant {
-
-class AuthenticationStateObserver;
-
-// Interface class that defines all assistant functionalities.
-class COMPONENT_EXPORT(ASSISTANT_SERVICE) AssistantManagerService
-    : public Assistant {
- public:
-  class StateObserver;
-
-  struct UserInfo {
-    UserInfo(const GaiaId& gaia_id, const std::string& access_token)
-        : gaia_id(gaia_id), access_token(access_token) {}
-
-    GaiaId gaia_id;
-    std::string access_token;
-  };
-
-  // These values are persisted to logs. Entries should not be renumbered and
-  // numeric values should never be reused.
-  // If any value is added, please update enums.xml
-  // `AssistantServiceState`.
-  // Enumeration of possible assistant manager service states.
-  enum State {
-    // Initial state, the service is created but not started yet.
-    STOPPED = 0,
-    // Start has been called but libassistant creation is still in progress.
-    // Calling |assistant_manager()| will still return a nullptr.
-    // TODO(b/171748795): I think we no longer need this state once
-    // Libassistant has migrated to a mojom service (in fact, we should be able
-    // to remove this enum and use ash::libassistant::mojom::ServiceState).
-    STARTING = 1,
-    // The service is started, libassistant has been created, but libassistant
-    // is not ready yet to take requests.
-    STARTED = 2,
-    // The service is fully running and ready to take requests.
-    RUNNING = 3,
-    // Stop has been called but `assistant_manager` has not been destroyed. It
-    // is possible that some functions will call back to the browser thread,
-    // e.g. the audio output.
-    STOPPING = 4,
-    // The libassistant mojom service is disconnected, e.g. process crashes.
-    DISCONNECTED = 5,
-
-    kMaxValue = DISCONNECTED
-  };
-
-  ~AssistantManagerService() override = default;
-
-  // Start the Assistant in the background with the given |user|.
-  // If the user is nullopt, the service will be started in signed-out mode.
-  // If you want to know when the service is started, use
-  // |AddAndFireStateObserver| to add an observer.
-  virtual void Start(const std::optional<UserInfo>& user,
-                     bool enable_hotword) = 0;
-
-  // Stop the Assistant.
-  virtual void Stop() = 0;
-
-  // Return the current state.
-  virtual State GetState() const = 0;
-
-  // Set user information for Assistant. Passing a nullopt will reconfigure
-  // Libassistant to run in signed-out mode, and passing a valid non-empty value
-  // will switch the mode back to normal.
-  virtual void SetUser(const std::optional<UserInfo>& user) = 0;
-
-  // Turn on / off all listening, including hotword and voice query.
-  virtual void EnableListening(bool enable) = 0;
-
-  // Turn on / off hotword listening.
-  virtual void EnableHotword(bool enable) = 0;
-
-  // Enable/disable ARC play store.
-  virtual void SetArcPlayStoreEnabled(bool enabled) = 0;
-
-  // Enable/disable Assistant Context.
-  virtual void SetAssistantContextEnabled(bool enable) = 0;
-
-  // Return a pointer of AssistantSettings.
-  virtual AssistantSettings* GetAssistantSettings() = 0;
-
-  virtual void AddAuthenticationStateObserver(
-      AuthenticationStateObserver* observer) = 0;
-
-  // Add/Remove an observer that is invoked when there is a change in the
-  // |AssistantManagerService::State| value.
-  // When adding an observer it will immediately be triggered with the current
-  // state value.
-  virtual void AddAndFireStateObserver(StateObserver* observer) = 0;
-  virtual void RemoveStateObserver(const StateObserver* observer) = 0;
-
-  // Sync the device apps user consent status.
-  virtual void SyncDeviceAppsStatus() = 0;
-
-  // Update and sync the internal media player status to Libassistant.
-  virtual void UpdateInternalMediaPlayerStatus(
-      media_session::mojom::MediaSessionAction action) = 0;
-};
-
-// Observes all state changes made to the |AssistantManagerService::State|.
-class AssistantManagerService::StateObserver : public base::CheckedObserver {
- public:
-  StateObserver() = default;
-  ~StateObserver() override = default;
-
-  virtual void OnStateChanged(AssistantManagerService::State new_state) = 0;
-};
-
-class AuthenticationStateObserver
-    : public libassistant::mojom::AuthenticationStateObserver {
- public:
-  AuthenticationStateObserver();
-  ~AuthenticationStateObserver() override;
-
-  mojo::PendingRemote<libassistant::mojom::AuthenticationStateObserver>
-  BindNewPipeAndPassRemote();
-  void ResetAuthenticationStateObserver();
-
- private:
-  mojo::Receiver<libassistant::mojom::AuthenticationStateObserver> receiver_{
-      this};
-};
-
-}  // namespace ash::assistant
-
-#endif  // CHROMEOS_ASH_SERVICES_ASSISTANT_ASSISTANT_MANAGER_SERVICE_H_
diff --git a/chromeos/ash/services/assistant/assistant_manager_service_impl.cc b/chromeos/ash/services/assistant/assistant_manager_service_impl.cc
deleted file mode 100644
index 2f1b5a7..0000000
--- a/chromeos/ash/services/assistant/assistant_manager_service_impl.cc
+++ /dev/null
@@ -1,789 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chromeos/ash/services/assistant/assistant_manager_service_impl.h"
-
-#include <algorithm>
-#include <memory>
-#include <optional>
-#include <utility>
-
-#include "ash/constants/ash_features.h"
-#include "ash/constants/ash_switches.h"
-#include "ash/public/cpp/assistant/assistant_state.h"
-#include "ash/public/cpp/assistant/assistant_state_base.h"
-#include "ash/public/cpp/assistant/controller/assistant_notification_controller.h"
-#include "base/barrier_closure.h"
-#include "base/check.h"
-#include "base/command_line.h"
-#include "base/containers/contains.h"
-#include "base/feature_list.h"
-#include "base/functional/bind.h"
-#include "base/i18n/rtl.h"
-#include "base/logging.h"
-#include "base/memory/raw_ref.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/notreached.h"
-#include "base/run_loop.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/task/sequenced_task_runner.h"
-#include "base/time/time.h"
-#include "base/unguessable_token.h"
-#include "chromeos/ash/services/assistant/device_settings_host.h"
-#include "chromeos/ash/services/assistant/libassistant_service_host_impl.h"
-#include "chromeos/ash/services/assistant/media_host.h"
-#include "chromeos/ash/services/assistant/platform/audio_input_host_impl.h"
-#include "chromeos/ash/services/assistant/platform/audio_output_delegate_impl.h"
-#include "chromeos/ash/services/assistant/platform/platform_delegate_impl.h"
-#include "chromeos/ash/services/assistant/public/cpp/assistant_browser_delegate.h"
-#include "chromeos/ash/services/assistant/public/cpp/assistant_enums.h"
-#include "chromeos/ash/services/assistant/public/cpp/device_actions.h"
-#include "chromeos/ash/services/assistant/public/cpp/features.h"
-#include "chromeos/ash/services/assistant/service.h"
-#include "chromeos/ash/services/assistant/service_context.h"
-#include "chromeos/ash/services/assistant/timer_host.h"
-#include "chromeos/ash/services/libassistant/public/mojom/android_app_info.mojom.h"
-#include "chromeos/ash/services/libassistant/public/mojom/speech_recognition_observer.mojom.h"
-#include "chromeos/services/assistant/public/shared/utils.h"
-#include "chromeos/strings/grit/chromeos_strings.h"
-#include "chromeos/version/version_loader.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "ui/accessibility/mojom/ax_assistant_structure.mojom.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "url/gurl.h"
-
-using media_session::mojom::MediaSessionAction;
-
-namespace ash::assistant {
-namespace {
-
-static base::OnceCallback<void()> initialized_internal_callback_for_testing;
-static bool is_first_init = true;
-
-constexpr char kAndroidSettingsAppPackage[] = "com.android.settings";
-
-std::vector<libassistant::mojom::AuthenticationTokenPtr> ToAuthenticationTokens(
-    const std::optional<AssistantManagerService::UserInfo>& user) {
-  std::vector<libassistant::mojom::AuthenticationTokenPtr> result;
-
-  if (user.has_value()) {
-    DCHECK(!user.value().gaia_id.empty());
-    DCHECK(!user.value().access_token.empty());
-    result.emplace_back(libassistant::mojom::AuthenticationToken::New(
-        /*gaia_id=*/user.value().gaia_id.ToString(),
-        /*access_token=*/user.value().access_token));
-  }
-
-  return result;
-}
-
-libassistant::mojom::BootupConfigPtr CreateBootupConfig(
-    const std::optional<std::string>& s3_server_uri_override,
-    const std::optional<std::string>& device_id_override) {
-  auto result = libassistant::mojom::BootupConfig::New();
-  result->s3_server_uri_override = s3_server_uri_override;
-  result->device_id_override = device_id_override;
-  return result;
-}
-
-}  // namespace
-
-// Observer that will receive all speech recognition related events,
-// and forwards them to all |AssistantInteractionSubscriber|.
-class SpeechRecognitionObserverWrapper
-    : public libassistant::mojom::SpeechRecognitionObserver {
- public:
-  explicit SpeechRecognitionObserverWrapper(
-      const base::ObserverList<AssistantInteractionSubscriber>* observers)
-      : interaction_subscribers_(*observers) {
-    DCHECK(observers);
-  }
-  SpeechRecognitionObserverWrapper(const SpeechRecognitionObserverWrapper&) =
-      delete;
-  SpeechRecognitionObserverWrapper& operator=(
-      const SpeechRecognitionObserverWrapper&) = delete;
-  ~SpeechRecognitionObserverWrapper() override = default;
-
-  mojo::PendingRemote<libassistant::mojom::SpeechRecognitionObserver>
-  BindNewPipeAndPassRemote() {
-    return receiver_.BindNewPipeAndPassRemote();
-  }
-
-  void Stop() { receiver_.reset(); }
-
-  // libassistant::mojom::SpeechRecognitionObserver implementation:
-  void OnSpeechLevelUpdated(float speech_level_in_decibels) override {
-    for (auto& it : *interaction_subscribers_) {
-      it.OnSpeechLevelUpdated(speech_level_in_decibels);
-    }
-  }
-
-  void OnSpeechRecognitionStart() override {
-    for (auto& it : *interaction_subscribers_) {
-      it.OnSpeechRecognitionStarted();
-    }
-  }
-
-  void OnIntermediateResult(const std::string& high_confidence_text,
-                            const std::string& low_confidence_text) override {
-    for (auto& it : *interaction_subscribers_) {
-      it.OnSpeechRecognitionIntermediateResult(high_confidence_text,
-                                               low_confidence_text);
-    }
-  }
-
-  void OnSpeechRecognitionEnd() override {
-    for (auto& it : *interaction_subscribers_) {
-      it.OnSpeechRecognitionEndOfUtterance();
-    }
-  }
-
-  void OnFinalResult(const std::string& recognized_text) override {
-    for (auto& it : *interaction_subscribers_) {
-      it.OnSpeechRecognitionFinalResult(recognized_text);
-    }
-  }
-
- private:
-  // Owned by our parent, |AssistantManagerServiceImpl|.
-  const raw_ref<const base::ObserverList<AssistantInteractionSubscriber>>
-      interaction_subscribers_;
-
-  mojo::Receiver<libassistant::mojom::SpeechRecognitionObserver> receiver_{
-      this};
-};
-
-// static
-void AssistantManagerServiceImpl::SetInitializedInternalCallbackForTesting(
-    base::OnceCallback<void()> callback) {
-  CHECK(initialized_internal_callback_for_testing.is_null());
-  // We expect that the callback is set when AssistantStatus is NOT_READY to
-  // confirm that AssistantStatus has changed from NOT_READY to READY. See more
-  // details at a comment in AssistantManagerServiceImpl::OnDeviceAppsEnabled.
-  CHECK(AssistantState::Get()->assistant_status() ==
-        AssistantStatus::NOT_READY);
-  initialized_internal_callback_for_testing = std::move(callback);
-}
-
-// static
-void AssistantManagerServiceImpl::ResetIsFirstInitFlagForTesting() {
-  is_first_init = true;
-}
-
-AssistantManagerServiceImpl::AssistantManagerServiceImpl(
-    ServiceContext* context,
-    std::unique_ptr<network::PendingSharedURLLoaderFactory>
-        pending_url_loader_factory,
-    std::optional<std::string> s3_server_uri_override,
-    std::optional<std::string> device_id_override,
-    std::unique_ptr<LibassistantServiceHost> libassistant_service_host)
-    : assistant_settings_(std::make_unique<AssistantSettingsImpl>(context)),
-      assistant_host_(std::make_unique<AssistantHost>(this)),
-      platform_delegate_(std::make_unique<PlatformDelegateImpl>()),
-      context_(context),
-      device_settings_host_(std::make_unique<DeviceSettingsHost>(context)),
-      media_host_(std::make_unique<MediaHost>(AssistantBrowserDelegate::Get(),
-                                              &interaction_subscribers_)),
-      timer_host_(std::make_unique<TimerHost>(context)),
-      audio_output_delegate_(std::make_unique<AudioOutputDelegateImpl>(
-          &media_host_->media_session())),
-      speech_recognition_observer_(
-          std::make_unique<SpeechRecognitionObserverWrapper>(
-              &interaction_subscribers_)),
-      bootup_config_(
-          CreateBootupConfig(s3_server_uri_override, device_id_override)),
-      url_loader_factory_(network::SharedURLLoaderFactory::Create(
-          std::move(pending_url_loader_factory))),
-      weak_factory_(this) {
-  if (libassistant_service_host) {
-    // During unittests a custom host is passed in, so we'll use that one.
-    libassistant_service_host_ = std::move(libassistant_service_host);
-  } else {
-    // Use the default service host if none was provided.
-    libassistant_service_host_ =
-        std::make_unique<LibassistantServiceHostImpl>();
-  }
-}
-
-AssistantManagerServiceImpl::~AssistantManagerServiceImpl() {
-  // Destroy the Assistant Proxy first so the background thread is flushed
-  // before any of the other objects are destroyed.
-  assistant_host_ = nullptr;
-}
-
-void AssistantManagerServiceImpl::Start(const std::optional<UserInfo>& user,
-                                        bool enable_hotword) {
-  DCHECK(!IsServiceStarted());
-  DCHECK(GetState() == State::STOPPED || GetState() == State::DISCONNECTED);
-
-  Initialize();
-
-  // Set the flag to avoid starting the service multiple times.
-  SetStateAndInformObservers(State::STARTING);
-
-  started_time_ = base::TimeTicks::Now();
-
-  EnableHotword(enable_hotword);
-  InitAssistant(user);
-}
-
-void AssistantManagerServiceImpl::Stop() {
-  // We cannot cleanly stop the service if it is in the process of starting up.
-  DCHECK_NE(GetState(), State::STARTING);
-
-  // During shutdown, this could be called when the libassistant
-  // service is not started.
-  if (!IsServiceStarted()) {
-    return;
-  }
-
-  weak_factory_.InvalidateWeakPtrs();
-  SetStateAndInformObservers(State::STOPPING);
-
-  // We stop observing media events before we destroy `AssistantManagerImpl`,
-  // otherwise notification may happen any time after it is destroyed.
-  media_host_->Stop();
-  // For similar reason, stop observing app_list events.
-  scoped_app_list_event_subscriber_.Reset();
-
-  // When user disables the feature, we also delete all data.
-  if (!assistant_state()->settings_enabled().value())
-    service_controller().ResetAllDataAndStop();
-  else
-    service_controller().Stop();
-}
-
-AssistantManagerService::State AssistantManagerServiceImpl::GetState() const {
-  return state_;
-}
-
-void AssistantManagerServiceImpl::SetUser(const std::optional<UserInfo>& user) {
-  if (!IsServiceStarted())
-    return;
-
-  VLOG(1) << "Set user information (Gaia ID and access token).";
-  settings_controller().SetAuthenticationTokens(ToAuthenticationTokens(user));
-}
-
-void AssistantManagerServiceImpl::EnableListening(bool enable) {
-  // Could be called when the libassistant service is not started.
-  if (!IsServiceStarted())
-    return;
-
-  settings_controller().SetListeningEnabled(enable);
-}
-
-void AssistantManagerServiceImpl::EnableHotword(bool enable) {
-  // Could be called when the libassistant service is not started.
-  if (!IsServiceStarted())
-    return;
-
-  audio_input_host_->OnHotwordEnabled(enable);
-}
-
-void AssistantManagerServiceImpl::SetArcPlayStoreEnabled(bool enable) {
-  DCHECK(GetState() == State::RUNNING);
-  if (assistant::features::IsAppSupportEnabled())
-    display_controller().SetArcPlayStoreEnabled(enable);
-}
-
-void AssistantManagerServiceImpl::SetAssistantContextEnabled(bool enable) {
-  DCHECK(GetState() == State::RUNNING);
-
-  media_host_->SetRelatedInfoEnabled(enable);
-  display_controller().SetRelatedInfoEnabled(enable);
-}
-
-AssistantSettings* AssistantManagerServiceImpl::GetAssistantSettings() {
-  return assistant_settings_.get();
-}
-
-void AssistantManagerServiceImpl::AddAuthenticationStateObserver(
-    AuthenticationStateObserver* observer) {
-  DCHECK(observer);
-  assistant_host_->AddAuthenticationStateObserver(
-      observer->BindNewPipeAndPassRemote());
-}
-
-void AssistantManagerServiceImpl::AddAndFireStateObserver(
-    AssistantManagerService::StateObserver* observer) {
-  state_observers_.AddObserver(observer);
-  observer->OnStateChanged(GetState());
-}
-
-void AssistantManagerServiceImpl::RemoveStateObserver(
-    const AssistantManagerService::StateObserver* observer) {
-  state_observers_.RemoveObserver(observer);
-}
-
-void AssistantManagerServiceImpl::SyncDeviceAppsStatus() {
-  assistant_settings_->SyncDeviceAppsStatus(
-      base::BindOnce(&AssistantManagerServiceImpl::OnDeviceAppsEnabled,
-                     weak_factory_.GetWeakPtr()));
-}
-
-void AssistantManagerServiceImpl::UpdateInternalMediaPlayerStatus(
-    MediaSessionAction action) {
-  if (action == MediaSessionAction::kPause) {
-    media_host_->PauseInternalMediaPlayer();
-  } else if (action == MediaSessionAction::kPlay) {
-    media_host_->ResumeInternalMediaPlayer();
-  }
-}
-
-void AssistantManagerServiceImpl::StartVoiceInteraction() {
-  DVLOG(1) << __func__;
-
-  audio_input_host_->SetMicState(true);
-  conversation_controller().StartVoiceInteraction();
-}
-
-void AssistantManagerServiceImpl::StopActiveInteraction(
-    bool cancel_conversation) {
-  DVLOG(1) << __func__;
-
-  audio_input_host_->SetMicState(false);
-  conversation_controller().StopActiveInteraction(cancel_conversation);
-}
-
-void AssistantManagerServiceImpl::StartEditReminderInteraction(
-    const std::string& client_id) {
-  conversation_controller().StartEditReminderInteraction(client_id);
-}
-
-void AssistantManagerServiceImpl::StartTextInteraction(
-    const std::string& query,
-    AssistantQuerySource source,
-    bool allow_tts) {
-  DVLOG(1) << __func__;
-
-  conversation_controller().SendTextQuery(query, source, allow_tts);
-}
-
-void AssistantManagerServiceImpl::AddAssistantInteractionSubscriber(
-    AssistantInteractionSubscriber* subscriber) {
-  // For now, this function is handling events registration for both Mojom
-  // side and Assistant service side. All native AssistantInteractionSubscriber
-  // should be deprecated and replaced with |ConversationObserver| once the
-  // migration is done.
-  interaction_subscribers_.AddObserver(subscriber);
-  AddRemoteConversationObserver(subscriber);
-}
-
-void AssistantManagerServiceImpl::RemoveAssistantInteractionSubscriber(
-    AssistantInteractionSubscriber* subscriber) {
-  interaction_subscribers_.RemoveObserver(subscriber);
-}
-
-void AssistantManagerServiceImpl::RetrieveNotification(
-    const AssistantNotification& notification,
-    int action_index) {
-  conversation_controller().RetrieveNotification(notification, action_index);
-}
-
-void AssistantManagerServiceImpl::DismissNotification(
-    const AssistantNotification& notification) {
-  conversation_controller().DismissNotification(notification);
-}
-
-void AssistantManagerServiceImpl::OnInteractionStarted(
-    const AssistantInteractionMetadata& metadata) {
-  audio_input_host_->OnConversationTurnStarted();
-}
-
-void AssistantManagerServiceImpl::OnInteractionFinished(
-    AssistantInteractionResolution resolution) {
-  switch (resolution) {
-    case AssistantInteractionResolution::kNormal:
-      RecordQueryResponseTypeUMA();
-      return;
-    case AssistantInteractionResolution::kInterruption:
-      if (HasReceivedQueryResponse())
-        RecordQueryResponseTypeUMA();
-      return;
-    case AssistantInteractionResolution::kMicTimeout:
-    case AssistantInteractionResolution::kError:
-    case AssistantInteractionResolution::kMultiDeviceHotwordLoss:
-      // No action needed.
-      return;
-  }
-}
-
-void AssistantManagerServiceImpl::OnHtmlResponse(const std::string& html,
-                                                 const std::string& fallback) {
-  receive_inline_response_ = true;
-}
-
-void AssistantManagerServiceImpl::OnTextResponse(const std::string& reponse) {
-  receive_inline_response_ = true;
-}
-
-void AssistantManagerServiceImpl::OnOpenUrlResponse(const GURL& url,
-                                                    bool in_background) {
-  receive_url_response_ = url.spec();
-}
-
-void AssistantManagerServiceImpl::OnStateChanged(
-    libassistant::mojom::ServiceState new_state) {
-  using libassistant::mojom::ServiceState;
-
-  DVLOG(1) << "Libassistant service state changed to " << new_state;
-  switch (new_state) {
-    case ServiceState::kStarted:
-      OnServiceStarted();
-      break;
-    case ServiceState::kRunning:
-      OnServiceRunning();
-      break;
-    case ServiceState::kDisconnected:
-      OnServiceDisconnected();
-      break;
-    case ServiceState::kStopped:
-      OnServiceStopped();
-      break;
-  }
-}
-
-void AssistantManagerServiceImpl::Initialize() {
-  assistant_host_->StartLibassistantService(libassistant_service_host_.get());
-
-  service_controller().AddAndFireStateObserver(
-      state_observer_receiver_.BindNewPipeAndPassRemote());
-  assistant_host_->AddSpeechRecognitionObserver(
-      speech_recognition_observer_->BindNewPipeAndPassRemote());
-  AddRemoteConversationObserver(this);
-
-  audio_output_delegate_->Bind(assistant_host_->ExtractAudioOutputDelegate());
-  platform_delegate_->Bind(assistant_host_->ExtractPlatformDelegate());
-  audio_input_host_ = std::make_unique<AudioInputHostImpl>(
-      assistant_host_->ExtractAudioInputController(),
-      context_->cras_audio_handler(), context_->power_manager_client(),
-      context_->assistant_state()->locale().value());
-
-  assistant_settings_->Initialize(
-      assistant_host_->ExtractSpeakerIdEnrollmentController(),
-      &assistant_host_->settings_controller());
-
-  media_host_->Initialize(&assistant_host_->media_controller(),
-                          assistant_host_->ExtractMediaDelegate());
-  timer_host_->Initialize(&assistant_host_->timer_controller(),
-                          assistant_host_->ExtractTimerDelegate());
-
-  device_settings_host_->Bind(assistant_host_->ExtractDeviceSettingsDelegate());
-}
-
-void AssistantManagerServiceImpl::InitAssistant(
-    const std::optional<UserInfo>& user) {
-  DCHECK(!IsServiceStarted());
-
-  auto bootup_config = bootup_config_.Clone();
-  bootup_config->authentication_tokens = ToAuthenticationTokens(user);
-  bootup_config->hotword_enabled = assistant_state()->hotword_enabled().value();
-  bootup_config->locale = assistant_state()->locale().value();
-  bootup_config->spoken_feedback_enabled = spoken_feedback_enabled_;
-  bootup_config->dark_mode_enabled = dark_mode_enabled_;
-
-  service_controller().Initialize(std::move(bootup_config),
-                                  BindURLLoaderFactory());
-  service_controller().Start();
-}
-
-base::Thread& AssistantManagerServiceImpl::GetBackgroundThreadForTesting() {
-  return background_thread();
-}
-
-void AssistantManagerServiceImpl::OnServiceStarted() {
-  DCHECK_EQ(GetState(), State::STARTING);
-
-  const base::TimeDelta time_since_started =
-      base::TimeTicks::Now() - started_time_;
-  UMA_HISTOGRAM_TIMES("Assistant.ServiceStartTime", time_since_started);
-
-  SetStateAndInformObservers(State::STARTED);
-}
-
-bool AssistantManagerServiceImpl::IsServiceStarted() const {
-  switch (state_) {
-    case State::STOPPED:
-    case State::STOPPING:
-    case State::STARTING:
-    case State::DISCONNECTED:
-      return false;
-    case State::STARTED:
-    case State::RUNNING:
-      return true;
-  }
-}
-
-mojo::PendingRemote<network::mojom::URLLoaderFactory>
-AssistantManagerServiceImpl::BindURLLoaderFactory() {
-  mojo::PendingRemote<network::mojom::URLLoaderFactory> pending_remote;
-  url_loader_factory_->Clone(pending_remote.InitWithNewPipeAndPassReceiver());
-  return pending_remote;
-}
-
-void AssistantManagerServiceImpl::OnServiceRunning() {
-  // Try to avoid double run by checking |GetState()|.
-  if (GetState() == State::RUNNING)
-    return;
-
-  SetStateAndInformObservers(State::RUNNING);
-
-  if (is_first_init) {
-    is_first_init = false;
-    // Only sync status at the first init to prevent unexpected corner cases.
-    if (assistant_state()->hotword_enabled().value())
-      assistant_settings_->SyncSpeakerIdEnrollmentStatus();
-  }
-
-  const base::TimeDelta time_since_started =
-      base::TimeTicks::Now() - started_time_;
-  UMA_HISTOGRAM_TIMES("Assistant.ServiceReadyTime", time_since_started);
-
-  SyncDeviceAppsStatus();
-
-  SetAssistantContextEnabled(assistant_state()->IsScreenContextAllowed());
-
-  if (assistant_state()->arc_play_store_enabled().has_value())
-    SetArcPlayStoreEnabled(assistant_state()->arc_play_store_enabled().value());
-
-  if (base::FeatureList::IsEnabled(assistant::features::kAssistantAppSupport))
-    scoped_app_list_event_subscriber_.Observe(device_actions());
-}
-
-void AssistantManagerServiceImpl::OnServiceStopped() {
-  // Valid `STOPPED` will only be called after `STOPPING`.
-  // However, a false `STOPPED` may be received.
-  // An ideal situation would be:
-  // 1. `AddAndFireStateObserver()` is called.
-  // 2. `ServiceController` sends the current state, e.g. STOPPED.
-  // However, since it takes a longer time (through mojom to another thread),
-  // the `state_` in this class could be temporary out of sync. For example:
-  // 1. `AddAndFireStateObserver()` is called when the `state_` is STOPPED.
-  // 2. `Start()`: sets `state_` to STARTING.
-  // 3. `ServiceController` sends the current state inside it, which is STOPPED.
-  // 4. `OnStateChanged()` receives STOPPED, but the `state_` is STARTING.
-  // We will ignore the invalid STOPPED signal.
-  if (GetState() != State::STOPPING)
-    return;
-
-  SetStateAndInformObservers(State::STOPPED);
-
-  // Stop after the `assistant_manager` was destroyed.
-  assistant_host_->StopLibassistantService();
-  ClearAfterStop();
-}
-
-void AssistantManagerServiceImpl::OnServiceDisconnected() {
-  SetStateAndInformObservers(State::DISCONNECTED);
-  ClearAfterStop();
-}
-
-void AssistantManagerServiceImpl::OnAndroidAppListRefreshed(
-    const std::vector<AndroidAppInfo>& apps_info) {
-  DCHECK(GetState() == State::RUNNING);
-
-  std::vector<AndroidAppInfo> filtered_apps_info;
-  for (const auto& app_info : apps_info) {
-    // TODO(b/146355799): Remove the special handling for Android settings app.
-    if (app_info.package_name == kAndroidSettingsAppPackage)
-      continue;
-
-    filtered_apps_info.push_back(app_info);
-  }
-  display_controller().SetAndroidAppList(std::move(filtered_apps_info));
-}
-
-void AssistantManagerServiceImpl::OnAccessibilityStatusChanged(
-    bool spoken_feedback_enabled) {
-  if (spoken_feedback_enabled_ == spoken_feedback_enabled)
-    return;
-
-  spoken_feedback_enabled_ = spoken_feedback_enabled;
-
-  // When |spoken_feedback_enabled_| changes we need to update our internal
-  // options to turn on/off A11Y features in LibAssistant.
-  if (IsServiceStarted())
-    settings_controller().SetSpokenFeedbackEnabled(spoken_feedback_enabled_);
-}
-
-void AssistantManagerServiceImpl::OnColorModeChanged(bool dark_mode_enabled) {
-  if (dark_mode_enabled_ == dark_mode_enabled)
-    return;
-
-  dark_mode_enabled_ = dark_mode_enabled;
-
-  if (IsServiceStarted())
-    settings_controller().SetDarkModeEnabled(dark_mode_enabled_);
-}
-
-void AssistantManagerServiceImpl::OnDeviceAppsEnabled(bool enabled) {
-  // The device apps state sync should only be sent after service is running.
-  // Check state here to prevent timing issue when the service is restarting.
-  if (GetState() != State::RUNNING)
-    return;
-
-  display_controller().SetDeviceAppsEnabled(enabled);
-
-  // You can set initialized_internal callback only when AssistantStatus is
-  // NOT_READY. Also this line reaches only after GetState() becomes RUNNING
-  // (i.e. READY). From that reason, test code can assume that status has
-  // changed from NOT_READY to READY between those two points.
-  //
-  // Test code expects those things when Assistant gets initialized:
-  //
-  // - Status becomes READY.
-  // - All necessary settings are passed to LibAssistant.
-  //
-  // We update necessary settings after status becomes READY. For now,
-  // DeviceAppsEnabled is the only settings update which involves async call.
-  // As other settings are sync, if this async call gets completed, we can also
-  // assume that all necessary settings are passed to LibAssistant, i.e.
-  // initialized.
-  if (!initialized_internal_callback_for_testing.is_null()) {
-    std::move(initialized_internal_callback_for_testing).Run();
-  }
-}
-
-void AssistantManagerServiceImpl::AddTimeToTimer(const std::string& id,
-                                                 base::TimeDelta duration) {
-  timer_host_->AddTimeToTimer(id, duration);
-}
-
-void AssistantManagerServiceImpl::PauseTimer(const std::string& id) {
-  timer_host_->PauseTimer(id);
-}
-
-void AssistantManagerServiceImpl::RemoveAlarmOrTimer(const std::string& id) {
-  timer_host_->RemoveTimer(id);
-}
-
-void AssistantManagerServiceImpl::ResumeTimer(const std::string& id) {
-  timer_host_->ResumeTimer(id);
-}
-
-void AssistantManagerServiceImpl::AddRemoteConversationObserver(
-    ConversationObserver* observer) {
-  conversation_controller().AddRemoteObserver(
-      observer->BindNewPipeAndPassRemote());
-}
-
-mojo::PendingReceiver<libassistant::mojom::NotificationDelegate>
-AssistantManagerServiceImpl::GetPendingNotificationDelegate() {
-  return assistant_host_->ExtractNotificationDelegate();
-}
-
-void AssistantManagerServiceImpl::RecordQueryResponseTypeUMA() {
-  AssistantQueryResponseType response_type = GetQueryResponseType();
-
-  UMA_HISTOGRAM_ENUMERATION("Assistant.QueryResponseType", response_type);
-
-  // Reset the flags.
-  receive_url_response_.clear();
-  receive_inline_response_ = false;
-  device_settings_host_->reset_has_setting_changed();
-}
-
-bool AssistantManagerServiceImpl::HasReceivedQueryResponse() const {
-  return GetQueryResponseType() != AssistantQueryResponseType::kUnspecified;
-}
-
-AssistantQueryResponseType AssistantManagerServiceImpl::GetQueryResponseType()
-    const {
-  if (device_settings_host_->has_setting_changed()) {
-    return AssistantQueryResponseType::kDeviceAction;
-  } else if (!receive_url_response_.empty()) {
-    if (base::Contains(receive_url_response_, "www.google.com/search?")) {
-      return AssistantQueryResponseType::kSearchFallback;
-    } else {
-      return AssistantQueryResponseType::kTargetedAction;
-    }
-  } else if (receive_inline_response_) {
-    return AssistantQueryResponseType::kInlineElement;
-  }
-
-  return AssistantQueryResponseType::kUnspecified;
-}
-
-void AssistantManagerServiceImpl::SendAssistantFeedback(
-    const AssistantFeedback& assistant_feedback) {
-  conversation_controller().SendAssistantFeedback(assistant_feedback);
-}
-
-AssistantNotificationController*
-AssistantManagerServiceImpl::assistant_notification_controller() {
-  return context_->assistant_notification_controller();
-}
-
-AssistantStateBase* AssistantManagerServiceImpl::assistant_state() {
-  return context_->assistant_state();
-}
-
-DeviceActions* AssistantManagerServiceImpl::device_actions() {
-  return context_->device_actions();
-}
-
-scoped_refptr<base::SequencedTaskRunner>
-AssistantManagerServiceImpl::main_task_runner() {
-  return context_->main_task_runner();
-}
-
-libassistant::mojom::DisplayController&
-AssistantManagerServiceImpl::display_controller() {
-  return assistant_host_->display_controller();
-}
-
-libassistant::mojom::ServiceController&
-AssistantManagerServiceImpl::service_controller() {
-  return assistant_host_->service_controller();
-}
-
-void AssistantManagerServiceImpl::SetMicState(bool mic_open) {
-  DCHECK(audio_input_host_);
-  audio_input_host_->SetMicState(mic_open);
-}
-
-libassistant::mojom::ConversationController&
-AssistantManagerServiceImpl::conversation_controller() {
-  return assistant_host_->conversation_controller();
-}
-
-libassistant::mojom::SettingsController&
-AssistantManagerServiceImpl::settings_controller() {
-  return assistant_host_->settings_controller();
-}
-
-base::Thread& AssistantManagerServiceImpl::background_thread() {
-  return assistant_host_->background_thread();
-}
-
-void AssistantManagerServiceImpl::SetStateAndInformObservers(State new_state) {
-  state_ = new_state;
-
-  for (auto& observer : state_observers_)
-    observer.OnStateChanged(state_);
-}
-
-void AssistantManagerServiceImpl::ClearAfterStop() {
-  weak_factory_.InvalidateWeakPtrs();
-
-  state_observer_receiver_.reset();
-  speech_recognition_observer_->Stop();
-  audio_output_delegate_->Stop();
-  platform_delegate_->Stop();
-  audio_input_host_.reset();
-  assistant_settings_->Stop();
-  media_host_->Stop();
-  timer_host_->Stop();
-  device_settings_host_->Stop();
-
-  scoped_app_list_event_subscriber_.Reset();
-  interaction_subscribers_.Clear();
-  state_observers_.Clear();
-
-  is_first_init = true;
-}
-
-}  // namespace ash::assistant
diff --git a/chromeos/ash/services/assistant/assistant_manager_service_impl.h b/chromeos/ash/services/assistant/assistant_manager_service_impl.h
deleted file mode 100644
index 9098c214..0000000
--- a/chromeos/ash/services/assistant/assistant_manager_service_impl.h
+++ /dev/null
@@ -1,283 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROMEOS_ASH_SERVICES_ASSISTANT_ASSISTANT_MANAGER_SERVICE_IMPL_H_
-#define CHROMEOS_ASH_SERVICES_ASSISTANT_ASSISTANT_MANAGER_SERVICE_IMPL_H_
-
-#include <map>
-#include <memory>
-#include <optional>
-#include <string>
-#include <vector>
-
-#include "base/cancelable_callback.h"
-#include "base/memory/raw_ptr.h"
-#include "base/memory/scoped_refptr.h"
-#include "base/observer_list.h"
-#include "base/scoped_observation.h"
-#include "base/synchronization/lock.h"
-#include "base/task/sequenced_task_runner.h"
-#include "base/threading/thread.h"
-#include "base/time/time.h"
-#include "chromeos/ash/services/assistant/assistant_host.h"
-#include "chromeos/ash/services/assistant/assistant_manager_service.h"
-#include "chromeos/ash/services/assistant/assistant_settings_impl.h"
-#include "chromeos/ash/services/assistant/libassistant_service_host.h"
-#include "chromeos/ash/services/assistant/public/cpp/assistant_service.h"
-#include "chromeos/ash/services/assistant/public/cpp/conversation_observer.h"
-#include "chromeos/ash/services/assistant/public/cpp/device_actions.h"
-#include "chromeos/ash/services/libassistant/public/cpp/assistant_notification.h"
-#include "chromeos/ash/services/libassistant/public/mojom/notification_delegate.mojom.h"
-#include "chromeos/ash/services/libassistant/public/mojom/service_controller.mojom-forward.h"
-#include "chromeos/services/assistant/public/shared/utils.h"
-#include "mojo/public/cpp/bindings/pending_receiver.h"
-#include "mojo/public/cpp/bindings/pending_remote.h"
-#include "mojo/public/cpp/bindings/receiver.h"
-#include "mojo/public/cpp/bindings/remote.h"
-#include "services/device/public/mojom/battery_monitor.mojom.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "ui/accessibility/ax_assistant_structure.h"
-#include "ui/accessibility/mojom/ax_assistant_structure.mojom.h"
-
-namespace ash {
-
-class AssistantNotificationController;
-class AssistantStateBase;
-
-namespace assistant {
-
-class AssistantHost;
-class AssistantMediaSession;
-class AudioInputHost;
-class AudioOutputDelegateImpl;
-class DeviceSettingsHost;
-class MediaHost;
-class PlatformDelegateImpl;
-class ServiceContext;
-class SpeechRecognitionObserverWrapper;
-class TimerHost;
-
-// Enumeration of Assistant query response type, also recorded in histograms.
-// These values are persisted to logs. Entries should not be renumbered and
-// numeric values should never be reused. Only append to this enum is allowed
-// if the possible type grows.
-enum class AssistantQueryResponseType {
-  // Query without response.
-  kUnspecified = 0,
-  // Query results in device actions (e.g. turn on bluetooth/WiFi).
-  kDeviceAction = 1,
-  // Query results in answer cards with contents rendered inside the
-  // Assistant UI.
-  kInlineElement = 2,
-  // Query results in searching on Google, indicating that Assistant
-  // doesn't know what to do.
-  kSearchFallback = 3,
-  // Query results in specific actions (e.g. opening a web app such as YouTube
-  // or Facebook, some deeplink actions such as opening chrome settings page),
-  // indicating that Assistant knows what to do.
-  kTargetedAction = 4,
-  // Special enumerator value used by histogram macros.
-  kMaxValue = kTargetedAction
-};
-
-// Implementation of AssistantManagerService based on LibAssistant.
-// This is the main class that interacts with LibAssistant.
-// Since LibAssistant is a standalone library, all callbacks come from it
-// running on threads not owned by Chrome. Thus we need to post the callbacks
-// onto the main thread.
-// NOTE: this class may start/stop LibAssistant multiple times throughout its
-// lifetime. This may occur, for example, if the user manually toggles Assistant
-// enabled/disabled in settings or switches to a non-primary profile.
-class COMPONENT_EXPORT(ASSISTANT_SERVICE) AssistantManagerServiceImpl
-    : public AssistantManagerService,
-      public AppListEventSubscriber,
-      private libassistant::mojom::StateObserver,
-      public ConversationObserver {
- public:
-  // |callback| is called when AssistantManagerServiceImpl got initialized
-  // internally. This waits DeviceApps config value sync.
-  static void SetInitializedInternalCallbackForTesting(
-      base::OnceCallback<void()> callback);
-  static void ResetIsFirstInitFlagForTesting();
-
-  // |service| owns this class and must outlive this class.
-  AssistantManagerServiceImpl(
-      ServiceContext* context,
-      std::unique_ptr<network::PendingSharedURLLoaderFactory>
-          pending_url_loader_factory,
-      std::optional<std::string> s3_server_uri_override,
-      std::optional<std::string> device_id_override,
-      // Allows to inect a custom |LibassistantServiceHost| during unittests.
-      std::unique_ptr<LibassistantServiceHost> libassistant_service_host =
-          nullptr);
-
-  AssistantManagerServiceImpl(const AssistantManagerServiceImpl&) = delete;
-  AssistantManagerServiceImpl& operator=(const AssistantManagerServiceImpl&) =
-      delete;
-
-  ~AssistantManagerServiceImpl() override;
-
-  // assistant::AssistantManagerService overrides:
-  void Start(const std::optional<UserInfo>& user, bool enable_hotword) override;
-  void Stop() override;
-  State GetState() const override;
-  void SetUser(const std::optional<UserInfo>& user) override;
-  void EnableListening(bool enable) override;
-  void EnableHotword(bool enable) override;
-  void SetArcPlayStoreEnabled(bool enable) override;
-  void SetAssistantContextEnabled(bool enable) override;
-  AssistantSettings* GetAssistantSettings() override;
-  void AddAuthenticationStateObserver(
-      AuthenticationStateObserver* observer) override;
-  void AddAndFireStateObserver(
-      AssistantManagerService::StateObserver* observer) override;
-  void RemoveStateObserver(
-      const AssistantManagerService::StateObserver* observer) override;
-  void SyncDeviceAppsStatus() override;
-  void UpdateInternalMediaPlayerStatus(
-      media_session::mojom::MediaSessionAction action) override;
-
-  // Assistant overrides:
-  void StartEditReminderInteraction(const std::string& client_id) override;
-  void StartTextInteraction(const std::string& query,
-                            AssistantQuerySource source,
-                            bool allow_tts) override;
-  void StartVoiceInteraction() override;
-  void StopActiveInteraction(bool cancel_conversation) override;
-  void AddAssistantInteractionSubscriber(
-      AssistantInteractionSubscriber* subscriber) override;
-  void RemoveAssistantInteractionSubscriber(
-      AssistantInteractionSubscriber* subscriber) override;
-  void RetrieveNotification(const AssistantNotification& notification,
-                            int action_index) override;
-  void DismissNotification(const AssistantNotification& notification) override;
-  void OnAccessibilityStatusChanged(bool spoken_feedback_enabled) override;
-  void OnColorModeChanged(bool dark_mode_enabled) override;
-  void SendAssistantFeedback(const AssistantFeedback& feedback) override;
-  void AddTimeToTimer(const std::string& id, base::TimeDelta duration) override;
-  void PauseTimer(const std::string& id) override;
-  void RemoveAlarmOrTimer(const std::string& id) override;
-  void ResumeTimer(const std::string& id) override;
-  void AddRemoteConversationObserver(ConversationObserver* observer) override;
-  mojo::PendingReceiver<libassistant::mojom::NotificationDelegate>
-  GetPendingNotificationDelegate() override;
-
-  // ConversationObserver overrides:
-  void OnInteractionStarted(
-      const AssistantInteractionMetadata& metadata) override;
-  void OnInteractionFinished(
-      AssistantInteractionResolution resolution) override;
-  void OnHtmlResponse(const std::string& response,
-                      const std::string& fallback) override;
-  void OnTextResponse(const std::string& reponse) override;
-  void OnOpenUrlResponse(const GURL& url, bool in_background) override;
-
-  // AppListEventSubscriber overrides:
-  void OnAndroidAppListRefreshed(
-      const std::vector<AndroidAppInfo>& apps_info) override;
-
-  // libassistant::mojom::StateObserver implementation:
-  void OnStateChanged(libassistant::mojom::ServiceState new_state) override;
-
-  void SetMicState(bool mic_open);
-
-  base::Thread& GetBackgroundThreadForTesting();
-
- private:
-  void Initialize();
-  void InitAssistant(const std::optional<UserInfo>& user);
-  void OnServiceStarted();
-  void OnServiceRunning();
-  void OnServiceStopped();
-  void OnServiceDisconnected();
-  bool IsServiceStarted() const;
-
-  mojo::PendingRemote<network::mojom::URLLoaderFactory> BindURLLoaderFactory();
-
-  void OnModifySettingsAction(const std::string& modify_setting_args_proto);
-
-  void OnDeviceAppsEnabled(bool enabled);
-
-  void FillServerExperimentIds(std::vector<std::string>* server_experiment_ids);
-
-  // Record the response type for each query. Note that query on device
-  // actions (e.g. turn on Bluetooth, turn on WiFi) will cause duplicate
-  // record because it interacts with server twice on on the same query.
-  // The first round interaction checks if a setting is supported with no
-  // responses sent back and ends normally (will be recorded as kUnspecified),
-  // and settings modification proto along with any text/voice responses would
-  // be sent back in the second round (recorded as kDeviceAction).
-  void RecordQueryResponseTypeUMA();
-  bool HasReceivedQueryResponse() const;
-  AssistantQueryResponseType GetQueryResponseType() const;
-
-  std::string NewPendingInteraction(AssistantInteractionType interaction_type,
-                                    AssistantQuerySource source,
-                                    const std::string& query);
-
-  void SendVoicelessInteraction(const std::string& interaction,
-                                const std::string& description,
-                                bool is_user_initiated);
-
-  AssistantNotificationController* assistant_notification_controller();
-  AssistantStateBase* assistant_state();
-  DeviceActions* device_actions();
-  scoped_refptr<base::SequencedTaskRunner> main_task_runner();
-
-  libassistant::mojom::ConversationController& conversation_controller();
-  libassistant::mojom::DisplayController& display_controller();
-  libassistant::mojom::ServiceController& service_controller();
-  libassistant::mojom::SettingsController& settings_controller();
-  base::Thread& background_thread();
-
-  void SetStateAndInformObservers(State new_state);
-
-  void ClearAfterStop();
-
-  State state_ = State::STOPPED;
-
-  std::unique_ptr<AssistantSettingsImpl> assistant_settings_;
-
-  std::unique_ptr<AssistantHost> assistant_host_;
-  std::unique_ptr<PlatformDelegateImpl> platform_delegate_;
-  std::unique_ptr<AudioInputHost> audio_input_host_;
-
-  base::ObserverList<AssistantInteractionSubscriber> interaction_subscribers_;
-
-  // Owned by the parent |Service| which will destroy |this| before |context_|.
-  const raw_ptr<ServiceContext> context_;
-
-  std::unique_ptr<LibassistantServiceHost> libassistant_service_host_;
-  std::unique_ptr<DeviceSettingsHost> device_settings_host_;
-  std::unique_ptr<MediaHost> media_host_;
-  std::unique_ptr<TimerHost> timer_host_;
-  std::unique_ptr<AudioOutputDelegateImpl> audio_output_delegate_;
-  std::unique_ptr<SpeechRecognitionObserverWrapper>
-      speech_recognition_observer_;
-  mojo::Receiver<libassistant::mojom::StateObserver> state_observer_receiver_{
-      this};
-
-  bool spoken_feedback_enabled_ = false;
-  bool dark_mode_enabled_ = false;
-
-  base::TimeTicks started_time_;
-
-  bool receive_inline_response_ = false;
-  std::string receive_url_response_;
-
-  // Configuration passed to libassistant.
-  libassistant::mojom::BootupConfigPtr bootup_config_;
-  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
-
-  base::ScopedObservation<DeviceActions, AppListEventSubscriber>
-      scoped_app_list_event_subscriber_{this};
-  base::ObserverList<AssistantManagerService::StateObserver> state_observers_;
-
-  base::WeakPtrFactory<AssistantManagerServiceImpl> weak_factory_;
-};
-
-}  // namespace assistant
-}  // namespace ash
-
-#endif  // CHROMEOS_ASH_SERVICES_ASSISTANT_ASSISTANT_MANAGER_SERVICE_IMPL_H_
diff --git a/chromeos/ash/services/assistant/assistant_manager_service_impl_unittest.cc b/chromeos/ash/services/assistant/assistant_manager_service_impl_unittest.cc
deleted file mode 100644
index 35a2796..0000000
--- a/chromeos/ash/services/assistant/assistant_manager_service_impl_unittest.cc
+++ /dev/null
@@ -1,785 +0,0 @@
-// 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.
-
-#include "chromeos/ash/services/assistant/assistant_manager_service_impl.h"
-
-#include <optional>
-#include <string>
-#include <utility>
-
-#include "ash/public/cpp/assistant/controller/assistant_alarm_timer_controller.h"
-#include "base/json/json_reader.h"
-#include "base/memory/raw_ptr.h"
-#include "base/memory/weak_ptr.h"
-#include "base/run_loop.h"
-#include "base/test/bind.h"
-#include "base/test/task_environment.h"
-#include "base/values.h"
-#include "chromeos/ash/components/assistant/test_support/expect_utils.h"
-#include "chromeos/ash/components/audio/cras_audio_handler.h"
-#include "chromeos/ash/services/assistant/assistant_manager_service.h"
-#include "chromeos/ash/services/assistant/libassistant_service_host.h"
-#include "chromeos/ash/services/assistant/public/cpp/assistant_service.h"
-#include "chromeos/ash/services/assistant/public/cpp/features.h"
-#include "chromeos/ash/services/assistant/service_context.h"
-#include "chromeos/ash/services/assistant/test_support/fake_libassistant_service.h"
-#include "chromeos/ash/services/assistant/test_support/fake_service_context.h"
-#include "chromeos/ash/services/assistant/test_support/fully_initialized_assistant_state.h"
-#include "chromeos/ash/services/assistant/test_support/libassistant_media_controller_mock.h"
-#include "chromeos/ash/services/assistant/test_support/mock_assistant_interaction_subscriber.h"
-#include "chromeos/ash/services/assistant/test_support/scoped_assistant_browser_delegate.h"
-#include "chromeos/ash/services/assistant/test_support/scoped_device_actions.h"
-#include "chromeos/ash/services/libassistant/public/cpp/assistant_timer.h"
-#include "chromeos/ash/services/libassistant/public/mojom/speaker_id_enrollment_controller.mojom.h"
-#include "chromeos/dbus/power/fake_power_manager_client.h"
-#include "google_apis/gaia/gaia_id.h"
-#include "mojo/public/cpp/bindings/pending_remote.h"
-#include "mojo/public/cpp/bindings/receiver.h"
-#include "services/media_session/public/mojom/media_session.mojom-shared.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
-#include "services/network/test/test_url_loader_factory.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace ash::assistant {
-
-using libassistant::mojom::ServiceState;
-using libassistant::mojom::SpeakerIdEnrollmentStatus;
-using media_session::mojom::MediaSessionAction;
-using testing::_;
-using testing::ElementsAre;
-using testing::Invoke;
-using testing::NiceMock;
-using testing::StrictMock;
-using UserInfo = AssistantManagerService::UserInfo;
-
-namespace {
-
-const char* kNoValue = FakeServiceController::kNoValue;
-#define EXPECT_STATE(_state) \
-  EXPECT_EQ(_state, assistant_manager_service()->GetState());
-
-class AssistantAlarmTimerControllerMock : public AssistantAlarmTimerController {
- public:
-  AssistantAlarmTimerControllerMock() = default;
-  AssistantAlarmTimerControllerMock(const AssistantAlarmTimerControllerMock&) =
-      delete;
-  AssistantAlarmTimerControllerMock& operator=(
-      const AssistantAlarmTimerControllerMock&) = delete;
-  ~AssistantAlarmTimerControllerMock() override = default;
-
-  // AssistantAlarmTimerController:
-  MOCK_METHOD((const AssistantAlarmTimerModel*),
-              GetModel,
-              (),
-              (const, override));
-
-  MOCK_METHOD(void,
-              OnTimerStateChanged,
-              (const std::vector<AssistantTimer>&),
-              (override));
-};
-
-class FakeLibassistantServiceHost : public LibassistantServiceHost {
- public:
-  explicit FakeLibassistantServiceHost(FakeLibassistantService* service)
-      : service_(service) {}
-
-  void Launch(mojo::PendingReceiver<libassistant::mojom::LibassistantService>
-                  receiver) override {
-    service_->Bind(std::move(receiver));
-  }
-  void Stop() override { service_->Unbind(); }
-
- private:
-  raw_ptr<FakeLibassistantService> service_;
-};
-
-class StateObserverMock : public AssistantManagerService::StateObserver {
- public:
-  StateObserverMock() = default;
-
-  StateObserverMock(const StateObserverMock&) = delete;
-  StateObserverMock& operator=(const StateObserverMock&) = delete;
-
-  ~StateObserverMock() override = default;
-
-  MOCK_METHOD(void, OnStateChanged, (AssistantManagerService::State new_state));
-};
-
-class AssistantManagerServiceImplTest : public testing::Test {
- public:
-  AssistantManagerServiceImplTest() = default;
-
-  AssistantManagerServiceImplTest(const AssistantManagerServiceImplTest&) =
-      delete;
-  AssistantManagerServiceImplTest& operator=(
-      const AssistantManagerServiceImplTest&) = delete;
-
-  ~AssistantManagerServiceImplTest() override = default;
-
-  void SetUp() override {
-    chromeos::PowerManagerClient::InitializeFake();
-    chromeos::FakePowerManagerClient::Get()->SetTabletMode(
-        chromeos::PowerManagerClient::TabletMode::OFF, base::TimeTicks());
-
-    mojo::PendingRemote<device::mojom::BatteryMonitor> battery_monitor;
-    delegate_.RequestBatteryMonitor(
-        battery_monitor.InitWithNewPipeAndPassReceiver());
-
-    shared_url_loader_factory_ =
-        base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
-            &url_loader_factory_);
-
-    alarm_timer_controller_ =
-        std::make_unique<NiceMock<AssistantAlarmTimerControllerMock>>();
-
-    service_context_ = std::make_unique<FakeServiceContext>();
-    service_context_
-        ->set_main_task_runner(task_environment().GetMainThreadTaskRunner())
-        .set_power_manager_client(chromeos::PowerManagerClient::Get())
-        .set_assistant_state(&assistant_state_)
-        .set_cras_audio_handler(&cras_audio_handler_.Get())
-        .set_assistant_alarm_timer_controller(alarm_timer_controller_.get());
-
-    CreateAssistantManagerServiceImpl();
-
-    // Flushes the background thread to let Mojom finish all its work (i.e.
-    // binding controllers) before moving formard.
-    RunUntilIdle();
-  }
-
-  void TearDown() override {
-    assistant_manager_service_.reset();
-    chromeos::PowerManagerClient::Shutdown();
-  }
-
-  void CreateAssistantManagerServiceImpl(
-      std::optional<std::string> s3_server_uri_override = std::nullopt,
-      std::optional<std::string> device_id_override = std::nullopt) {
-    // We can not have 2 instances of |AssistantManagerServiceImpl| at the same
-    // time, so we must destroy the old one before creating a new one.
-    assistant_manager_service_.reset();
-
-    assistant_manager_service_ = std::make_unique<AssistantManagerServiceImpl>(
-        service_context_.get(), shared_url_loader_factory_->Clone(),
-        s3_server_uri_override, device_id_override,
-        std::make_unique<FakeLibassistantServiceHost>(&libassistant_service_));
-  }
-
-  FakeServiceController& mojom_service_controller() {
-    return libassistant_service_.service_controller();
-  }
-
-  FakeLibassistantService& mojom_libassistant_service() {
-    return libassistant_service_;
-  }
-
-  AssistantManagerServiceImpl* assistant_manager_service() {
-    return assistant_manager_service_.get();
-  }
-
-  AssistantSettings& assistant_settings() {
-    auto* result = assistant_manager_service()->GetAssistantSettings();
-    DCHECK(result);
-    return *result;
-  }
-
-  FullyInitializedAssistantState& assistant_state() { return assistant_state_; }
-
-  void SetAssistantStateContext(bool enabled) {
-    assistant_state_.SetContextEnabled(enabled);
-  }
-
-  FakeServiceContext* fake_service_context() { return service_context_.get(); }
-
-  base::test::TaskEnvironment& task_environment() { return task_environment_; }
-
-  void Start() {
-    assistant_manager_service()->Start(
-        UserInfo(GaiaId("<user-id>"), "<access-token>"),
-        /*enable_hotword=*/false);
-  }
-
-  // Start Libassistant, and wait until it is running.
-  void StartAndWaitForRunning() {
-    Start();
-    WaitForState(AssistantManagerService::STARTED);
-    mojom_service_controller().SetState(ServiceState::kRunning);
-    WaitForState(AssistantManagerService::RUNNING);
-  }
-
-  void RunUntilIdle() {
-    // First ensure our mojom thread is finished.
-    FlushForTesting();
-    // Then handle any callbacks.
-    base::RunLoop().RunUntilIdle();
-  }
-
-  void FlushForTesting() {
-    libassistant_service_.FlushForTesting();
-    background_thread().FlushForTesting();
-  }
-
-  // Adds a state observer mock, and add the expectation for the fact that it
-  // auto-fires the observer.
-  void AddStateObserver(StateObserverMock* observer) {
-    EXPECT_CALL(*observer,
-                OnStateChanged(assistant_manager_service()->GetState()));
-    assistant_manager_service()->AddAndFireStateObserver(observer);
-  }
-
-  void WaitForState(AssistantManagerService::State expected_state) {
-    test::ExpectResult(
-        expected_state,
-        base::BindRepeating(&AssistantManagerServiceImpl::GetState,
-                            base::Unretained(assistant_manager_service())),
-        "AssistantManagerStateImpl");
-  }
-
- private:
-  base::Thread& background_thread() {
-    return assistant_manager_service()->GetBackgroundThreadForTesting();
-  }
-
-  base::test::SingleThreadTaskEnvironment task_environment_;
-
-  ScopedAssistantBrowserDelegate delegate_;
-  ScopedCrasAudioHandlerForTesting cras_audio_handler_;
-  ScopedDeviceActions device_actions_;
-  FullyInitializedAssistantState assistant_state_;
-
-  // Fake implementation of the Libassistant Mojom service.
-  FakeLibassistantService libassistant_service_;
-
-  std::unique_ptr<AssistantAlarmTimerControllerMock> alarm_timer_controller_;
-  std::unique_ptr<FakeServiceContext> service_context_;
-
-  network::TestURLLoaderFactory url_loader_factory_;
-  scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory_;
-
-  std::unique_ptr<AssistantManagerServiceImpl> assistant_manager_service_;
-};
-
-class SpeakerIdEnrollmentControllerMock
-    : public libassistant::mojom::SpeakerIdEnrollmentController {
- public:
-  SpeakerIdEnrollmentControllerMock() = default;
-  SpeakerIdEnrollmentControllerMock(const SpeakerIdEnrollmentControllerMock&) =
-      delete;
-  SpeakerIdEnrollmentControllerMock& operator=(
-      const SpeakerIdEnrollmentControllerMock&) = delete;
-  ~SpeakerIdEnrollmentControllerMock() override = default;
-
-  // libassistant::mojom::SpeakerIdEnrollmentController implementation:
-  MOCK_METHOD(
-      void,
-      StartSpeakerIdEnrollment,
-      (const std::string& user_gaia_id,
-       bool skip_cloud_enrollment,
-       mojo::PendingRemote<libassistant::mojom::SpeakerIdEnrollmentClient>
-           client));
-  MOCK_METHOD(void, StopSpeakerIdEnrollment, ());
-  MOCK_METHOD(void,
-              GetSpeakerIdEnrollmentStatus,
-              (const std::string& user_gaia_id,
-               GetSpeakerIdEnrollmentStatusCallback callback));
-
-  void Bind(
-      mojo::PendingReceiver<libassistant::mojom::SpeakerIdEnrollmentController>
-          pending_receiver) {
-    receiver_.Bind(std::move(pending_receiver));
-  }
-
-  void Bind(FakeLibassistantService& service) {
-    Bind(service.GetSpeakerIdEnrollmentControllerPendingReceiver());
-  }
-
-  void FlushForTesting() { receiver_.FlushForTesting(); }
-
- private:
-  mojo::Receiver<SpeakerIdEnrollmentController> receiver_{this};
-};
-
-class SpeakerIdEnrollmentClientMock : public SpeakerIdEnrollmentClient {
- public:
-  SpeakerIdEnrollmentClientMock() = default;
-  SpeakerIdEnrollmentClientMock(const SpeakerIdEnrollmentClientMock&) = delete;
-  SpeakerIdEnrollmentClientMock& operator=(
-      const SpeakerIdEnrollmentClientMock&) = delete;
-  ~SpeakerIdEnrollmentClientMock() override = default;
-
-  base::WeakPtr<SpeakerIdEnrollmentClientMock> GetWeakPtr() {
-    return weak_factory_.GetWeakPtr();
-  }
-
-  // SpeakerIdEnrollmentClient implementation:
-  MOCK_METHOD(void, OnListeningHotword, ());
-  MOCK_METHOD(void, OnProcessingHotword, ());
-  MOCK_METHOD(void, OnSpeakerIdEnrollmentDone, ());
-  MOCK_METHOD(void, OnSpeakerIdEnrollmentFailure, ());
-
- private:
-  base::WeakPtrFactory<SpeakerIdEnrollmentClientMock> weak_factory_{this};
-};
-
-}  // namespace
-
-TEST_F(AssistantManagerServiceImplTest, StateShouldStartAsStopped) {
-  EXPECT_STATE(AssistantManagerService::STOPPED);
-}
-
-TEST_F(AssistantManagerServiceImplTest,
-       StateShouldChangeToStartingAfterCallingStart) {
-  Start();
-
-  EXPECT_STATE(AssistantManagerService::STARTING);
-}
-
-TEST_F(AssistantManagerServiceImplTest,
-       StateShouldRemainStartingUntilLibassistantServiceIsStarted) {
-  mojom_service_controller().BlockStartCalls();
-
-  Start();
-  WaitForState(AssistantManagerService::STARTING);
-
-  mojom_service_controller().UnblockStartCalls();
-  WaitForState(AssistantManagerService::STARTED);
-}
-
-TEST_F(AssistantManagerServiceImplTest,
-       StateShouldBecomeRunningAfterLibassistantSignalsRunningState) {
-  Start();
-  WaitForState(AssistantManagerService::STARTED);
-
-  mojom_service_controller().SetState(ServiceState::kRunning);
-
-  WaitForState(AssistantManagerService::RUNNING);
-}
-
-TEST_F(AssistantManagerServiceImplTest, ShouldSetStateToStoppedAfterStopping) {
-  Start();
-  WaitForState(AssistantManagerService::STARTED);
-
-  assistant_manager_service()->Stop();
-  WaitForState(AssistantManagerService::STOPPED);
-}
-
-TEST_F(AssistantManagerServiceImplTest, ShouldSetStateToDisconnected) {
-  Start();
-  WaitForState(AssistantManagerService::STARTED);
-
-  mojom_service_controller().SetState(ServiceState::kDisconnected);
-  WaitForState(AssistantManagerService::DISCONNECTED);
-}
-
-TEST_F(AssistantManagerServiceImplTest, ShouldAllowRestartingAfterStopping) {
-  Start();
-  WaitForState(AssistantManagerService::STARTED);
-
-  assistant_manager_service()->Stop();
-  WaitForState(AssistantManagerService::STOPPED);
-
-  Start();
-  WaitForState(AssistantManagerService::STARTED);
-}
-
-TEST_F(AssistantManagerServiceImplTest, ShouldNotResetDataWhenStopping) {
-  Start();
-  WaitForState(AssistantManagerService::STARTED);
-
-  assistant_manager_service()->Stop();
-  WaitForState(AssistantManagerService::STOPPED);
-  RunUntilIdle();
-
-  EXPECT_EQ(false, mojom_service_controller().has_data_been_reset());
-}
-
-TEST_F(AssistantManagerServiceImplTest,
-       ShouldResetDataWhenAssistantIsDisabled) {
-  Start();
-  WaitForState(AssistantManagerService::STARTED);
-
-  assistant_state().SetAssistantEnabled(false);
-  assistant_manager_service()->Stop();
-  WaitForState(AssistantManagerService::STOPPED);
-  RunUntilIdle();
-
-  EXPECT_EQ(true, mojom_service_controller().has_data_been_reset());
-}
-
-TEST_F(AssistantManagerServiceImplTest,
-       ShouldPassUserInfoToAssistantManagerWhenStarting) {
-  assistant_manager_service()->Start(
-      UserInfo(GaiaId("<user-id>"), "<access-token>"),
-      /*enable_hotword=*/false);
-
-  WaitForState(AssistantManagerService::STARTED);
-
-  EXPECT_EQ(GaiaId("<user-id>"), mojom_service_controller().gaia_id());
-  EXPECT_EQ("<access-token>", mojom_service_controller().access_token());
-}
-
-// TODO(crbug.com/40902244): Re-enable this test
-TEST_F(AssistantManagerServiceImplTest,
-       DISABLED_ShouldPassUserInfoToAssistantManager) {
-  Start();
-  WaitForState(AssistantManagerService::STARTED);
-
-  assistant_manager_service()->SetUser(
-      UserInfo(GaiaId("<new-user-id>"), "<new-access-token>"));
-  RunUntilIdle();
-
-  EXPECT_EQ(GaiaId("<new-user-id>"), mojom_service_controller().gaia_id());
-  EXPECT_EQ("<new-access-token>", mojom_service_controller().access_token());
-}
-
-TEST_F(AssistantManagerServiceImplTest,
-       // TODO(crbug.com/40902244): Re-enable this test
-       DISABLED_ShouldPassEmptyUserInfoToAssistantManager) {
-  Start();
-  WaitForState(AssistantManagerService::STARTED);
-
-  assistant_manager_service()->SetUser(std::nullopt);
-  RunUntilIdle();
-
-  EXPECT_EQ(GaiaId(kNoValue), mojom_service_controller().gaia_id());
-  EXPECT_EQ(kNoValue, mojom_service_controller().access_token());
-}
-
-TEST_F(AssistantManagerServiceImplTest,
-       ShouldNotCrashWhenSettingUserInfoBeforeStartIsFinished) {
-  EXPECT_STATE(AssistantManagerService::STOPPED);
-  assistant_manager_service()->SetUser(
-      UserInfo(GaiaId("<user-id>"), "<access-token>"));
-
-  Start();
-  EXPECT_STATE(AssistantManagerService::STARTING);
-  assistant_manager_service()->SetUser(
-      UserInfo(GaiaId("<user-id>"), "<access-token>"));
-}
-
-TEST_F(AssistantManagerServiceImplTest,
-       ShouldPassS3ServerUriOverrideToMojomService) {
-  CreateAssistantManagerServiceImpl("the-uri-override");
-
-  Start();
-  WaitForState(AssistantManagerService::STARTED);
-
-  EXPECT_EQ(mojom_service_controller()
-                .libassistant_config()
-                .s3_server_uri_override.value_or("<none>"),
-            "the-uri-override");
-}
-
-TEST_F(AssistantManagerServiceImplTest,
-       ShouldPassDeviceIdOverrideToMojomService) {
-  CreateAssistantManagerServiceImpl(
-      /*s3_server_uri_override=*/std::nullopt, "the-device-id-override");
-
-  Start();
-  WaitForState(AssistantManagerService::STARTED);
-
-  EXPECT_EQ(mojom_service_controller()
-                .libassistant_config()
-                .device_id_override.value_or("<none>"),
-            "the-device-id-override");
-}
-
-TEST_F(AssistantManagerServiceImplTest, ShouldPauseMediaManagerOnPause) {
-  StrictMock<LibassistantMediaControllerMock> mock;
-
-  StartAndWaitForRunning();
-
-  mock.Bind(mojom_libassistant_service().GetMediaControllerPendingReceiver());
-
-  EXPECT_CALL(mock, PauseInternalMediaPlayer);
-
-  assistant_manager_service()->UpdateInternalMediaPlayerStatus(
-      MediaSessionAction::kPause);
-  mock.FlushForTesting();
-}
-
-TEST_F(AssistantManagerServiceImplTest, ShouldResumeMediaManagerOnPlay) {
-  StrictMock<LibassistantMediaControllerMock> mock;
-
-  StartAndWaitForRunning();
-
-  mock.Bind(mojom_libassistant_service().GetMediaControllerPendingReceiver());
-
-  EXPECT_CALL(mock, ResumeInternalMediaPlayer);
-
-  assistant_manager_service()->UpdateInternalMediaPlayerStatus(
-      MediaSessionAction::kPlay);
-  mock.FlushForTesting();
-}
-
-TEST_F(AssistantManagerServiceImplTest, ShouldIgnoreOtherMediaManagerActions) {
-  StrictMock<LibassistantMediaControllerMock> mock;
-
-  const auto unsupported_media_session_actions = {
-      MediaSessionAction::kPreviousTrack, MediaSessionAction::kNextTrack,
-      MediaSessionAction::kSeekBackward,  MediaSessionAction::kSeekForward,
-      MediaSessionAction::kSkipAd,        MediaSessionAction::kStop,
-      MediaSessionAction::kSeekTo,        MediaSessionAction::kScrubTo,
-  };
-
-  StartAndWaitForRunning();
-
-  mock.Bind(mojom_libassistant_service().GetMediaControllerPendingReceiver());
-
-  for (auto action : unsupported_media_session_actions) {
-    // If this is not ignored, |mock| will complain about an uninterested call.
-    assistant_manager_service()->UpdateInternalMediaPlayerStatus(action);
-  }
-
-  mock.FlushForTesting();
-}
-
-TEST_F(AssistantManagerServiceImplTest,
-       ShouldNotCrashWhenMediaManagerIsAbsent) {
-  Start();
-  WaitForState(AssistantManagerService::STARTED);
-
-  assistant_manager_service()->UpdateInternalMediaPlayerStatus(
-      media_session::mojom::MediaSessionAction::kPlay);
-}
-
-TEST_F(AssistantManagerServiceImplTest, ShouldFireStateObserverWhenAddingIt) {
-  StrictMock<StateObserverMock> observer;
-  EXPECT_CALL(observer,
-              OnStateChanged(AssistantManagerService::State::STARTED));
-
-  Start();
-  WaitForState(AssistantManagerService::STARTED);
-
-  assistant_manager_service()->AddAndFireStateObserver(&observer);
-
-  assistant_manager_service()->RemoveStateObserver(&observer);
-}
-
-TEST_F(AssistantManagerServiceImplTest, ShouldFireStateObserverWhenStarting) {
-  StrictMock<StateObserverMock> observer;
-  AddStateObserver(&observer);
-
-  mojom_service_controller().BlockStartCalls();
-
-  EXPECT_CALL(observer,
-              OnStateChanged(AssistantManagerService::State::STARTING));
-  Start();
-
-  assistant_manager_service()->RemoveStateObserver(&observer);
-  mojom_service_controller().UnblockStartCalls();
-}
-
-TEST_F(AssistantManagerServiceImplTest, ShouldFireStateObserverWhenStarted) {
-  StrictMock<StateObserverMock> observer;
-  AddStateObserver(&observer);
-
-  EXPECT_CALL(observer,
-              OnStateChanged(AssistantManagerService::State::STARTING));
-  EXPECT_CALL(observer,
-              OnStateChanged(AssistantManagerService::State::STARTED));
-  Start();
-  WaitForState(AssistantManagerService::STARTED);
-
-  assistant_manager_service()->RemoveStateObserver(&observer);
-}
-
-TEST_F(AssistantManagerServiceImplTest,
-       ShouldFireStateObserverWhenLibAssistantServiceIsRunning) {
-  Start();
-  WaitForState(AssistantManagerService::STARTED);
-
-  StrictMock<StateObserverMock> observer;
-  AddStateObserver(&observer);
-  EXPECT_CALL(observer,
-              OnStateChanged(AssistantManagerService::State::RUNNING));
-
-  mojom_service_controller().SetState(ServiceState::kRunning);
-  WaitForState(AssistantManagerService::RUNNING);
-
-  assistant_manager_service()->RemoveStateObserver(&observer);
-}
-
-TEST_F(AssistantManagerServiceImplTest, ShouldFireStateObserverWhenStopping) {
-  Start();
-  WaitForState(AssistantManagerService::STARTED);
-
-  StrictMock<StateObserverMock> observer;
-  AddStateObserver(&observer);
-  EXPECT_CALL(observer,
-              OnStateChanged(AssistantManagerService::State::STOPPING));
-  EXPECT_CALL(observer,
-              OnStateChanged(AssistantManagerService::State::STOPPED));
-
-  assistant_manager_service()->Stop();
-  WaitForState(AssistantManagerService::STOPPING);
-  WaitForState(AssistantManagerService::STOPPED);
-
-  assistant_manager_service()->RemoveStateObserver(&observer);
-}
-
-TEST_F(AssistantManagerServiceImplTest,
-       ShouldNotFireStateObserverAfterItIsRemoved) {
-  StrictMock<StateObserverMock> observer;
-  AddStateObserver(&observer);
-
-  assistant_manager_service()->RemoveStateObserver(&observer);
-  EXPECT_CALL(observer, OnStateChanged).Times(0);
-
-  Start();
-}
-
-TEST_F(AssistantManagerServiceImplTest,
-       ShouldStartSpeakerIdEnrollmentWhenRequested) {
-  NiceMock<SpeakerIdEnrollmentClientMock> client_mock;
-  Start();
-  WaitForState(AssistantManagerService::STARTED);
-
-  StrictMock<SpeakerIdEnrollmentControllerMock> mojom_mock;
-  mojom_mock.Bind(mojom_libassistant_service());
-
-  EXPECT_CALL(mojom_mock, StartSpeakerIdEnrollment);
-
-  assistant_settings().StartSpeakerIdEnrollment(/*skip_cloud_enrollment=*/false,
-                                                client_mock.GetWeakPtr());
-
-  mojom_mock.FlushForTesting();
-}
-
-TEST_F(AssistantManagerServiceImplTest,
-       ShouldSendGaiaIdDuringSpeakerIdEnrollment) {
-  NiceMock<SpeakerIdEnrollmentClientMock> client_mock;
-  fake_service_context()->set_primary_account_gaia_id(GaiaId("gaia user id"));
-  Start();
-  WaitForState(AssistantManagerService::STARTED);
-
-  StrictMock<SpeakerIdEnrollmentControllerMock> mojom_mock;
-  mojom_mock.Bind(mojom_libassistant_service());
-
-  EXPECT_CALL(mojom_mock, StartSpeakerIdEnrollment("gaia user id", _, _));
-
-  assistant_settings().StartSpeakerIdEnrollment(/*skip_cloud_enrollment=*/false,
-                                                client_mock.GetWeakPtr());
-
-  mojom_mock.FlushForTesting();
-}
-
-TEST_F(AssistantManagerServiceImplTest,
-       ShouldSendSkipCloudEnrollmentDuringSpeakerIdEnrollment) {
-  Start();
-  WaitForState(AssistantManagerService::STARTED);
-
-  StrictMock<SpeakerIdEnrollmentControllerMock> mojom_mock;
-  mojom_mock.Bind(mojom_libassistant_service());
-
-  {
-    NiceMock<SpeakerIdEnrollmentClientMock> client_mock;
-
-    EXPECT_CALL(mojom_mock, StartSpeakerIdEnrollment(_, true, _));
-
-    assistant_settings().StartSpeakerIdEnrollment(
-        /*skip_cloud_enrollment=*/true, client_mock.GetWeakPtr());
-    mojom_mock.FlushForTesting();
-  }
-
-  {
-    NiceMock<SpeakerIdEnrollmentClientMock> client_mock;
-
-    EXPECT_CALL(mojom_mock, StartSpeakerIdEnrollment(_, false, _));
-
-    assistant_settings().StartSpeakerIdEnrollment(
-        /*skip_cloud_enrollment=*/false, client_mock.GetWeakPtr());
-    mojom_mock.FlushForTesting();
-  }
-}
-
-TEST_F(AssistantManagerServiceImplTest, ShouldSendStopSpeakerIdEnrollment) {
-  NiceMock<SpeakerIdEnrollmentClientMock> client_mock;
-  Start();
-  WaitForState(AssistantManagerService::STARTED);
-
-  StrictMock<SpeakerIdEnrollmentControllerMock> mojom_mock;
-  mojom_mock.Bind(mojom_libassistant_service());
-
-  EXPECT_CALL(mojom_mock, StopSpeakerIdEnrollment);
-
-  assistant_settings().StopSpeakerIdEnrollment();
-  mojom_mock.FlushForTesting();
-}
-
-TEST_F(AssistantManagerServiceImplTest, ShouldSyncSpeakerIdEnrollmentStatus) {
-  StrictMock<SpeakerIdEnrollmentClientMock> client_mock;
-  Start();
-  WaitForState(AssistantManagerService::STARTED);
-
-  StrictMock<SpeakerIdEnrollmentControllerMock> mojom_mock;
-  mojom_mock.Bind(mojom_libassistant_service());
-
-  EXPECT_CALL(mojom_mock, GetSpeakerIdEnrollmentStatus)
-      .WillOnce([](const std::string& user_gaia_id,
-                   SpeakerIdEnrollmentControllerMock::
-                       GetSpeakerIdEnrollmentStatusCallback callback) {
-        std::move(callback).Run(
-            SpeakerIdEnrollmentStatus::New(/*user_model_exists=*/true));
-      });
-
-  assistant_settings().SyncSpeakerIdEnrollmentStatus();
-  mojom_mock.FlushForTesting();
-}
-
-TEST_F(AssistantManagerServiceImplTest,
-       ShouldSyncSpeakerIdEnrollmentStatusWhenRunning) {
-  AssistantManagerServiceImpl::ResetIsFirstInitFlagForTesting();
-  StartAndWaitForRunning();
-
-  StrictMock<SpeakerIdEnrollmentClientMock> client_mock;
-  StrictMock<SpeakerIdEnrollmentControllerMock> mojom_mock;
-
-  mojom_mock.Bind(mojom_libassistant_service());
-
-  EXPECT_CALL(mojom_mock, GetSpeakerIdEnrollmentStatus)
-      .WillOnce([](const std::string& user_gaia_id,
-                   SpeakerIdEnrollmentControllerMock::
-                       GetSpeakerIdEnrollmentStatusCallback callback) {
-        std::move(callback).Run(
-            SpeakerIdEnrollmentStatus::New(/*user_model_exists=*/true));
-      });
-
-  mojom_mock.FlushForTesting();
-}
-
-// TODO(crbug.com/40902244): Re-enable this test
-TEST_F(AssistantManagerServiceImplTest, DISABLED_ShouldPropagateColorMode) {
-  ASSERT_FALSE(mojom_service_controller().dark_mode_enabled().has_value());
-
-  StartAndWaitForRunning();
-
-  ASSERT_TRUE(mojom_service_controller().dark_mode_enabled().has_value());
-  EXPECT_FALSE(mojom_service_controller().dark_mode_enabled().value());
-
-  assistant_manager_service()->OnColorModeChanged(true);
-  FlushForTesting();
-
-  ASSERT_TRUE(mojom_service_controller().dark_mode_enabled().has_value());
-  EXPECT_TRUE(mojom_service_controller().dark_mode_enabled().value());
-}
-
-TEST_F(AssistantManagerServiceImplTest, ShouldNotCrashRunningAfterStopped) {
-  Start();
-  SetAssistantStateContext(/*enabled=*/false);
-  WaitForState(AssistantManagerService::STARTED);
-
-  // http://crbug.com/1414264: calling Stop() before Running is set, should not
-  // crash.
-  assistant_manager_service()->Stop();
-  WaitForState(AssistantManagerService::STOPPING);
-
-  mojom_service_controller().SetState(ServiceState::kRunning);
-  WaitForState(AssistantManagerService::RUNNING);
-}
-
-}  // namespace ash::assistant
diff --git a/chromeos/ash/services/assistant/assistant_settings_impl.cc b/chromeos/ash/services/assistant/assistant_settings_impl.cc
deleted file mode 100644
index e1a25c28..0000000
--- a/chromeos/ash/services/assistant/assistant_settings_impl.cc
+++ /dev/null
@@ -1,162 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chromeos/ash/services/assistant/assistant_settings_impl.h"
-
-#include <utility>
-
-#include "ash/public/cpp/assistant/assistant_state_base.h"
-#include "ash/public/cpp/assistant/controller/assistant_controller.h"
-#include "base/functional/bind.h"
-#include "base/functional/callback_helpers.h"
-#include "chromeos/ash/services/assistant/public/cpp/features.h"
-#include "chromeos/ash/services/assistant/public/proto/settings_ui.pb.h"
-#include "chromeos/ash/services/assistant/service_context.h"
-#include "chromeos/ash/services/libassistant/public/mojom/settings_controller.mojom.h"
-#include "chromeos/ash/services/libassistant/public/mojom/speaker_id_enrollment_controller.mojom.h"
-#include "chromeos/version/version_loader.h"
-
-namespace ash::assistant {
-
-AssistantSettingsImpl::AssistantSettingsImpl(ServiceContext* context)
-    : context_(context) {}
-
-AssistantSettingsImpl::~AssistantSettingsImpl() = default;
-
-void AssistantSettingsImpl::Initialize(
-    mojo::PendingRemote<libassistant::mojom::SpeakerIdEnrollmentController>
-        remote,
-    libassistant::mojom::SettingsController* settings_controller) {
-  DCHECK(!settings_controller_);
-
-  speaker_id_enrollment_remote_.Bind(std::move(remote));
-  settings_controller_ = settings_controller;
-}
-
-void AssistantSettingsImpl::Stop() {
-  speaker_id_enrollment_remote_.reset();
-  settings_controller_ = nullptr;
-}
-
-void AssistantSettingsImpl::GetSettings(const std::string& selector,
-                                        GetSettingsCallback callback) {
-  settings_controller().GetSettings(selector, /*include_header=*/false,
-                                    std::move(callback));
-}
-
-void AssistantSettingsImpl::GetSettingsWithHeader(
-    const std::string& selector,
-    GetSettingsCallback callback) {
-  settings_controller().GetSettings(selector, /*include_header=*/true,
-                                    std::move(callback));
-}
-
-void AssistantSettingsImpl::UpdateSettings(const std::string& update,
-                                           GetSettingsCallback callback) {
-  settings_controller().UpdateSettings(update, std::move(callback));
-}
-
-void AssistantSettingsImpl::StartSpeakerIdEnrollment(
-    bool skip_cloud_enrollment,
-    base::WeakPtr<SpeakerIdEnrollmentClient> client) {
-  DCHECK(speaker_id_enrollment_remote_.is_bound());
-
-  speaker_id_enrollment_remote_->StartSpeakerIdEnrollment(
-      context_->primary_account_gaia_id().ToString(), skip_cloud_enrollment,
-      client->BindNewPipeAndPassRemote());
-}
-
-void AssistantSettingsImpl::StopSpeakerIdEnrollment() {
-  DCHECK(speaker_id_enrollment_remote_.is_bound());
-
-  speaker_id_enrollment_remote_->StopSpeakerIdEnrollment();
-}
-
-void AssistantSettingsImpl::SyncSpeakerIdEnrollmentStatus() {
-  if (assistant_state()->allowed_state() != AssistantAllowedState::ALLOWED ||
-      features::IsVoiceMatchDisabled()) {
-    return;
-  }
-
-  DCHECK(speaker_id_enrollment_remote_.is_bound());
-
-  speaker_id_enrollment_remote_->GetSpeakerIdEnrollmentStatus(
-      context_->primary_account_gaia_id().ToString(),
-      base::BindOnce(
-          &AssistantSettingsImpl::HandleSpeakerIdEnrollmentStatusSync,
-          weak_factory_.GetWeakPtr()));
-}
-
-void AssistantSettingsImpl::SyncDeviceAppsStatus(
-    base::OnceCallback<void(bool)> callback) {
-  SettingsUiSelector selector;
-  ConsentFlowUiSelector* consent_flow_ui =
-      selector.mutable_consent_flow_ui_selector();
-  consent_flow_ui->set_flow_id(
-      ActivityControlSettingsUiSelector::ASSISTANT_SUW_ONBOARDING_ON_CHROME_OS);
-  selector.set_gaia_user_context_ui(true);
-  GetSettings(selector.SerializeAsString(),
-              base::BindOnce(&AssistantSettingsImpl::HandleDeviceAppsStatusSync,
-                             weak_factory_.GetWeakPtr(), std::move(callback)));
-}
-
-void AssistantSettingsImpl::HandleSpeakerIdEnrollmentStatusSync(
-    libassistant::mojom::SpeakerIdEnrollmentStatusPtr status) {
-  if (!status->user_model_exists) {
-    // If hotword is enabled but there is no voice model found, launch the
-    // enrollment flow.
-    if (assistant_state()->hotword_enabled().value())
-      assistant_controller()->StartSpeakerIdEnrollmentFlow();
-  }
-}
-
-void AssistantSettingsImpl::HandleDeviceAppsStatusSync(
-    base::OnceCallback<void(bool)> callback,
-    const std::string& settings) {
-  if (settings.empty()) {
-    // Note: we deliberately do not log an error here, as this happens quite
-    // regularly when there is a network issue during signup. See b/151321970.
-    DVLOG(1) << "Assistant: Error while syncing device apps status.";
-    std::move(callback).Run(false);
-    return;
-  }
-
-  SettingsUi settings_ui;
-  if (!settings_ui.ParseFromString(settings)) {
-    LOG(ERROR) << "Failed to parse the response proto, set the DA bit to false";
-    std::move(callback).Run(false);
-    return;
-  }
-
-  if (!settings_ui.has_gaia_user_context_ui()) {
-    LOG(ERROR) << "Failed to get gaia user context, set the DA bit to false";
-    std::move(callback).Run(false);
-    return;
-  }
-
-  const auto& gaia_user_context_ui = settings_ui.gaia_user_context_ui();
-  if (!gaia_user_context_ui.has_device_apps_enabled()) {
-    LOG(ERROR) << "Failed to get the device apps bit, set it to false";
-    std::move(callback).Run(false);
-    return;
-  }
-
-  std::move(callback).Run(gaia_user_context_ui.device_apps_enabled());
-}
-
-AssistantStateBase* AssistantSettingsImpl::assistant_state() {
-  return context_->assistant_state();
-}
-
-AssistantController* AssistantSettingsImpl::assistant_controller() {
-  return context_->assistant_controller();
-}
-
-libassistant::mojom::SettingsController&
-AssistantSettingsImpl::settings_controller() {
-  DCHECK(settings_controller_);
-  return *settings_controller_;
-}
-
-}  // namespace ash::assistant
diff --git a/chromeos/ash/services/assistant/assistant_settings_impl.h b/chromeos/ash/services/assistant/assistant_settings_impl.h
deleted file mode 100644
index 4340e9a..0000000
--- a/chromeos/ash/services/assistant/assistant_settings_impl.h
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROMEOS_ASH_SERVICES_ASSISTANT_ASSISTANT_SETTINGS_IMPL_H_
-#define CHROMEOS_ASH_SERVICES_ASSISTANT_ASSISTANT_SETTINGS_IMPL_H_
-
-#include <memory>
-#include <string>
-
-#include "base/memory/raw_ptr.h"
-#include "chromeos/ash/services/assistant/public/cpp/assistant_settings.h"
-#include "chromeos/ash/services/libassistant/public/mojom/settings_controller.mojom-forward.h"
-#include "chromeos/ash/services/libassistant/public/mojom/speaker_id_enrollment_controller.mojom-forward.h"
-#include "mojo/public/cpp/bindings/pending_receiver.h"
-#include "mojo/public/cpp/bindings/pending_remote.h"
-#include "mojo/public/cpp/bindings/receiver_set.h"
-#include "mojo/public/cpp/bindings/remote.h"
-
-namespace ash {
-
-class AssistantController;
-class AssistantStateBase;
-
-namespace assistant {
-
-class ServiceContext;
-
-class AssistantSettingsImpl : public AssistantSettings {
- public:
-  explicit AssistantSettingsImpl(ServiceContext* context);
-
-  AssistantSettingsImpl(const AssistantSettingsImpl&) = delete;
-  AssistantSettingsImpl& operator=(const AssistantSettingsImpl&) = delete;
-
-  ~AssistantSettingsImpl() override;
-
-  void Initialize(
-      mojo::PendingRemote<libassistant::mojom::SpeakerIdEnrollmentController>
-          enrollment_controller_remote,
-      libassistant::mojom::SettingsController* settings_controller);
-  void Stop();
-
-  // AssistantSettings overrides:
-  void GetSettings(const std::string& selector,
-                   GetSettingsCallback callback) override;
-  void GetSettingsWithHeader(const std::string& selector,
-                             GetSettingsCallback callback) override;
-  void UpdateSettings(const std::string& update,
-                      UpdateSettingsCallback callback) override;
-  void StartSpeakerIdEnrollment(
-      bool skip_cloud_enrollment,
-      base::WeakPtr<SpeakerIdEnrollmentClient> client) override;
-  void StopSpeakerIdEnrollment() override;
-  void SyncSpeakerIdEnrollmentStatus() override;
-
-  void SyncDeviceAppsStatus(base::OnceCallback<void(bool)> callback);
-
- private:
-  void HandleSpeakerIdEnrollmentStatusSync(
-      libassistant::mojom::SpeakerIdEnrollmentStatusPtr status);
-  void HandleDeviceAppsStatusSync(base::OnceCallback<void(bool)> callback,
-                                  const std::string& settings);
-
-  AssistantStateBase* assistant_state();
-  AssistantController* assistant_controller();
-  libassistant::mojom::SettingsController& settings_controller();
-
-  const raw_ptr<ServiceContext> context_;
-  raw_ptr<libassistant::mojom::SettingsController, DanglingUntriaged>
-      settings_controller_ = nullptr;
-
-  mojo::Remote<libassistant::mojom::SpeakerIdEnrollmentController>
-      speaker_id_enrollment_remote_;
-
-  base::WeakPtrFactory<AssistantSettingsImpl> weak_factory_{this};
-};
-
-}  // namespace assistant
-}  // namespace ash
-
-#endif  // CHROMEOS_ASH_SERVICES_ASSISTANT_ASSISTANT_SETTINGS_IMPL_H_
diff --git a/chromeos/ash/services/assistant/audio_decoder/BUILD.gn b/chromeos/ash/services/assistant/audio_decoder/BUILD.gn
deleted file mode 100644
index 4f1e14d..0000000
--- a/chromeos/ash/services/assistant/audio_decoder/BUILD.gn
+++ /dev/null
@@ -1,29 +0,0 @@
-# Copyright 2018 The Chromium Authors
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//chromeos/ash/components/assistant/assistant.gni")
-
-assert(is_chromeos, "Non Chrome OS builds cannot depend on //chromeos/ash")
-assert(enable_cros_libassistant)
-
-source_set("lib") {
-  sources = [
-    "assistant_audio_decoder.cc",
-    "assistant_audio_decoder.h",
-    "assistant_audio_decoder_factory.cc",
-    "assistant_audio_decoder_factory.h",
-    "ipc_data_source.cc",
-    "ipc_data_source.h",
-  ]
-
-  deps = [
-    "//base",
-    "//chromeos/ash/services/assistant/public/mojom",
-  ]
-
-  public_deps = [
-    "//media",
-    "//mojo/public/cpp/bindings",
-  ]
-}
diff --git a/chromeos/ash/services/assistant/audio_decoder/DEPS b/chromeos/ash/services/assistant/audio_decoder/DEPS
deleted file mode 100644
index 386e0fef..0000000
--- a/chromeos/ash/services/assistant/audio_decoder/DEPS
+++ /dev/null
@@ -1,5 +0,0 @@
-include_rules = [
-  "+media/filters",
-  "+media/formats",
-  "+media/mojo",
-]
diff --git a/chromeos/ash/services/assistant/audio_decoder/README.md b/chromeos/ash/services/assistant/audio_decoder/README.md
deleted file mode 100644
index 3a4b337..0000000
--- a/chromeos/ash/services/assistant/audio_decoder/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-This directory contains audio decoder service for the Chrome OS native Assistant
-to decode the audio output by Libassistant, before connecting to AudioService.
-We cannot use the standard media service, which does not have the demuxer.
diff --git a/chromeos/ash/services/assistant/audio_decoder/assistant_audio_decoder.cc b/chromeos/ash/services/assistant/audio_decoder/assistant_audio_decoder.cc
deleted file mode 100644
index 3aad9b4..0000000
--- a/chromeos/ash/services/assistant/audio_decoder/assistant_audio_decoder.cc
+++ /dev/null
@@ -1,170 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// 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/374320451): Fix and remove.
-#pragma allow_unsafe_buffers
-#endif
-
-#include "chromeos/ash/services/assistant/audio_decoder/assistant_audio_decoder.h"
-
-#include <utility>
-#include <vector>
-
-#include "base/functional/bind.h"
-#include "base/task/sequenced_task_runner.h"
-#include "base/threading/thread.h"
-#include "chromeos/ash/services/assistant/audio_decoder/ipc_data_source.h"
-#include "media/base/audio_bus.h"
-#include "media/base/data_source.h"
-#include "media/filters/audio_file_reader.h"
-#include "media/filters/blocking_url_protocol.h"
-
-namespace ash::assistant {
-
-namespace {
-
-// Preferred bytes per sample when get interleaved data from AudioBus.
-constexpr int kBytesPerSample = 2;
-
-}  // namespace
-
-AssistantAudioDecoder::AssistantAudioDecoder(
-    mojo::PendingRemote<mojom::AssistantAudioDecoderClient> client,
-    mojo::PendingRemote<mojom::AssistantMediaDataSource> data_source)
-    : client_(std::move(client)),
-      task_runner_(base::SequencedTaskRunner::GetCurrentDefault()),
-      data_source_(std::make_unique<IPCDataSource>(std::move(data_source))),
-      media_thread_(std::make_unique<base::Thread>("media_thread")),
-      weak_factory_(this) {
-  weak_this_ = weak_factory_.GetWeakPtr();
-  CHECK(media_thread_->Start());
-  client_.set_disconnect_handler(base::BindOnce(
-      &AssistantAudioDecoder::OnConnectionError, base::Unretained(this)));
-}
-
-AssistantAudioDecoder::~AssistantAudioDecoder() = default;
-
-void AssistantAudioDecoder::Decode() {
-  media_thread_->task_runner()->PostTask(
-      FROM_HERE, base::BindOnce(&AssistantAudioDecoder::DecodeOnMediaThread,
-                                base::Unretained(this)));
-}
-
-void AssistantAudioDecoder::OpenDecoder(OpenDecoderCallback callback) {
-  DCHECK(!open_callback_);
-  open_callback_ = std::move(callback);
-  media_thread_->task_runner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&AssistantAudioDecoder::OpenDecoderOnMediaThread,
-                     base::Unretained(this)));
-}
-
-void AssistantAudioDecoder::CloseDecoder(CloseDecoderCallback callback) {
-  DCHECK(!close_callback_);
-  close_callback_ = std::move(callback);
-  media_thread_->task_runner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&AssistantAudioDecoder::CloseDecoderOnMediaThread,
-                     base::Unretained(this)));
-}
-
-void AssistantAudioDecoder::OnDataReadError() {
-  read_error_ = true;
-}
-
-void AssistantAudioDecoder::OpenDecoderOnMediaThread() {
-  protocol_ = std::make_unique<media::BlockingUrlProtocol>(
-      data_source_.get(),
-      base::BindRepeating(&AssistantAudioDecoder::OnDataReadError,
-                          base::Unretained(this)));
-  decoder_ = std::make_unique<media::AudioFileReader>(protocol_.get());
-
-  if (closed_ || !decoder_->Open() || read_error_) {
-    CloseDecoderOnMediaThread();
-    return;
-  }
-
-  task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(&AssistantAudioDecoder::OnDecoderInitializedOnThread,
-                     weak_this_, decoder_->sample_rate(),
-                     decoder_->channels()));
-}
-
-void AssistantAudioDecoder::DecodeOnMediaThread() {
-  std::vector<std::unique_ptr<media::AudioBus>> decoded_audio_packets;
-  // Experimental number of decoded packets before sending to |client_|.
-  constexpr int kPacketsToRead = 16;
-  DCHECK(decoder_);
-  // The client expects to be called |OnNewBuffers()| so that to return
-  // AudioDeviceOwner's |FillBuffer()| call. If |closed_| is true, still return
-  // empty |decoded_audio_packets| to indicate no more data available.
-  if (!closed_)
-    decoder_->Read(&decoded_audio_packets, kPacketsToRead);
-
-  task_runner_->PostTask(
-      FROM_HERE, base::BindOnce(&AssistantAudioDecoder::OnBufferDecodedOnThread,
-                                weak_this_, std::move(decoded_audio_packets)));
-}
-
-void AssistantAudioDecoder::CloseDecoderOnMediaThread() {
-  // |decoder_| may not be initialized.
-  if (decoder_)
-    decoder_->Close();
-
-  closed_ = true;
-  task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(&AssistantAudioDecoder::RunCallbacksAsClosed, weak_this_));
-}
-
-void AssistantAudioDecoder::OnDecoderInitializedOnThread(
-    int sample_rate,
-    int channels) {
-  DCHECK(open_callback_);
-  std::move(open_callback_)
-      .Run(/*success=*/true, kBytesPerSample, sample_rate, channels);
-}
-
-void AssistantAudioDecoder::OnBufferDecodedOnThread(
-    const std::vector<std::unique_ptr<media::AudioBus>>&
-        decoded_audio_packets) {
-  if (!client_)
-    return;
-
-  std::vector<std::vector<uint8_t>> buffers;
-  for (const auto& audio_bus : decoded_audio_packets) {
-    const int bytes_to_alloc =
-        audio_bus->frames() * kBytesPerSample * audio_bus->channels();
-    std::vector<uint8_t> buffer(bytes_to_alloc);
-    audio_bus->ToInterleaved<media::SignedInt16SampleTypeTraits>(
-        audio_bus->frames(), reinterpret_cast<int16_t*>(buffer.data()));
-    buffers.emplace_back(buffer);
-  }
-  client_->OnNewBuffers(buffers);
-}
-
-void AssistantAudioDecoder::OnConnectionError() {
-  client_.reset();
-  media_thread_->task_runner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&AssistantAudioDecoder::CloseDecoderOnMediaThread,
-                     base::Unretained(this)));
-}
-
-void AssistantAudioDecoder::RunCallbacksAsClosed() {
-  if (open_callback_) {
-    std::move(open_callback_)
-        .Run(/*success=*/false,
-             /*bytes_per_sample=*/0,
-             /*samples_per_second=*/0,
-             /*channels=*/0);
-  }
-
-  if (close_callback_)
-    std::move(close_callback_).Run();
-}
-
-}  // namespace ash::assistant
diff --git a/chromeos/ash/services/assistant/audio_decoder/assistant_audio_decoder.h b/chromeos/ash/services/assistant/audio_decoder/assistant_audio_decoder.h
deleted file mode 100644
index 8fbd0359..0000000
--- a/chromeos/ash/services/assistant/audio_decoder/assistant_audio_decoder.h
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROMEOS_ASH_SERVICES_ASSISTANT_AUDIO_DECODER_ASSISTANT_AUDIO_DECODER_H_
-#define CHROMEOS_ASH_SERVICES_ASSISTANT_AUDIO_DECODER_ASSISTANT_AUDIO_DECODER_H_
-
-#include <memory>
-
-#include "base/memory/weak_ptr.h"
-#include "base/task/sequenced_task_runner.h"
-#include "base/threading/thread.h"
-#include "chromeos/ash/services/assistant/public/mojom/assistant_audio_decoder.mojom.h"
-#include "media/filters/blocking_url_protocol.h"
-#include "mojo/public/cpp/bindings/pending_remote.h"
-#include "mojo/public/cpp/bindings/remote.h"
-
-namespace media {
-class AudioFileReader;
-class AudioBus;
-class DataSource;
-}  // namespace media
-
-namespace ash::assistant {
-
-class AssistantAudioDecoder : public mojom::AssistantAudioDecoder {
- public:
-  AssistantAudioDecoder(
-      mojo::PendingRemote<mojom::AssistantAudioDecoderClient> client,
-      mojo::PendingRemote<mojom::AssistantMediaDataSource> data_source);
-
-  AssistantAudioDecoder(const AssistantAudioDecoder&) = delete;
-  AssistantAudioDecoder& operator=(const AssistantAudioDecoder&) = delete;
-
-  ~AssistantAudioDecoder() override;
-
-  // Called by |client_| on main thread.
-  void OpenDecoder(OpenDecoderCallback callback) override;
-  void Decode() override;
-  void CloseDecoder(CloseDecoderCallback callback) override;
-
- private:
-  // Calls |decoder_| to decode on media thread.
-  void OpenDecoderOnMediaThread();
-  void DecodeOnMediaThread();
-  void CloseDecoderOnMediaThread();
-
-  // Calls |client_| methods on main thread.
-  void OnDecoderInitializedOnThread(int sample_rate, int channels);
-  void OnBufferDecodedOnThread(
-      const std::vector<std::unique_ptr<media::AudioBus>>&
-          decoded_audio_buffers);
-
-  // Error callback for media::BlockingUrlProtocol. Only run on media thread.
-  void OnDataReadError();
-
-  void OnConnectionError();
-  void RunCallbacksAsClosed();
-
-  mojo::Remote<mojom::AssistantAudioDecoderClient> client_;
-  scoped_refptr<base::SequencedTaskRunner> task_runner_;
-
-  OpenDecoderCallback open_callback_;
-  CloseDecoderCallback close_callback_;
-  bool closed_ = false;
-  bool read_error_ = false;
-
-  // Weak reference to |this| for use by the media thread. Note, ordering is
-  // important here. This _must_ appear before |media_thread_| so that the media
-  // thread is destroyed (and joined) first, and hence any attempt to copy
-  // |weak_this_| happens before it is destroyed.
-  base::WeakPtr<AssistantAudioDecoder> weak_this_;
-
-  std::unique_ptr<media::DataSource> data_source_;
-  std::unique_ptr<media::BlockingUrlProtocol> protocol_;
-  std::unique_ptr<media::AudioFileReader> decoder_;
-  std::unique_ptr<base::Thread> media_thread_;
-
-  base::WeakPtrFactory<AssistantAudioDecoder> weak_factory_;
-};
-
-}  // namespace ash::assistant
-
-#endif  // CHROMEOS_ASH_SERVICES_ASSISTANT_AUDIO_DECODER_ASSISTANT_AUDIO_DECODER_H_
diff --git a/chromeos/ash/services/assistant/audio_decoder/assistant_audio_decoder_factory.cc b/chromeos/ash/services/assistant/audio_decoder/assistant_audio_decoder_factory.cc
deleted file mode 100644
index 21f7591..0000000
--- a/chromeos/ash/services/assistant/audio_decoder/assistant_audio_decoder_factory.cc
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chromeos/ash/services/assistant/audio_decoder/assistant_audio_decoder_factory.h"
-
-#include "chromeos/ash/services/assistant/audio_decoder/assistant_audio_decoder.h"
-#include "mojo/public/cpp/bindings/self_owned_receiver.h"
-
-namespace ash::assistant {
-
-AssistantAudioDecoderFactory::AssistantAudioDecoderFactory(
-    mojo::PendingReceiver<mojom::AssistantAudioDecoderFactory> receiver)
-    : receiver_(this, std::move(receiver)) {}
-
-AssistantAudioDecoderFactory::~AssistantAudioDecoderFactory() = default;
-
-void AssistantAudioDecoderFactory::CreateAssistantAudioDecoder(
-    mojo::PendingReceiver<mojom::AssistantAudioDecoder> receiver,
-    mojo::PendingRemote<mojom::AssistantAudioDecoderClient> client,
-    mojo::PendingRemote<mojom::AssistantMediaDataSource> data_source) {
-  mojo::MakeSelfOwnedReceiver(std::make_unique<AssistantAudioDecoder>(
-                                  std::move(client), std::move(data_source)),
-                              std::move(receiver));
-}
-
-}  // namespace ash::assistant
diff --git a/chromeos/ash/services/assistant/audio_decoder/assistant_audio_decoder_factory.h b/chromeos/ash/services/assistant/audio_decoder/assistant_audio_decoder_factory.h
deleted file mode 100644
index 85b48ab6..0000000
--- a/chromeos/ash/services/assistant/audio_decoder/assistant_audio_decoder_factory.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROMEOS_ASH_SERVICES_ASSISTANT_AUDIO_DECODER_ASSISTANT_AUDIO_DECODER_FACTORY_H_
-#define CHROMEOS_ASH_SERVICES_ASSISTANT_AUDIO_DECODER_ASSISTANT_AUDIO_DECODER_FACTORY_H_
-
-#include "chromeos/ash/services/assistant/public/mojom/assistant_audio_decoder.mojom.h"
-#include "mojo/public/cpp/bindings/pending_receiver.h"
-#include "mojo/public/cpp/bindings/pending_remote.h"
-#include "mojo/public/cpp/bindings/receiver.h"
-
-namespace ash::assistant {
-
-class AssistantAudioDecoderFactory
-    : public mojom::AssistantAudioDecoderFactory {
- public:
-  explicit AssistantAudioDecoderFactory(
-      mojo::PendingReceiver<mojom::AssistantAudioDecoderFactory> receiver);
-
-  AssistantAudioDecoderFactory(const AssistantAudioDecoderFactory&) = delete;
-  AssistantAudioDecoderFactory& operator=(const AssistantAudioDecoderFactory&) =
-      delete;
-
-  ~AssistantAudioDecoderFactory() override;
-
- private:
-  // mojom::AssistantAudioDecoderFactory:
-  void CreateAssistantAudioDecoder(
-      mojo::PendingReceiver<mojom::AssistantAudioDecoder> receiver,
-      mojo::PendingRemote<mojom::AssistantAudioDecoderClient> client,
-      mojo::PendingRemote<mojom::AssistantMediaDataSource> data_source)
-      override;
-
-  mojo::Receiver<mojom::AssistantAudioDecoderFactory> receiver_;
-};
-
-}  // namespace ash::assistant
-
-#endif  // CHROMEOS_ASH_SERVICES_ASSISTANT_AUDIO_DECODER_ASSISTANT_AUDIO_DECODER_FACTORY_H_
diff --git a/chromeos/ash/services/assistant/audio_decoder/ipc_data_source.cc b/chromeos/ash/services/assistant/audio_decoder/ipc_data_source.cc
deleted file mode 100644
index a85f491..0000000
--- a/chromeos/ash/services/assistant/audio_decoder/ipc_data_source.cc
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chromeos/ash/services/assistant/audio_decoder/ipc_data_source.h"
-
-#include <algorithm>
-#include <utility>
-#include <vector>
-
-#include "base/functional/bind.h"
-#include "base/task/sequenced_task_runner.h"
-#include "mojo/public/cpp/bindings/message.h"
-
-namespace ash::assistant {
-
-IPCDataSource::IPCDataSource(
-    mojo::PendingRemote<mojom::AssistantMediaDataSource> media_data_source)
-    : media_data_source_(std::move(media_data_source)),
-      utility_task_runner_(base::SequencedTaskRunner::GetCurrentDefault()) {
-  DETACH_FROM_THREAD(data_source_thread_checker_);
-}
-
-IPCDataSource::~IPCDataSource() {
-  DCHECK_CALLED_ON_VALID_THREAD(utility_thread_checker_);
-}
-
-void IPCDataSource::Stop() {
-  DCHECK_CALLED_ON_VALID_THREAD(data_source_thread_checker_);
-}
-
-void IPCDataSource::Abort() {
-  DCHECK_CALLED_ON_VALID_THREAD(data_source_thread_checker_);
-}
-
-void IPCDataSource::Read(int64_t position,
-                         int size,
-                         uint8_t* destination,
-                         DataSource::ReadCB callback) {
-  DCHECK_CALLED_ON_VALID_THREAD(data_source_thread_checker_);
-
-  utility_task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(&IPCDataSource::ReadMediaData, base::Unretained(this),
-                     destination, std::move(callback), size));
-}
-
-bool IPCDataSource::GetSize(int64_t* size_out) {
-  DCHECK_CALLED_ON_VALID_THREAD(data_source_thread_checker_);
-  *size_out = 0;
-  return false;
-}
-
-bool IPCDataSource::IsStreaming() {
-  DCHECK_CALLED_ON_VALID_THREAD(data_source_thread_checker_);
-  return true;
-}
-
-void IPCDataSource::SetBitrate(int bitrate) {
-  DCHECK_CALLED_ON_VALID_THREAD(data_source_thread_checker_);
-}
-
-bool IPCDataSource::PassedTimingAllowOriginCheck() {
-  // The mojo ipc channel doesn't support this yet, so cautiously return false,
-  // for now.
-  // TODO(crbug.com/40243452): Rework this method to be asynchronous, if
-  // possible, so that the mojo interface can be queried.
-  return false;
-}
-
-bool IPCDataSource::WouldTaintOrigin() {
-  // The mojo ipc channel doesn't support this yet, so cautiously return true,
-  // for now.
-  // TODO(crbug.com/40243452): Rework this method to be asynchronous, if
-  // possible, so that the mojo interface can be queried.
-  return true;
-}
-
-void IPCDataSource::ReadMediaData(uint8_t* destination,
-                                  DataSource::ReadCB callback,
-                                  int size) {
-  DCHECK_CALLED_ON_VALID_THREAD(utility_thread_checker_);
-  CHECK_GE(size, 0);
-
-  media_data_source_->Read(
-      size, base::BindOnce(&IPCDataSource::ReadDone, base::Unretained(this),
-                           destination, std::move(callback), size));
-}
-
-void IPCDataSource::ReadDone(uint8_t* destination,
-                             DataSource::ReadCB callback,
-                             uint32_t requested_size,
-                             const std::vector<uint8_t>& data) {
-  DCHECK_CALLED_ON_VALID_THREAD(utility_thread_checker_);
-  if (data.size() > requested_size) {
-    mojo::ReportBadMessage("IPCDataSource::ReadDone: Unexpected data size.");
-    std::move(callback).Run(0);
-    return;
-  }
-
-  std::ranges::copy(data, destination);
-  std::move(callback).Run(data.size());
-}
-
-}  // namespace ash::assistant
diff --git a/chromeos/ash/services/assistant/audio_decoder/ipc_data_source.h b/chromeos/ash/services/assistant/audio_decoder/ipc_data_source.h
deleted file mode 100644
index 360f445..0000000
--- a/chromeos/ash/services/assistant/audio_decoder/ipc_data_source.h
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROMEOS_ASH_SERVICES_ASSISTANT_AUDIO_DECODER_IPC_DATA_SOURCE_H_
-#define CHROMEOS_ASH_SERVICES_ASSISTANT_AUDIO_DECODER_IPC_DATA_SOURCE_H_
-
-#include <stdint.h>
-
-#include "base/task/sequenced_task_runner.h"
-#include "base/threading/thread_checker.h"
-#include "chromeos/ash/services/assistant/public/mojom/assistant_audio_decoder.mojom.h"
-#include "media/base/data_source.h"
-#include "mojo/public/cpp/bindings/pending_remote.h"
-#include "mojo/public/cpp/bindings/remote.h"
-
-namespace ash::assistant {
-
-// Provides data source to the audio stream decoder. Class must be created and
-// destroyed on a same thread. The thread must not be blocked for read
-// operations to succeed.
-class IPCDataSource : public media::DataSource {
- public:
-  // May only be called on the utility thread.
-  explicit IPCDataSource(
-      mojo::PendingRemote<mojom::AssistantMediaDataSource> media_data_source);
-
-  IPCDataSource(const IPCDataSource&) = delete;
-  IPCDataSource& operator=(const IPCDataSource&) = delete;
-
-  ~IPCDataSource() override;
-
-  // media::DataSource implementation. The methods may be called on any single
-  // thread. First usage of these methods attaches a thread checker.
-  void Stop() override;
-  void Abort() override;
-  void Read(int64_t position,
-            int size,
-            uint8_t* destination,
-            ReadCB callback) override;
-  bool GetSize(int64_t* size_out) override;
-  bool IsStreaming() override;
-  void SetBitrate(int bitrate) override;
-  bool PassedTimingAllowOriginCheck() override;
-  bool WouldTaintOrigin() override;
-
- private:
-  // Media data read helpers: must be run on the utility thread.
-  void ReadMediaData(uint8_t* destination, ReadCB callback, int size);
-  void ReadDone(uint8_t* destination,
-                ReadCB callback,
-                uint32_t requested_size,
-                const std::vector<uint8_t>& data);
-
-  mojo::Remote<mojom::AssistantMediaDataSource> media_data_source_;
-
-  scoped_refptr<base::SequencedTaskRunner> utility_task_runner_;
-
-  THREAD_CHECKER(utility_thread_checker_);
-
-  // Enforces that the DataSource methods are called on one other thread only.
-  THREAD_CHECKER(data_source_thread_checker_);
-};
-
-}  // namespace ash::assistant
-
-#endif  // CHROMEOS_ASH_SERVICES_ASSISTANT_AUDIO_DECODER_IPC_DATA_SOURCE_H_
diff --git a/chromeos/ash/services/assistant/device_settings_host.cc b/chromeos/ash/services/assistant/device_settings_host.cc
deleted file mode 100644
index d227ce3..0000000
--- a/chromeos/ash/services/assistant/device_settings_host.cc
+++ /dev/null
@@ -1,98 +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.
-
-#include "chromeos/ash/services/assistant/device_settings_host.h"
-
-#include "ash/public/cpp/assistant/controller/assistant_notification_controller.h"
-#include "chromeos/ash/services/assistant/public/cpp/device_actions.h"
-#include "chromeos/ash/services/assistant/service_context.h"
-
-namespace ash::assistant {
-
-namespace {
-
-using libassistant::mojom::GetBrightnessResult;
-using GetScreenBrightnessLevelCallback = libassistant::mojom::
-    DeviceSettingsDelegate::GetScreenBrightnessLevelCallback;
-
-void HandleScreenBrightnessCallback(GetScreenBrightnessLevelCallback callback,
-                                    bool success,
-                                    double level) {
-  if (success) {
-    std::move(callback).Run(GetBrightnessResult::New(level));
-  } else {
-    std::move(callback).Run(nullptr);
-  }
-}
-
-}  // namespace
-
-DeviceSettingsHost::DeviceSettingsHost(ServiceContext* context)
-    : context_(*context) {}
-
-DeviceSettingsHost::~DeviceSettingsHost() = default;
-
-void DeviceSettingsHost::Bind(
-    mojo::PendingReceiver<DeviceSettingsDelegate> pending_receiver) {
-  receiver_.Bind(std::move(pending_receiver));
-}
-
-void DeviceSettingsHost::Stop() {
-  receiver_.reset();
-}
-
-void DeviceSettingsHost::GetScreenBrightnessLevel(
-    GetScreenBrightnessLevelCallback callback) {
-  device_actions().GetScreenBrightnessLevel(
-      base::BindOnce(&HandleScreenBrightnessCallback, std::move(callback)));
-}
-
-void DeviceSettingsHost::SetBluetoothEnabled(bool enabled) {
-  has_setting_changed_ = true;
-  device_actions().SetBluetoothEnabled(enabled);
-}
-
-void DeviceSettingsHost::SetDoNotDisturbEnabled(bool enabled) {
-  has_setting_changed_ = true;
-  assistant_notification_controller().SetQuietMode(enabled);
-}
-
-void DeviceSettingsHost::SetNightLightEnabled(bool enabled) {
-  has_setting_changed_ = true;
-  device_actions().SetNightLightEnabled(enabled);
-}
-
-void DeviceSettingsHost::SetScreenBrightnessLevel(double level, bool gradual) {
-  has_setting_changed_ = true;
-  device_actions().SetScreenBrightnessLevel(level, gradual);
-}
-
-void DeviceSettingsHost::SetSwitchAccessEnabled(bool enabled) {
-  has_setting_changed_ = true;
-  device_actions().SetSwitchAccessEnabled(enabled);
-}
-
-void DeviceSettingsHost::SetWifiEnabled(bool enabled) {
-  has_setting_changed_ = true;
-  device_actions().SetWifiEnabled(enabled);
-}
-
-void DeviceSettingsHost::reset_has_setting_changed() {
-  has_setting_changed_ = false;
-}
-
-DeviceActions& DeviceSettingsHost::device_actions() {
-  auto* result = context_->device_actions();
-  DCHECK(result);
-  return *result;
-}
-
-AssistantNotificationController&
-DeviceSettingsHost::assistant_notification_controller() {
-  auto* result = context_->assistant_notification_controller();
-  DCHECK(result);
-  return *result;
-}
-
-}  // namespace ash::assistant
diff --git a/chromeos/ash/services/assistant/device_settings_host.h b/chromeos/ash/services/assistant/device_settings_host.h
deleted file mode 100644
index a42e7ec..0000000
--- a/chromeos/ash/services/assistant/device_settings_host.h
+++ /dev/null
@@ -1,61 +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.
-
-#ifndef CHROMEOS_ASH_SERVICES_ASSISTANT_DEVICE_SETTINGS_HOST_H_
-#define CHROMEOS_ASH_SERVICES_ASSISTANT_DEVICE_SETTINGS_HOST_H_
-
-#include "base/component_export.h"
-#include "base/memory/raw_ref.h"
-#include "chromeos/ash/services/libassistant/public/mojom/device_settings_delegate.mojom.h"
-#include "mojo/public/cpp/bindings/pending_receiver.h"
-#include "mojo/public/cpp/bindings/receiver.h"
-
-namespace ash {
-
-class AssistantNotificationController;
-
-namespace assistant {
-
-class DeviceActions;
-class ServiceContext;
-
-class COMPONENT_EXPORT(ASSISTANT_SERVICE) DeviceSettingsHost
-    : public libassistant::mojom::DeviceSettingsDelegate {
- public:
-  explicit DeviceSettingsHost(ServiceContext* context);
-  DeviceSettingsHost(const DeviceSettingsHost&) = delete;
-  DeviceSettingsHost& operator=(const DeviceSettingsHost&) = delete;
-  ~DeviceSettingsHost() override;
-
-  void Bind(mojo::PendingReceiver<DeviceSettingsDelegate> pending_receiver);
-  void Stop();
-
-  // libassistant::mojom::DeviceSettingsDelegate implementation:
-  void GetScreenBrightnessLevel(
-      GetScreenBrightnessLevelCallback callback) override;
-  void SetBluetoothEnabled(bool enabled) override;
-  void SetDoNotDisturbEnabled(bool enabled) override;
-  void SetNightLightEnabled(bool enabled) override;
-  void SetScreenBrightnessLevel(double level, bool gradual) override;
-  void SetSwitchAccessEnabled(bool enabled) override;
-  void SetWifiEnabled(bool enabled) override;
-
-  // Return if any setting has been modified.
-  bool has_setting_changed() const { return has_setting_changed_; }
-  void reset_has_setting_changed();
-
- private:
-  const raw_ref<ServiceContext> context_;
-
-  DeviceActions& device_actions();
-  AssistantNotificationController& assistant_notification_controller();
-
-  bool has_setting_changed_ = false;
-
-  mojo::Receiver<DeviceSettingsDelegate> receiver_{this};
-};
-}  // namespace assistant
-}  // namespace ash
-
-#endif  // CHROMEOS_ASH_SERVICES_ASSISTANT_DEVICE_SETTINGS_HOST_H_
diff --git a/chromeos/ash/services/assistant/libassistant_loader_stub.cc b/chromeos/ash/services/assistant/libassistant_loader_stub.cc
deleted file mode 100644
index e2384ef..0000000
--- a/chromeos/ash/services/assistant/libassistant_loader_stub.cc
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chromeos/ash/services/libassistant/public/cpp/libassistant_loader.h"
-
-namespace ash::libassistant {
-
-// static
-void LibassistantLoader::Load(LoadCallback callback) {
-  std::move(callback).Run(/*success=*/true);
-}
-
-}  // namespace ash::libassistant
diff --git a/chromeos/ash/services/assistant/libassistant_service_host.h b/chromeos/ash/services/assistant/libassistant_service_host.h
deleted file mode 100644
index 56c6c5c..0000000
--- a/chromeos/ash/services/assistant/libassistant_service_host.h
+++ /dev/null
@@ -1,34 +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.
-
-#ifndef CHROMEOS_ASH_SERVICES_ASSISTANT_LIBASSISTANT_SERVICE_HOST_H_
-#define CHROMEOS_ASH_SERVICES_ASSISTANT_LIBASSISTANT_SERVICE_HOST_H_
-
-#include "chromeos/ash/services/libassistant/public/mojom/service.mojom-forward.h"
-#include "mojo/public/cpp/bindings/pending_receiver.h"
-
-namespace ash::assistant {
-
-// Interface which can be implemented to control launching and the lifetime of
-// the Libassistant service. The API is losely inspired by
-// ServiceProcessHost::Launch(), to make it easier to migrate to a real mojom
-// service running in its own process.
-class LibassistantServiceHost {
- public:
-  virtual ~LibassistantServiceHost() = default;
-
-  // Launch the mojom service. Barring crashes, the service will remain running
-  // as long as both the receiver and this host class remain alive, or until
-  // |Stop| is called.
-  virtual void Launch(
-      mojo::PendingReceiver<libassistant::mojom::LibassistantService>
-          receiver) = 0;
-
-  // Stop the mojom service.
-  virtual void Stop() = 0;
-};
-
-}  // namespace ash::assistant
-
-#endif  // CHROMEOS_ASH_SERVICES_ASSISTANT_LIBASSISTANT_SERVICE_HOST_H_
diff --git a/chromeos/ash/services/assistant/libassistant_service_host_impl.cc b/chromeos/ash/services/assistant/libassistant_service_host_impl.cc
deleted file mode 100644
index 06d92bd4..0000000
--- a/chromeos/ash/services/assistant/libassistant_service_host_impl.cc
+++ /dev/null
@@ -1,59 +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.
-
-#include "chromeos/ash/services/assistant/libassistant_service_host_impl.h"
-
-#include "base/check.h"
-#include "base/sequence_checker.h"
-#include "build/buildflag.h"
-#include "chromeos/ash/components/assistant/buildflags.h"
-
-#if BUILDFLAG(ENABLE_CROS_LIBASSISTANT)
-#include "chromeos/ash/services/assistant/public/cpp/assistant_browser_delegate.h"
-#include "chromeos/ash/services/assistant/public/cpp/features.h"
-#include "chromeos/ash/services/libassistant/libassistant_service.h"
-#include "chromeos/ash/services/libassistant/public/mojom/service.mojom-forward.h"
-#endif  // BUILDFLAG(ENABLE_CROS_LIBASSISTANT)
-
-namespace ash::assistant {
-
-#if BUILDFLAG(ENABLE_CROS_LIBASSISTANT)
-
-LibassistantServiceHostImpl::LibassistantServiceHostImpl() {
-  DETACH_FROM_SEQUENCE(sequence_checker_);
-}
-
-LibassistantServiceHostImpl::~LibassistantServiceHostImpl() = default;
-
-void LibassistantServiceHostImpl::Launch(
-    mojo::PendingReceiver<libassistant::mojom::LibassistantService> receiver) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (assistant::features::IsLibAssistantSandboxEnabled()) {
-    AssistantBrowserDelegate::Get()->RequestLibassistantService(
-        std::move(receiver));
-  } else {
-    DCHECK(!libassistant_service_);
-    libassistant_service_ = std::make_unique<libassistant::LibassistantService>(
-        std::move(receiver));
-  }
-}
-
-void LibassistantServiceHostImpl::Stop() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  libassistant_service_ = nullptr;
-}
-
-#else
-
-LibassistantServiceHostImpl::LibassistantServiceHostImpl() = default;
-LibassistantServiceHostImpl::~LibassistantServiceHostImpl() = default;
-
-void LibassistantServiceHostImpl::Launch(
-    mojo::PendingReceiver<libassistant::mojom::LibassistantService> receiver) {}
-
-void LibassistantServiceHostImpl::Stop() {}
-
-#endif
-
-}  // namespace ash::assistant
diff --git a/chromeos/ash/services/assistant/libassistant_service_host_impl.h b/chromeos/ash/services/assistant/libassistant_service_host_impl.h
deleted file mode 100644
index e37e6ae..0000000
--- a/chromeos/ash/services/assistant/libassistant_service_host_impl.h
+++ /dev/null
@@ -1,48 +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.
-
-#ifndef CHROMEOS_ASH_SERVICES_ASSISTANT_LIBASSISTANT_SERVICE_HOST_IMPL_H_
-#define CHROMEOS_ASH_SERVICES_ASSISTANT_LIBASSISTANT_SERVICE_HOST_IMPL_H_
-
-#include <memory>
-
-#include "base/thread_annotations.h"
-#include "build/buildflag.h"
-#include "chromeos/ash/components/assistant/buildflags.h"
-#include "chromeos/ash/services/assistant/libassistant_service_host.h"
-
-namespace ash {
-
-namespace libassistant {
-class LibassistantService;
-}
-
-namespace assistant {
-
-// Host class controlling the lifetime of the Libassistant service.
-// The implementation will be stubbed out in the unbranded build.
-class LibassistantServiceHostImpl : public LibassistantServiceHost {
- public:
-  LibassistantServiceHostImpl();
-  LibassistantServiceHostImpl(LibassistantServiceHostImpl&) = delete;
-  LibassistantServiceHostImpl& operator=(LibassistantServiceHostImpl&) = delete;
-  ~LibassistantServiceHostImpl() override;
-
-  // LibassistantServiceHost implementation:
-  void Launch(mojo::PendingReceiver<libassistant::mojom::LibassistantService>
-                  receiver) override;
-  void Stop() override;
-
- private:
-#if BUILDFLAG(ENABLE_CROS_LIBASSISTANT)
-  SEQUENCE_CHECKER(sequence_checker_);
-  std::unique_ptr<libassistant::LibassistantService> libassistant_service_
-      GUARDED_BY_CONTEXT(sequence_checker_);
-#endif
-};
-
-}  // namespace assistant
-}  // namespace ash
-
-#endif  // CHROMEOS_ASH_SERVICES_ASSISTANT_LIBASSISTANT_SERVICE_HOST_IMPL_H_
diff --git a/chromeos/ash/services/assistant/media_host.cc b/chromeos/ash/services/assistant/media_host.cc
deleted file mode 100644
index d7fd220..0000000
--- a/chromeos/ash/services/assistant/media_host.cc
+++ /dev/null
@@ -1,297 +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.
-
-#include "chromeos/ash/services/assistant/media_host.h"
-
-#include "base/memory/raw_ptr.h"
-#include "base/strings/utf_string_conversions.h"
-#include "chromeos/ash/services/assistant/media_session/assistant_media_session.h"
-#include "chromeos/ash/services/assistant/public/cpp/assistant_browser_delegate.h"
-#include "chromeos/ash/services/assistant/public/cpp/assistant_service.h"
-#include "chromeos/ash/services/assistant/public/cpp/features.h"
-#include "chromeos/ash/services/libassistant/public/mojom/media_controller.mojom.h"
-#include "chromeos/services/assistant/public/shared/utils.h"
-
-namespace ash::assistant {
-
-namespace {
-using libassistant::mojom::PlaybackState;
-using media_session::mojom::MediaSessionAction;
-using media_session::mojom::MediaSessionInfo;
-using media_session::mojom::MediaSessionInfoPtr;
-
-constexpr char kIntentActionView[] = "android.intent.action.VIEW";
-
-}  // namespace
-
-////////////////////////////////////////////////////////////////////////////////
-//   MediaHost::ChromeosMediaStateObserver
-////////////////////////////////////////////////////////////////////////////////
-
-// Helper class that will observe media changes on ChromeOS and sync them to
-// |MediaHost::UpdateMediaState| (which will sync them to
-// Libassistant).
-class MediaHost::ChromeosMediaStateObserver
-    : private media_session::mojom::MediaControllerObserver {
- public:
-  explicit ChromeosMediaStateObserver(MediaHost* parent) : parent_(parent) {
-    DCHECK(parent_);
-  }
-  ChromeosMediaStateObserver(const ChromeosMediaStateObserver&) = delete;
-  ChromeosMediaStateObserver& operator=(const ChromeosMediaStateObserver&) =
-      delete;
-  ~ChromeosMediaStateObserver() override = default;
-
-  mojo::PendingRemote<MediaControllerObserver> BindNewPipeAndPassRemote() {
-    return receiver_.BindNewPipeAndPassRemote();
-  }
-
- private:
-  // media_session::mojom::MediaControllerObserver overrides:
-  void MediaSessionInfoChanged(MediaSessionInfoPtr info) override {
-    media_session_info_ptr_ = std::move(info);
-    UpdateMediaState();
-  }
-  void MediaSessionMetadataChanged(
-      const std::optional<media_session::MediaMetadata>& metadata) override {
-    media_metadata_ = std::move(metadata);
-    UpdateMediaState();
-  }
-  void MediaSessionActionsChanged(
-      const std::vector<MediaSessionAction>& action) override {}
-  void MediaSessionChanged(
-      const std::optional<base::UnguessableToken>& request_id) override {
-    if (request_id.has_value())
-      media_session_audio_focus_id_ = std::move(request_id.value());
-  }
-  void MediaSessionPositionChanged(
-      const std::optional<media_session::MediaPosition>& position) override {}
-
-  void UpdateMediaState() {
-    if (media_session_info_ptr_) {
-      if (media_session_info_ptr_->state ==
-              MediaSessionInfo::SessionState::kSuspended &&
-          media_session_info_ptr_->playback_state ==
-              media_session::mojom::MediaPlaybackState::kPlaying) {
-        // It is an intermediate state caused by some providers override the
-        // playback state. We considered it as invalid and skip reporting the
-        // state.
-        return;
-      }
-    }
-
-    libassistant::mojom::MediaStatePtr media_state =
-        libassistant::mojom::MediaState::New();
-    media_state->metadata = libassistant::mojom::MediaMetadata::New();
-
-    // Set media metadata.
-    if (media_metadata_.has_value()) {
-      media_state->metadata->title =
-          base::UTF16ToUTF8(media_metadata_.value().title);
-    }
-
-    // Set playback state.
-    media_state->playback_state = PlaybackState::kIdle;
-    if (media_session_info_ptr_ &&
-        media_session_info_ptr_->state !=
-            MediaSessionInfo::SessionState::kInactive) {
-      switch (media_session_info_ptr_->playback_state) {
-        case media_session::mojom::MediaPlaybackState::kPlaying:
-          media_state->playback_state = PlaybackState::kPlaying;
-          break;
-        case media_session::mojom::MediaPlaybackState::kPaused:
-          media_state->playback_state = PlaybackState::kPaused;
-          break;
-      }
-    }
-
-    parent_->UpdateMediaState(media_session_audio_focus_id_,
-                              std::move(media_state));
-  }
-
-  const raw_ptr<MediaHost> parent_;
-  mojo::Receiver<media_session::mojom::MediaControllerObserver> receiver_{this};
-
-  // Info associated to the active media session.
-  MediaSessionInfoPtr media_session_info_ptr_;
-  // The metadata for the active media session. It can be null to be reset,
-  // e.g. the media that was being played has been stopped.
-  std::optional<media_session::MediaMetadata> media_metadata_ = std::nullopt;
-
-  base::UnguessableToken media_session_audio_focus_id_ =
-      base::UnguessableToken::Null();
-};
-
-////////////////////////////////////////////////////////////////////////////////
-//   MediaHost::LibassistantMediaStateObserver
-////////////////////////////////////////////////////////////////////////////////
-
-// Helper class that will observe media changes in Libassisstant and sync them
-// to either |MediaHost::interaction_subscribers_|,
-// |MediaHost::chromeos_media_controller_| or
-// |MediaHost::media_session_|.
-class MediaHost::LibassistantMediaDelegate
-    : public libassistant::mojom::MediaDelegate {
- public:
-  explicit LibassistantMediaDelegate(
-      MediaHost* parent,
-      mojo::PendingReceiver<MediaDelegate> pending_receiver)
-      : parent_(parent), receiver_(this, std::move(pending_receiver)) {}
-
-  LibassistantMediaDelegate(const LibassistantMediaDelegate&) = delete;
-  LibassistantMediaDelegate& operator=(const LibassistantMediaDelegate&) =
-      delete;
-  ~LibassistantMediaDelegate() override = default;
-
- private:
-  // libassistant::mojom::MediaDelegate implementation:
-  void OnPlaybackStateChanged(
-      libassistant::mojom::MediaStatePtr new_state) override {
-    parent_->media_session_->NotifyMediaSessionMetadataChanged(*new_state);
-  }
-
-  void PlayAndroidMedia(const AndroidAppInfo& app_info) override {
-    // This is the only action that can be executed when we play android media.
-    DCHECK_EQ(app_info.action, kIntentActionView);
-    // Status is meaningless when playing android media.
-    DCHECK_EQ(app_info.status, AppStatus::kUnknown);
-
-    for (auto& subscriber : interaction_subscribers())
-      subscriber.OnOpenAppResponse(app_info);
-  }
-
-  void PlayWebMedia(const std::string& url) override {
-    const GURL gurl = GURL(url);
-    for (auto& it : interaction_subscribers())
-      it.OnOpenUrlResponse(gurl, /*in_background=*/false);
-  }
-
-  void NextTrack() override { media_controller().NextTrack(); }
-
-  void PreviousTrack() override { media_controller().PreviousTrack(); }
-
-  void Pause() override { media_controller().Suspend(); }
-
-  void Resume() override { media_controller().Resume(); }
-
-  void Stop() override {
-    // Note: we intentionally use 'suspend' here so the user can later resume;
-    // if we issued 'stop' there would be no way to resume.
-    // See b/140945356.
-    media_controller().Suspend();
-  }
-
-  const base::ObserverList<AssistantInteractionSubscriber>&
-  interaction_subscribers() {
-    return *parent_->interaction_subscribers_;
-  }
-
-  media_session::mojom::MediaController& media_controller() {
-    return *parent_->chromeos_media_controller_;
-  }
-
-  const raw_ptr<MediaHost> parent_;
-  mojo::Receiver<MediaDelegate> receiver_;
-};
-
-////////////////////////////////////////////////////////////////////////////////
-//   MediaHost
-////////////////////////////////////////////////////////////////////////////////
-
-MediaHost::MediaHost(AssistantBrowserDelegate* delegate,
-                     const base::ObserverList<AssistantInteractionSubscriber>*
-                         interaction_subscribers)
-    : interaction_subscribers_(interaction_subscribers),
-      media_session_(std::make_unique<AssistantMediaSession>(this)) {
-  DCHECK(delegate);
-
-  mojo::Remote<media_session::mojom::MediaControllerManager>
-      media_controller_manager;
-  delegate->RequestMediaControllerManager(
-      media_controller_manager.BindNewPipeAndPassReceiver());
-  media_controller_manager->CreateActiveMediaController(
-      chromeos_media_controller_.BindNewPipeAndPassReceiver());
-}
-
-MediaHost::~MediaHost() = default;
-
-void MediaHost::Initialize(
-    libassistant::mojom::MediaController* libassistant_controller,
-    mojo::PendingReceiver<libassistant::mojom::MediaDelegate> media_delegate) {
-  DCHECK(!libassistant_media_controller_);
-
-  libassistant_media_controller_ = libassistant_controller;
-  libassistant_media_delegate_ = std::make_unique<LibassistantMediaDelegate>(
-      this, std::move(media_delegate));
-}
-
-void MediaHost::Stop() {
-  libassistant_media_controller_ = nullptr;
-  StopObservingMediaController();
-}
-
-void MediaHost::ResumeInternalMediaPlayer() {
-  if (!libassistant_media_controller_) {
-    return;
-  }
-  libassistant_media_controller_->ResumeInternalMediaPlayer();
-}
-
-void MediaHost::PauseInternalMediaPlayer() {
-  if (!libassistant_media_controller_) {
-    return;
-  }
-  libassistant_media_controller_->PauseInternalMediaPlayer();
-}
-
-void MediaHost::SetRelatedInfoEnabled(bool enable) {
-  if (enable) {
-    StartObservingMediaController();
-  } else {
-    StopObservingMediaController();
-    ResetMediaState();
-  }
-}
-
-void MediaHost::UpdateMediaState(
-    const base::UnguessableToken& media_session_id,
-    libassistant::mojom::MediaStatePtr media_state) {
-  // MediaSession Integrated providers (include the libassistant internal
-  // media provider) will trigger media state change event. Only update the
-  // external media status if the state changes is triggered by external
-  // providers.
-  if (media_session_->internal_audio_focus_id() == media_session_id) {
-    return;
-  }
-
-  if (!libassistant_media_controller_) {
-    return;
-  }
-  libassistant_media_controller_->SetExternalPlaybackState(
-      std::move(media_state));
-}
-
-void MediaHost::ResetMediaState() {
-  if (!libassistant_media_controller_) {
-    return;
-  }
-  libassistant_media_controller_->SetExternalPlaybackState(
-      libassistant::mojom::MediaState::New());
-}
-
-void MediaHost::StartObservingMediaController() {
-  if (chromeos_media_state_observer_)
-    return;
-
-  chromeos_media_state_observer_ =
-      std::make_unique<ChromeosMediaStateObserver>(this);
-  chromeos_media_controller_->AddObserver(
-      chromeos_media_state_observer_->BindNewPipeAndPassRemote());
-}
-
-void MediaHost::StopObservingMediaController() {
-  chromeos_media_state_observer_.reset();
-}
-
-}  // namespace ash::assistant
diff --git a/chromeos/ash/services/assistant/media_host.h b/chromeos/ash/services/assistant/media_host.h
deleted file mode 100644
index e31df05..0000000
--- a/chromeos/ash/services/assistant/media_host.h
+++ /dev/null
@@ -1,88 +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.
-
-#ifndef CHROMEOS_ASH_SERVICES_ASSISTANT_MEDIA_HOST_H_
-#define CHROMEOS_ASH_SERVICES_ASSISTANT_MEDIA_HOST_H_
-
-#include "base/component_export.h"
-#include "base/memory/raw_ptr.h"
-#include "base/observer_list.h"
-#include "base/unguessable_token.h"
-#include "chromeos/ash/services/libassistant/public/mojom/media_controller.mojom-forward.h"
-#include "mojo/public/cpp/bindings/receiver.h"
-#include "mojo/public/cpp/bindings/remote.h"
-#include "services/media_session/public/mojom/media_controller.mojom.h"
-#include "services/media_session/public/mojom/media_session.mojom.h"
-
-namespace ash::assistant {
-
-class AssistantBrowserDelegate;
-class AssistantInteractionSubscriber;
-class AssistantManagerServiceImpl;
-class AssistantMediaSession;
-
-// Handles all media related interactions with Libassistant, which can broadly
-// be separated in 2 responsibilities:
-//   1) Let Libassistant know about the currently playing media.
-//   1) Let Libassistant control media (start/stop/open spotify).
-class COMPONENT_EXPORT(ASSISTANT_SERVICE) MediaHost {
- public:
-  MediaHost(AssistantBrowserDelegate* delegate,
-            const base::ObserverList<AssistantInteractionSubscriber>*
-                interaction_subscribers);
-  MediaHost(const MediaHost&) = delete;
-  MediaHost& operator=(const MediaHost&) = delete;
-  ~MediaHost();
-
-  void Initialize(
-      libassistant::mojom::MediaController* libassistant_controller,
-      mojo::PendingReceiver<libassistant::mojom::MediaDelegate> media_delegate);
-
-  // Stop observing ChromeOS media state.
-  void Stop();
-
-  // Pause/resume playback of Libassistant media player (which plays podcasts
-  // and news).
-  void ResumeInternalMediaPlayer();
-  void PauseInternalMediaPlayer();
-
-  // Called when the user allows/disallows related info.
-  // We only observe the current playing audio when related info is allowed.
-  void SetRelatedInfoEnabled(bool enable);
-
-  AssistantMediaSession& media_session() { return *media_session_; }
-
- private:
-  class LibassistantMediaDelegate;
-  class ChromeosMediaStateObserver;
-
-  void UpdateMediaState(const base::UnguessableToken& media_session_id,
-                        libassistant::mojom::MediaStatePtr media_state);
-  void ResetMediaState();
-
-  void StartObservingMediaController();
-  void StopObservingMediaController();
-
-  // Owned by our parent |AssistantManagerServiceImpl|.
-  const raw_ptr<const base::ObserverList<AssistantInteractionSubscriber>>
-      interaction_subscribers_;
-  // Owned by our parent |AssistantManagerServiceImpl|.
-  raw_ptr<libassistant::mojom::MediaController, DanglingUntriaged>
-      libassistant_media_controller_ = nullptr;
-
-  std::unique_ptr<AssistantMediaSession> media_session_;
-  mojo::Remote<media_session::mojom::MediaController>
-      chromeos_media_controller_;
-
-  // Helper class that will observe media changes on ChromeOS and sync them
-  // to Libassistant.
-  std::unique_ptr<ChromeosMediaStateObserver> chromeos_media_state_observer_;
-  // Helper class that will observe media changes in Libassistant and
-  // sync/apply them in ChromeOS.
-  std::unique_ptr<LibassistantMediaDelegate> libassistant_media_delegate_;
-};
-
-}  // namespace ash::assistant
-
-#endif  // CHROMEOS_ASH_SERVICES_ASSISTANT_MEDIA_HOST_H_
diff --git a/chromeos/ash/services/assistant/media_host_unittest.cc b/chromeos/ash/services/assistant/media_host_unittest.cc
deleted file mode 100644
index a7df6335..0000000
--- a/chromeos/ash/services/assistant/media_host_unittest.cc
+++ /dev/null
@@ -1,458 +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.
-
-#include "chromeos/ash/services/assistant/media_host.h"
-
-#include "base/notreached.h"
-#include "base/strings/stringprintf.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/test/task_environment.h"
-#include "chromeos/ash/services/assistant/media_session/assistant_media_session.h"
-#include "chromeos/ash/services/assistant/public/cpp/assistant_service.h"
-#include "chromeos/ash/services/assistant/test_support/libassistant_media_controller_mock.h"
-#include "chromeos/ash/services/assistant/test_support/mock_assistant_interaction_subscriber.h"
-#include "chromeos/ash/services/assistant/test_support/scoped_assistant_browser_delegate.h"
-#include "chromeos/ash/services/libassistant/public/mojom/android_app_info.mojom-shared.h"
-#include "chromeos/ash/services/libassistant/public/mojom/android_app_info.mojom.h"
-#include "chromeos/services/assistant/public/shared/utils.h"
-#include "services/media_session/public/cpp/test/mock_media_session.h"
-#include "services/media_session/public/cpp/test/test_media_controller.h"
-#include "testing/gmock/include/gmock/gmock.h"
-
-namespace ash::assistant {
-
-namespace {
-
-using libassistant::mojom::MediaState;
-using libassistant::mojom::MediaStatePtr;
-using libassistant::mojom::PlaybackState;
-using media_session::mojom::MediaSessionInfo;
-using media_session::test::MockMediaSessionMojoObserver;
-using media_session::test::TestMediaController;
-using ::testing::_;
-
-constexpr char kPlayAndroidMediaAction[] = "android.intent.action.VIEW";
-
-#define EXPECT_NO_CALLS(args...) EXPECT_CALL(args).Times(0);
-
-MATCHER_P(PlaybackStateIs, expected_state, "") {
-  if (arg.is_null()) {
-    *result_listener << "MediaStatePtr is nullptr";
-    return false;
-  }
-
-  if (arg->playback_state != expected_state) {
-    *result_listener << "Expected " << expected_state << " but got "
-                     << arg->playback_state;
-    return false;
-  }
-  return true;
-}
-
-std::string AndroidAppInfoToString(const AndroidAppInfo& app_info) {
-  return base::StringPrintf(R"(
-          AndroidAppInfo {
-              package_name '%s'
-              version '%i'
-              localized_app_name '%s'
-              action '%s'
-              intent '%s'
-              status '%i'
-          )",
-                            app_info.package_name.c_str(), app_info.version,
-                            app_info.localized_app_name.c_str(),
-                            app_info.action.c_str(), app_info.intent.c_str(),
-                            static_cast<int>(app_info.status));
-}
-
-MATCHER_P(MatchesAndroidAppInfo, expected, "") {
-  if (AndroidAppInfoToString(arg) == AndroidAppInfoToString(expected))
-    return true;
-
-  *result_listener << "\nExpected: " << AndroidAppInfoToString(expected);
-  *result_listener << "\nActual: " << AndroidAppInfoToString(arg);
-  return false;
-}
-
-class FakeMediaControllerManager
-    : public media_session::mojom::MediaControllerManager {
- public:
-  FakeMediaControllerManager() {
-    media_controller_ = std::make_unique<TestMediaController>();
-  }
-  FakeMediaControllerManager(const FakeMediaControllerManager&) = delete;
-  FakeMediaControllerManager& operator=(const FakeMediaControllerManager&) =
-      delete;
-  ~FakeMediaControllerManager() override = default;
-
-  mojo::Receiver<media_session::mojom::MediaControllerManager>& receiver() {
-    return receiver_;
-  }
-
-  TestMediaController* media_controller() const {
-    return media_controller_.get();
-  }
-
-  // media_session::mojom::MediaControllerManager implementation:
-  void CreateMediaControllerForSession(
-      mojo::PendingReceiver<media_session::mojom::MediaController> receiver,
-      const ::base::UnguessableToken& request_id) override {
-    NOTIMPLEMENTED();
-  }
-  void CreateActiveMediaController(
-      mojo::PendingReceiver<media_session::mojom::MediaController> receiver)
-      override {
-    media_controller_->BindMediaControllerReceiver(std::move(receiver));
-  }
-  void SuspendAllSessions() override { NOTIMPLEMENTED(); }
-
- private:
-  mojo::Receiver<media_session::mojom::MediaControllerManager> receiver_{this};
-  std::unique_ptr<TestMediaController> media_controller_;
-};
-
-}  // namespace
-
-class MediaHostTest : public testing::Test {
- public:
-  MediaHostTest() = default;
-  MediaHostTest(const MediaHostTest&) = delete;
-  MediaHostTest& operator=(const MediaHostTest&) = delete;
-  ~MediaHostTest() override = default;
-
-  void SetUp() override {
-    delegate_.SetMediaControllerManager(&media_controller_manager_.receiver());
-
-    media_host_ = std::make_unique<MediaHost>(AssistantBrowserDelegate::Get(),
-                                              &interaction_subscribers_);
-    media_host().Initialize(
-        &libassistant_controller_,
-        libassistant_media_delegate_.BindNewPipeAndPassReceiver());
-  }
-
-  LibassistantMediaControllerMock& libassistant_controller_mock() {
-    return libassistant_controller_;
-  }
-
-  libassistant::mojom::MediaDelegate& libassistant_media_delegate() {
-    return *libassistant_media_delegate_;
-  }
-
-  MediaHost& media_host() { return *media_host_; }
-
-  AssistantMediaSession& media_session() {
-    return media_host().media_session();
-  }
-
-  TestMediaController* media_controller() const {
-    return media_controller_manager_.media_controller();
-  }
-
-  void FlushMojomPipes() {
-    media_controller_manager_.receiver().FlushForTesting();
-    libassistant_media_delegate_.FlushForTesting();
-  }
-
-  void SetRelatedInfoEnabled(bool enabled) {
-    media_host().SetRelatedInfoEnabled(enabled);
-    FlushMojomPipes();
-  }
-
-  void StartMediaSession(
-      base::UnguessableToken token = base::UnguessableToken::Create()) {
-    media_controller()->SimulateMediaSessionChanged(token);
-    media_controller()->Flush();
-  }
-
-  void MediaSessionInfoChanged(
-      media_session::mojom::MediaSessionInfoPtr session_info) {
-    media_controller()->SimulateMediaSessionInfoChanged(
-        std::move(session_info));
-    media_controller()->Flush();
-  }
-
-  void MediaSessionMetadataChanged(
-      const media_session::MediaMetadata& meta_data) {
-    media_controller()->SimulateMediaSessionMetadataChanged(meta_data);
-    media_controller()->Flush();
-  }
-
-  void AddAssistantInteractionSubscriber(
-      AssistantInteractionSubscriber* subscriber) {
-    interaction_subscribers_.AddObserver(subscriber);
-  }
-
-  void ClearAssistantInteractionSubscribers() {
-    interaction_subscribers_.Clear();
-  }
-
- private:
-  base::test::SingleThreadTaskEnvironment environment_;
-
-  base::ObserverList<AssistantInteractionSubscriber> interaction_subscribers_;
-  FakeMediaControllerManager media_controller_manager_;
-  ScopedAssistantBrowserDelegate delegate_;
-  testing::StrictMock<LibassistantMediaControllerMock> libassistant_controller_;
-  mojo::Remote<libassistant::mojom::MediaDelegate> libassistant_media_delegate_;
-  std::unique_ptr<MediaHost> media_host_;
-};
-
-TEST_F(MediaHostTest, ShouldSupportResumePlaying) {
-  EXPECT_CALL(libassistant_controller_mock(), ResumeInternalMediaPlayer);
-
-  media_host().ResumeInternalMediaPlayer();
-}
-
-TEST_F(MediaHostTest, ShouldSupportPausePlaying) {
-  EXPECT_CALL(libassistant_controller_mock(), PauseInternalMediaPlayer);
-
-  media_host().PauseInternalMediaPlayer();
-}
-
-TEST_F(MediaHostTest, ShouldInitiallyNotObserveMediaChanges) {
-  EXPECT_EQ(0, media_controller()->add_observer_count());
-}
-
-TEST_F(MediaHostTest,
-       ShouldStartObservingMediaChangesWhenRelatedInfoIsEnabled) {
-  SetRelatedInfoEnabled(true);
-  EXPECT_EQ(1, media_controller()->add_observer_count());
-  EXPECT_EQ(1, media_controller()->GetActiveObserverCount());
-}
-
-TEST_F(MediaHostTest,
-       ShouldStopObservingMediaChangesAndFlushStateWhenRelatedInfoIsDisabled) {
-  SetRelatedInfoEnabled(true);
-
-  EXPECT_CALL(libassistant_controller_mock(), SetExternalPlaybackState);
-  SetRelatedInfoEnabled(false);
-
-  // Note the observer is not unbound, but the connection is severed.
-  EXPECT_EQ(1, media_controller()->add_observer_count());
-  EXPECT_EQ(0, media_controller()->GetActiveObserverCount());
-}
-
-TEST_F(MediaHostTest, ShouldSetTitleWhenCallingSetExternalPlaybackState) {
-  SetRelatedInfoEnabled(true);
-  StartMediaSession();
-
-  MediaStatePtr actual_state;
-  EXPECT_CALL(libassistant_controller_mock(), SetExternalPlaybackState)
-      .WillOnce([&](MediaStatePtr state) { actual_state = std::move(state); });
-
-  media_session::MediaMetadata meta_data;
-  meta_data.title = u"the title";
-  MediaSessionMetadataChanged(meta_data);
-
-  ASSERT_FALSE(actual_state.is_null());
-  ASSERT_FALSE(actual_state->metadata.is_null());
-  EXPECT_EQ(actual_state->metadata->title, "the title");
-}
-
-TEST_F(MediaHostTest, ShouldDropInvalidStates) {
-  SetRelatedInfoEnabled(true);
-  StartMediaSession();
-
-  auto session_info = MediaSessionInfo::New();
-  session_info->state = MediaSessionInfo::SessionState::kSuspended;
-  session_info->playback_state =
-      media_session::mojom::MediaPlaybackState::kPlaying;
-  MediaSessionInfoChanged(std::move(session_info));
-
-  EXPECT_NO_CALLS(libassistant_controller_mock(), SetExternalPlaybackState);
-
-  media_session::MediaMetadata meta_data;
-  MediaSessionMetadataChanged(meta_data);
-}
-
-TEST_F(MediaHostTest, ShouldSetPlaybackState) {
-  SetRelatedInfoEnabled(true);
-  StartMediaSession();
-
-  // State is idle if MediaSessionInfo is never set.
-  {
-    EXPECT_CALL(
-        libassistant_controller_mock(),
-        SetExternalPlaybackState(PlaybackStateIs(PlaybackState::kIdle)));
-    media_session::MediaMetadata meta_data;
-    MediaSessionMetadataChanged(meta_data);
-  }
-
-  // State kPlaying.
-  {
-    EXPECT_CALL(
-        libassistant_controller_mock(),
-        SetExternalPlaybackState(PlaybackStateIs(PlaybackState::kPlaying)));
-
-    auto session_info = MediaSessionInfo::New();
-    session_info->playback_state =
-        media_session::mojom::MediaPlaybackState::kPlaying;
-    MediaSessionInfoChanged(std::move(session_info));
-  }
-
-  // State kPaused.
-  {
-    EXPECT_CALL(
-        libassistant_controller_mock(),
-        SetExternalPlaybackState(PlaybackStateIs(PlaybackState::kPaused)));
-
-    auto session_info = MediaSessionInfo::New();
-    session_info->playback_state =
-        media_session::mojom::MediaPlaybackState::kPaused;
-    MediaSessionInfoChanged(std::move(session_info));
-  }
-
-  // State is kInvalid if session is inactive
-  {
-    EXPECT_CALL(
-        libassistant_controller_mock(),
-        SetExternalPlaybackState(PlaybackStateIs(PlaybackState::kIdle)));
-
-    auto session_info = MediaSessionInfo::New();
-    session_info->state = MediaSessionInfo::SessionState::kInactive;
-    session_info->playback_state =
-        media_session::mojom::MediaPlaybackState::kPaused;
-    MediaSessionInfoChanged(std::move(session_info));
-  }
-}
-
-TEST_F(MediaHostTest, ShouldResetPlaybackStateWhenDisablingRelatedInfo) {
-  SetRelatedInfoEnabled(true);
-  StartMediaSession();
-
-  MediaStatePtr actual_state;
-  EXPECT_CALL(libassistant_controller_mock(), SetExternalPlaybackState)
-      .WillOnce([&](MediaStatePtr state) { actual_state = std::move(state); });
-
-  SetRelatedInfoEnabled(false);
-
-  const MediaStatePtr empty_state = MediaState::New();
-  EXPECT_EQ(actual_state, empty_state);
-}
-
-TEST_F(MediaHostTest,
-       ShouldIgnorePlaybackStateUpdatesWhenRelatedInfoIsDisabled) {
-  SetRelatedInfoEnabled(true);
-  StartMediaSession();
-
-  // Playback state is updated when disabling related info.
-  EXPECT_CALL(libassistant_controller_mock(), SetExternalPlaybackState);
-  SetRelatedInfoEnabled(false);
-
-  // But not for any consecutive changes.
-  EXPECT_NO_CALLS(libassistant_controller_mock(), SetExternalPlaybackState);
-  media_session::MediaMetadata meta_data;
-  MediaSessionMetadataChanged(meta_data);
-}
-
-TEST_F(MediaHostTest,
-       ShouldIgnorePlaybackStateUpdatesForLibassistantInternalSessions) {
-  SetRelatedInfoEnabled(true);
-
-  const auto session_id = base::UnguessableToken::Create();
-  StartMediaSession(session_id);
-  media_session().SetInternalAudioFocusIdForTesting(session_id);
-
-  EXPECT_NO_CALLS(libassistant_controller_mock(), SetExternalPlaybackState);
-
-  media_session::MediaMetadata meta_data;
-  MediaSessionMetadataChanged(meta_data);
-}
-
-TEST_F(MediaHostTest, ShouldForwardLibassistantMediaSessionUpdates) {
-  MockMediaSessionMojoObserver media_session_observer(media_session());
-  media_session_observer.WaitForEmptyMetadata();
-
-  auto input = MediaState::New();
-  input->metadata = libassistant::mojom::MediaMetadata::New();
-  input->metadata->title = "the title";
-  input->metadata->artist = "the artist";
-  input->metadata->album = "the album";
-  libassistant_media_delegate().OnPlaybackStateChanged(std::move(input));
-  FlushMojomPipes();
-
-  media_session::MediaMetadata expected_output;
-  expected_output.title = u"the title";
-  expected_output.artist = u"the artist";
-  expected_output.album = u"the album";
-  media_session_observer.WaitForExpectedMetadata(expected_output);
-}
-
-TEST_F(MediaHostTest, ShouldForwardLibassistantOpenAndroidMediaUpdates) {
-  testing::StrictMock<MockAssistantInteractionSubscriber> mock;
-  AddAssistantInteractionSubscriber(&mock);
-
-  AndroidAppInfo output_app_info;
-  output_app_info.package_name = "the package name";
-  output_app_info.version = 111;
-  output_app_info.localized_app_name = "the localized name";
-  output_app_info.action = kPlayAndroidMediaAction;
-  output_app_info.intent = "the intent";
-  output_app_info.status = AppStatus::kUnknown;
-
-  EXPECT_CALL(mock, OnOpenAppResponse(MatchesAndroidAppInfo(output_app_info)));
-
-  AndroidAppInfo input_app_info;
-  input_app_info.package_name = "the package name";
-  input_app_info.version = 111;
-  input_app_info.localized_app_name = "the localized name";
-  input_app_info.intent = "the intent";
-  input_app_info.status = AppStatus::kUnknown;
-  input_app_info.action = kPlayAndroidMediaAction;
-
-  libassistant_media_delegate().PlayAndroidMedia(std::move(input_app_info));
-
-  FlushMojomPipes();
-  ClearAssistantInteractionSubscribers();
-}
-
-TEST_F(MediaHostTest, ShouldOpenWebMediaUrl) {
-  testing::StrictMock<MockAssistantInteractionSubscriber> mock;
-  AddAssistantInteractionSubscriber(&mock);
-
-  EXPECT_CALL(
-      mock, OnOpenUrlResponse(GURL("http://the.url"), /*in_background=*/false));
-
-  libassistant_media_delegate().PlayWebMedia("http://the.url");
-
-  FlushMojomPipes();
-  ClearAssistantInteractionSubscribers();
-}
-
-TEST_F(MediaHostTest, ShouldPlayNextTrack) {
-  EXPECT_EQ(0, media_controller()->next_track_count());
-  libassistant_media_delegate().NextTrack();
-  FlushMojomPipes();
-  EXPECT_EQ(1, media_controller()->next_track_count());
-}
-
-TEST_F(MediaHostTest, ShouldPlayPreviousTrack) {
-  EXPECT_EQ(0, media_controller()->previous_track_count());
-  libassistant_media_delegate().PreviousTrack();
-  FlushMojomPipes();
-  EXPECT_EQ(1, media_controller()->previous_track_count());
-}
-
-TEST_F(MediaHostTest, ShouldPause) {
-  EXPECT_EQ(0, media_controller()->suspend_count());
-  libassistant_media_delegate().Pause();
-  FlushMojomPipes();
-  EXPECT_EQ(1, media_controller()->suspend_count());
-}
-
-TEST_F(MediaHostTest, ShouldResume) {
-  EXPECT_EQ(0, media_controller()->resume_count());
-  libassistant_media_delegate().Resume();
-  FlushMojomPipes();
-  EXPECT_EQ(1, media_controller()->resume_count());
-}
-
-TEST_F(MediaHostTest, ShouldStop) {
-  EXPECT_EQ(0, media_controller()->suspend_count());
-  libassistant_media_delegate().Stop();
-  FlushMojomPipes();
-  EXPECT_EQ(1, media_controller()->suspend_count());
-}
-
-}  // namespace ash::assistant
diff --git a/chromeos/ash/services/assistant/media_session/assistant_media_session.cc b/chromeos/ash/services/assistant/media_session/assistant_media_session.cc
deleted file mode 100644
index 41e0a13..0000000
--- a/chromeos/ash/services/assistant/media_session/assistant_media_session.cc
+++ /dev/null
@@ -1,247 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chromeos/ash/services/assistant/media_session/assistant_media_session.h"
-
-#include <utility>
-
-#include "base/functional/bind.h"
-#include "base/memory/scoped_refptr.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/task/sequenced_task_runner.h"
-#include "chromeos/ash/services/assistant/media_host.h"
-#include "chromeos/ash/services/assistant/public/cpp/assistant_browser_delegate.h"
-#include "chromeos/ash/services/libassistant/public/mojom/media_controller.mojom.h"
-#include "services/media_session/public/cpp/features.h"
-
-// A macro which ensures we are running on the main thread.
-#define ENSURE_MAIN_THREAD(method, ...)                                     \
-  if (!main_task_runner_->RunsTasksInCurrentSequence()) {                   \
-    main_task_runner_->PostTask(                                            \
-        FROM_HERE,                                                          \
-        base::BindOnce(method, weak_factory_.GetWeakPtr(), ##__VA_ARGS__)); \
-    return;                                                                 \
-  }
-
-namespace ash::assistant {
-
-namespace {
-
-using media_session::mojom::AudioFocusType;
-using media_session::mojom::MediaPlaybackState;
-using media_session::mojom::MediaSessionInfo;
-
-const char kAudioFocusSourceName[] = "assistant";
-
-}  // namespace
-
-AssistantMediaSession::AssistantMediaSession(MediaHost* host)
-    : host_(host),
-      main_task_runner_(base::SequencedTaskRunner::GetCurrentDefault()) {}
-
-AssistantMediaSession::~AssistantMediaSession() {
-  AbandonAudioFocusIfNeeded();
-}
-
-void AssistantMediaSession::GetMediaSessionInfo(
-    GetMediaSessionInfoCallback callback) {
-  std::move(callback).Run(session_info_.Clone());
-}
-
-void AssistantMediaSession::AddObserver(
-    mojo::PendingRemote<media_session::mojom::MediaSessionObserver> observer) {
-  ENSURE_MAIN_THREAD(&AssistantMediaSession::AddObserver, std::move(observer));
-  mojo::Remote<media_session::mojom::MediaSessionObserver>
-      media_session_observer(std::move(observer));
-  media_session_observer->MediaSessionInfoChanged(session_info_.Clone());
-  media_session_observer->MediaSessionMetadataChanged(metadata_);
-  observers_.Add(std::move(media_session_observer));
-}
-
-void AssistantMediaSession::GetDebugInfo(GetDebugInfoCallback callback) {
-  std::move(callback).Run(media_session::mojom::MediaSessionDebugInfo::New());
-}
-
-void AssistantMediaSession::GetVisibility(GetVisibilityCallback callback) {
-  std::move(callback).Run(false);
-}
-
-void AssistantMediaSession::StartDucking() {
-  if (!IsSessionStateActive())
-    return;
-  SetAudioFocusInfo(MediaSessionInfo::SessionState::kDucking,
-                    audio_focus_type_);
-}
-
-void AssistantMediaSession::StopDucking() {
-  if (!IsSessionStateDucking())
-    return;
-  SetAudioFocusInfo(MediaSessionInfo::SessionState::kActive, audio_focus_type_);
-}
-
-void AssistantMediaSession::Suspend(SuspendType suspend_type) {
-  if (!IsSessionStateActive() && !IsSessionStateDucking())
-    return;
-  SetAudioFocusInfo(MediaSessionInfo::SessionState::kSuspended,
-                    audio_focus_type_);
-  host_->PauseInternalMediaPlayer();
-}
-
-void AssistantMediaSession::Resume(SuspendType suspend_type) {
-  if (!IsSessionStateSuspended())
-    return;
-  SetAudioFocusInfo(MediaSessionInfo::SessionState::kActive, audio_focus_type_);
-  host_->ResumeInternalMediaPlayer();
-}
-
-void AssistantMediaSession::RequestAudioFocus(AudioFocusType audio_focus_type) {
-  if (!base::FeatureList::IsEnabled(
-          media_session::features::kMediaSessionService)) {
-    return;
-  }
-
-  if (audio_focus_request_client_.is_bound()) {
-    // We have an existing request so we should request an updated focus type.
-    audio_focus_request_client_->RequestAudioFocus(
-        session_info_.Clone(), audio_focus_type,
-        base::BindOnce(&AssistantMediaSession::FinishAudioFocusRequest,
-                       base::Unretained(this), audio_focus_type));
-    return;
-  }
-
-  EnsureServiceConnection();
-
-  // Create a mojo interface pointer to our media session.
-  receiver_.reset();
-  audio_focus_manager_->RequestAudioFocus(
-      audio_focus_request_client_.BindNewPipeAndPassReceiver(),
-      receiver_.BindNewPipeAndPassRemote(), session_info_.Clone(),
-      audio_focus_type,
-      base::BindOnce(&AssistantMediaSession::FinishInitialAudioFocusRequest,
-                     base::Unretained(this), audio_focus_type));
-}
-
-void AssistantMediaSession::AbandonAudioFocusIfNeeded() {
-  if (!base::FeatureList::IsEnabled(
-          media_session::features::kMediaSessionService)) {
-    return;
-  }
-
-  if (IsSessionStateInactive())
-    return;
-
-  SetAudioFocusInfo(MediaSessionInfo::SessionState::kInactive,
-                    audio_focus_type_);
-
-  if (!audio_focus_request_client_.is_bound())
-    return;
-
-  audio_focus_request_client_->AbandonAudioFocus();
-  audio_focus_request_client_.reset();
-  audio_focus_manager_.reset();
-  internal_audio_focus_id_ = base::UnguessableToken::Null();
-}
-
-void AssistantMediaSession::NotifyMediaSessionMetadataChanged(
-    const libassistant::mojom::MediaState& status) {
-  media_session::MediaMetadata metadata;
-
-  if (!status.metadata.is_null()) {
-    metadata.title = base::UTF8ToUTF16(status.metadata->title);
-    metadata.artist = base::UTF8ToUTF16(status.metadata->artist);
-    metadata.album = base::UTF8ToUTF16(status.metadata->album);
-  }
-
-  bool metadata_changed = metadata_ != metadata;
-  if (!metadata_changed)
-    return;
-
-  metadata_ = metadata;
-
-  for (auto& observer : observers_)
-    observer->MediaSessionMetadataChanged(this->metadata_);
-}
-
-base::WeakPtr<AssistantMediaSession> AssistantMediaSession::GetWeakPtr() {
-  return weak_factory_.GetWeakPtr();
-}
-
-bool AssistantMediaSession::IsSessionStateActive() const {
-  return session_info_.state == MediaSessionInfo::SessionState::kActive;
-}
-
-bool AssistantMediaSession::IsSessionStateDucking() const {
-  return session_info_.state == MediaSessionInfo::SessionState::kDucking;
-}
-
-bool AssistantMediaSession::IsSessionStateSuspended() const {
-  return session_info_.state == MediaSessionInfo::SessionState::kSuspended;
-}
-
-bool AssistantMediaSession::IsSessionStateInactive() const {
-  return session_info_.state == MediaSessionInfo::SessionState::kInactive;
-}
-
-void AssistantMediaSession::SetInternalAudioFocusIdForTesting(
-    const base::UnguessableToken& token) {
-  internal_audio_focus_id_ = token;
-}
-
-void AssistantMediaSession::EnsureServiceConnection() {
-  DCHECK(base::FeatureList::IsEnabled(
-      media_session::features::kMediaSessionService));
-
-  if (audio_focus_manager_.is_bound() && audio_focus_manager_.is_connected())
-    return;
-
-  audio_focus_manager_.reset();
-
-  AssistantBrowserDelegate::Get()->RequestAudioFocusManager(
-      audio_focus_manager_.BindNewPipeAndPassReceiver());
-  audio_focus_manager_->SetSource(base::UnguessableToken::Create(),
-                                  kAudioFocusSourceName);
-}
-
-void AssistantMediaSession::FinishAudioFocusRequest(
-    AudioFocusType audio_focus_type) {
-  DCHECK(audio_focus_request_client_.is_bound());
-
-  SetAudioFocusInfo(MediaSessionInfo::SessionState::kActive, audio_focus_type);
-}
-
-void AssistantMediaSession::FinishInitialAudioFocusRequest(
-    AudioFocusType audio_focus_type,
-    const base::UnguessableToken& request_id) {
-  internal_audio_focus_id_ = request_id;
-  FinishAudioFocusRequest(audio_focus_type);
-}
-
-void AssistantMediaSession::SetAudioFocusInfo(
-    MediaSessionInfo::SessionState audio_focus_state,
-    AudioFocusType audio_focus_type) {
-  if (audio_focus_state == session_info_.state &&
-      audio_focus_type == audio_focus_type_) {
-    return;
-  }
-
-  // Update |session_info_| and |audio_focus_type_|.
-  session_info_.state = audio_focus_state;
-  audio_focus_type_ = audio_focus_type;
-  session_info_.is_controllable =
-      !IsSessionStateInactive() &&
-      (audio_focus_type != AudioFocusType::kGainTransient);
-
-  NotifyMediaSessionInfoChanged();
-}
-
-void AssistantMediaSession::NotifyMediaSessionInfoChanged() {
-  ENSURE_MAIN_THREAD(&AssistantMediaSession::NotifyMediaSessionInfoChanged);
-  if (audio_focus_request_client_.is_bound())
-    audio_focus_request_client_->MediaSessionInfoChanged(session_info_.Clone());
-
-  for (auto& observer : observers_)
-    observer->MediaSessionInfoChanged(session_info_.Clone());
-}
-
-}  // namespace ash::assistant
diff --git a/chromeos/ash/services/assistant/media_session/assistant_media_session.h b/chromeos/ash/services/assistant/media_session/assistant_media_session.h
deleted file mode 100644
index fa9f2d7d..0000000
--- a/chromeos/ash/services/assistant/media_session/assistant_media_session.h
+++ /dev/null
@@ -1,146 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROMEOS_ASH_SERVICES_ASSISTANT_MEDIA_SESSION_ASSISTANT_MEDIA_SESSION_H_
-#define CHROMEOS_ASH_SERVICES_ASSISTANT_MEDIA_SESSION_ASSISTANT_MEDIA_SESSION_H_
-
-#include "base/memory/raw_ptr.h"
-#include "base/memory/scoped_refptr.h"
-#include "base/task/sequenced_task_runner.h"
-#include "base/timer/timer.h"
-#include "base/unguessable_token.h"
-#include "chromeos/ash/services/libassistant/public/mojom/media_controller.mojom-forward.h"
-#include "mojo/public/cpp/bindings/receiver.h"
-#include "mojo/public/cpp/bindings/remote_set.h"
-#include "services/media_session/public/mojom/audio_focus.mojom.h"
-#include "services/media_session/public/mojom/media_session.mojom.h"
-
-namespace ash::assistant {
-
-class MediaHost;
-
-// MediaSession manages the media session and audio focus for Assistant.
-// MediaSession allows clients to observe its changes via MediaSessionObserver,
-// and allows clients to resume/suspend/stop the managed players.
-class COMPONENT_EXPORT(ASSISTANT_SERVICE) AssistantMediaSession
-    : public media_session::mojom::MediaSession {
- public:
-  explicit AssistantMediaSession(MediaHost* host);
-
-  AssistantMediaSession(const AssistantMediaSession&) = delete;
-  AssistantMediaSession& operator=(const AssistantMediaSession&) = delete;
-
-  ~AssistantMediaSession() override;
-
-  // media_session.mojom.MediaSession overrides:
-  void Suspend(SuspendType suspend_type) override;
-  void Resume(SuspendType suspend_type) override;
-  void StartDucking() override;
-  void StopDucking() override;
-  void GetMediaSessionInfo(GetMediaSessionInfoCallback callback) override;
-  void GetDebugInfo(GetDebugInfoCallback callback) override;
-  void AddObserver(
-      mojo::PendingRemote<media_session::mojom::MediaSessionObserver> observer)
-      override;
-  void PreviousTrack() override {}
-  void NextTrack() override {}
-  void SkipAd() override {}
-  void PreviousSlide() override {}
-  void NextSlide() override {}
-  void Seek(base::TimeDelta seek_time) override {}
-  void Stop(SuspendType suspend_type) override {}
-  void GetMediaImageBitmap(const media_session::MediaImage& image,
-                           int minimum_size_px,
-                           int desired_size_px,
-                           GetMediaImageBitmapCallback callback) override {}
-  void SeekTo(base::TimeDelta seek_time) override {}
-  void ScrubTo(base::TimeDelta seek_time) override {}
-  void EnterPictureInPicture() override {}
-  void ExitPictureInPicture() override {}
-  void GetVisibility(GetVisibilityCallback callback) override;
-  void SetAudioSinkId(const std::optional<std::string>& sink_id) override {}
-  void ToggleMicrophone() override {}
-  void ToggleCamera() override {}
-  void HangUp() override {}
-  void Raise() override {}
-  void SetMute(bool mute) override {}
-  void RequestMediaRemoting() override {}
-  void EnterAutoPictureInPicture() override {}
-
-  // Requests/abandons audio focus to the AudioFocusManager.
-  void RequestAudioFocus(media_session::mojom::AudioFocusType audio_focus_type);
-  void AbandonAudioFocusIfNeeded();
-
-  void NotifyMediaSessionMetadataChanged(
-      const libassistant::mojom::MediaState& status);
-
-  base::WeakPtr<AssistantMediaSession> GetWeakPtr();
-
-  // Returns if the session is currently active.
-  bool IsSessionStateActive() const;
-  // Returns if the session is currently ducking.
-  bool IsSessionStateDucking() const;
-  // Returns if the session is currently suspended.
-  bool IsSessionStateSuspended() const;
-  // Returns if the session is currently inactive.
-  bool IsSessionStateInactive() const;
-
-  // Returns internal audio focus id.
-  base::UnguessableToken internal_audio_focus_id() {
-    return internal_audio_focus_id_;
-  }
-
-  void SetInternalAudioFocusIdForTesting(const base::UnguessableToken& token);
-
- private:
-  // Ensures that |audio_focus_ptr_| is connected.
-  void EnsureServiceConnection();
-
-  // Called by AudioFocusManager when an async audio focus request is completed.
-  void FinishAudioFocusRequest(media_session::mojom::AudioFocusType type);
-  void FinishInitialAudioFocusRequest(media_session::mojom::AudioFocusType type,
-                                      const base::UnguessableToken& request_id);
-
-  // Sets |session_info_|, |audio_focus_type_| and notifies observers about
-  // the state change.
-  void SetAudioFocusInfo(
-      media_session::mojom::MediaSessionInfo::SessionState audio_focus_state,
-      media_session::mojom::AudioFocusType audio_focus_type);
-
-  // Notifies mojo observers that the MediaSessionInfo has changed.
-  void NotifyMediaSessionInfoChanged();
-
-  // The current metadata associated with the current media session.
-  media_session::MediaMetadata metadata_;
-
-  const raw_ptr<MediaHost> host_;
-
-  scoped_refptr<base::SequencedTaskRunner> main_task_runner_;
-  // Binding for Mojo pointer to |this| held by AudioFocusManager.
-  mojo::Receiver<media_session::mojom::MediaSession> receiver_{this};
-
-  mojo::RemoteSet<media_session::mojom::MediaSessionObserver> observers_;
-
-  // Holds a pointer to the MediaSessionService.
-  mojo::Remote<media_session::mojom::AudioFocusManager> audio_focus_manager_;
-
-  // If the media session has acquired audio focus then this will contain a
-  // pointer to that requests AudioFocusRequestClient.
-  mojo::Remote<media_session::mojom::AudioFocusRequestClient>
-      audio_focus_request_client_;
-
-  media_session::mojom::MediaSessionInfo session_info_;
-
-  media_session::mojom::AudioFocusType audio_focus_type_;
-
-  // Audio focus request Id for the internal media which is playing.
-  base::UnguessableToken internal_audio_focus_id_ =
-      base::UnguessableToken::Null();
-
-  base::WeakPtrFactory<AssistantMediaSession> weak_factory_{this};
-};
-
-}  // namespace ash::assistant
-
-#endif  // CHROMEOS_ASH_SERVICES_ASSISTANT_MEDIA_SESSION_ASSISTANT_MEDIA_SESSION_H_
diff --git a/chromeos/ash/services/assistant/media_session/assistant_media_session_unittest.cc b/chromeos/ash/services/assistant/media_session/assistant_media_session_unittest.cc
deleted file mode 100644
index fa330ca5..0000000
--- a/chromeos/ash/services/assistant/media_session/assistant_media_session_unittest.cc
+++ /dev/null
@@ -1,88 +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.
-
-#include "chromeos/ash/services/assistant/media_session/assistant_media_session.h"
-
-#include <memory>
-
-#include "base/test/task_environment.h"
-#include "chromeos/ash/services/assistant/media_host.h"
-#include "chromeos/ash/services/assistant/public/cpp/assistant_browser_delegate.h"
-#include "chromeos/ash/services/assistant/test_support/libassistant_media_controller_mock.h"
-#include "chromeos/ash/services/assistant/test_support/scoped_assistant_browser_delegate.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace ash::assistant {
-
-namespace {
-
-using media_session::mojom::MediaSession;
-using media_session::mojom::MediaSessionAction;
-using media_session::mojom::MediaSessionInfo;
-
-}  // namespace
-
-class AssistantMediaSessionTest : public testing::Test {
- public:
-  AssistantMediaSessionTest() = default;
-  ~AssistantMediaSessionTest() override = default;
-
-  void SetUp() override {
-    media_host().Initialize(
-        &libassistant_media_controller_,
-        libassistant_media_delegate_.BindNewPipeAndPassReceiver());
-  }
-
-  AssistantMediaSession* assistant_media_session() {
-    return &assistant_media_session_;
-  }
-
-  MediaHost& media_host() { return media_host_; }
-
-  LibassistantMediaControllerMock& libassistant_media_controller_mock() {
-    return libassistant_media_controller_;
-  }
-
- private:
-  // Needed for a test environment to support post tasks and should outlive
-  // other class members.
-  base::test::SingleThreadTaskEnvironment task_environment_;
-
-  ScopedAssistantBrowserDelegate delegate_;
-  testing::StrictMock<LibassistantMediaControllerMock>
-      libassistant_media_controller_;
-  mojo::Remote<libassistant::mojom::MediaDelegate> libassistant_media_delegate_;
-  MediaHost media_host_{AssistantBrowserDelegate::Get(),
-                        /*interaction_subscribers=*/nullptr};
-  AssistantMediaSession assistant_media_session_{&media_host_};
-};
-
-TEST_F(AssistantMediaSessionTest, ShouldUpdateSessionStateOnStartStopDucking) {
-  assistant_media_session()->StartDucking();
-  EXPECT_TRUE(assistant_media_session()->IsSessionStateDucking());
-
-  assistant_media_session()->StopDucking();
-  EXPECT_TRUE(assistant_media_session()->IsSessionStateActive());
-}
-
-TEST_F(AssistantMediaSessionTest,
-       ShouldUpdateSessionStateAndSendActionOnSuspendResumePlaying) {
-  // Suspend.
-  EXPECT_CALL(libassistant_media_controller_mock(), PauseInternalMediaPlayer);
-  assistant_media_session()->Suspend(MediaSession::SuspendType::kSystem);
-  EXPECT_TRUE(assistant_media_session()->IsSessionStateSuspended());
-
-  // Then resume.
-  EXPECT_CALL(libassistant_media_controller_mock(), ResumeInternalMediaPlayer);
-  assistant_media_session()->Resume(MediaSession::SuspendType::kSystem);
-  EXPECT_TRUE(assistant_media_session()->IsSessionStateActive());
-
-  // And pause again.
-  EXPECT_CALL(libassistant_media_controller_mock(), PauseInternalMediaPlayer);
-  assistant_media_session()->Suspend(MediaSession::SuspendType::kSystem);
-  EXPECT_TRUE(assistant_media_session()->IsSessionStateSuspended());
-}
-
-}  // namespace ash::assistant
diff --git a/chromeos/ash/services/assistant/platform/audio_devices.cc b/chromeos/ash/services/assistant/platform/audio_devices.cc
deleted file mode 100644
index 80c62307..0000000
--- a/chromeos/ash/services/assistant/platform/audio_devices.cc
+++ /dev/null
@@ -1,301 +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.
-
-#include "chromeos/ash/services/assistant/platform/audio_devices.h"
-
-#include <optional>
-
-#include "base/memory/raw_ptr.h"
-#include "base/metrics/histogram_functions.h"
-#include "base/scoped_observation.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_split.h"
-#include "base/strings/string_util.h"
-#include "base/system/sys_info.h"
-#include "chromeos/ash/components/audio/audio_device.h"
-#include "chromeos/ash/components/audio/cras_audio_handler.h"
-#include "chromeos/ash/services/assistant/public/cpp/features.h"
-
-namespace ash::assistant {
-
-namespace {
-
-constexpr const char kDefaultLocale[] = "en_us";
-
-// Hotword model is expected to have <language>_<region> format with lower
-// case, while the locale in pref is stored as <language>-<region> with region
-// code in capital letters. So we need to convert the pref locale to the
-// correct format.
-// Examples:
-//     "fr"     ->  "fr_fr"
-//     "nl-BE"  ->  "nl_be"
-std::optional<std::string> ToHotwordModel(std::string pref_locale) {
-  std::vector<std::string> code_strings = base::SplitString(
-      pref_locale, "-", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
-
-  if (code_strings.size() == 0) {
-    // Note: I am not sure this happens during real operations, but it
-    // definitely happens during the ChromeOS performance tests.
-    return std::nullopt;
-  }
-
-  DCHECK_LT(code_strings.size(), 3u);
-
-  // For locales with language code "en", use "en_all" hotword model.
-  if (code_strings[0] == "en")
-    return "en_all";
-
-  // If the language code and country code happen to be the same, e.g.
-  // France (FR) and French (fr), the locale will be stored as "fr" instead
-  // of "fr-FR" in the profile on Chrome OS.
-  if (code_strings.size() == 1)
-    return code_strings[0] + "_" + code_strings[0];
-
-  return code_strings[0] + "_" + base::ToLowerASCII(code_strings[1]);
-}
-
-const AudioDevice* GetHighestPriorityDevice(const AudioDevice* left,
-                                            const AudioDevice* right) {
-  if (!left)
-    return right;
-  if (!right)
-    return left;
-  return left->priority < right->priority ? right : left;
-}
-
-std::optional<uint64_t> IdToOptional(const AudioDevice* device) {
-  if (!device)
-    return std::nullopt;
-  return device->id;
-}
-
-std::optional<uint64_t> GetHotwordDeviceId(const AudioDeviceList& devices) {
-  const AudioDevice* result = nullptr;
-
-  for (const AudioDevice& device : devices) {
-    if (!device.is_input)
-      continue;
-
-    switch (device.type) {
-      case AudioDeviceType::kHotword:
-        result = GetHighestPriorityDevice(result, &device);
-        break;
-      default:
-        // ignore other devices
-        break;
-    }
-  }
-
-  return IdToOptional(result);
-}
-
-std::optional<uint64_t> GetPreferredDeviceId(const AudioDeviceList& devices) {
-  const AudioDevice* result = nullptr;
-
-  for (const AudioDevice& device : devices) {
-    if (!device.is_input)
-      continue;
-
-    switch (device.type) {
-      case AudioDeviceType::kMic:
-      case AudioDeviceType::kUsb:
-      case AudioDeviceType::kHeadphone:
-      case AudioDeviceType::kInternalMic:
-      case AudioDeviceType::kFrontMic:
-      case AudioDeviceType::kRearMic:
-      case AudioDeviceType::kKeyboardMic:
-        result = GetHighestPriorityDevice(result, &device);
-        break;
-      default:
-        // Ignore other devices. Note that we ignore bluetooth devices due to
-        // battery consumption concerns.
-        break;
-    }
-  }
-
-  return IdToOptional(result);
-}
-
-std::optional<std::string> ToString(std::optional<uint64_t> int_value) {
-  if (!int_value)
-    return std::nullopt;
-  return base::NumberToString(int_value.value());
-}
-
-}  // namespace
-
-// Observer that will report all changes to the audio devices.
-// It will unsubscribe from |CrasAudioHandler| in its destructor.
-class AudioDevices::ScopedCrasAudioHandlerObserver
-    : private CrasAudioHandler::AudioObserver {
- public:
-  ScopedCrasAudioHandlerObserver(CrasAudioHandler* cras_audio_handler,
-                                 AudioDevices* parent)
-      : parent_(parent), cras_audio_handler_(cras_audio_handler) {}
-  ScopedCrasAudioHandlerObserver(const ScopedCrasAudioHandlerObserver&) =
-      delete;
-  ScopedCrasAudioHandlerObserver& operator=(
-      const ScopedCrasAudioHandlerObserver&) = delete;
-  ~ScopedCrasAudioHandlerObserver() override = default;
-
-  // Start the observer, which means it will
-  //    - Subscribe for changes
-  //    - Fetch the current state.
-  void StartObserving() {
-    scoped_observer_.Observe(cras_audio_handler_.get());
-    FetchAudioNodes();
-  }
-
- private:
-  // CrasAudioHandler::AudioObserver implementation:
-  void OnAudioNodesChanged() override { FetchAudioNodes(); }
-
-  void FetchAudioNodes() {
-    if (!base::SysInfo::IsRunningOnChromeOS())
-      return;
-
-    AudioDeviceList audio_devices;
-    cras_audio_handler_->GetAudioDevices(&audio_devices);
-    parent_->SetAudioDevices(audio_devices);
-  }
-
-  const raw_ptr<AudioDevices> parent_;
-  // Owned by |AssistantManagerServiceImpl|.
-  const raw_ptr<CrasAudioHandler> cras_audio_handler_;
-  base::ScopedObservation<CrasAudioHandler, CrasAudioHandler::AudioObserver>
-      scoped_observer_{this};
-};
-
-// Sends the new hotword model to |cras_audio_handler|. If that fails this class
-// will attempt to set the hotword model to |kDefaultLocale|.
-class AudioDevices::HotwordModelUpdater {
- public:
-  HotwordModelUpdater(CrasAudioHandler* cras_audio_handler,
-                      uint64_t hotword_device,
-                      const std::string& locale)
-      : cras_audio_handler_(cras_audio_handler),
-        hotword_device_(hotword_device),
-        locale_(locale) {
-    SendUpdate();
-  }
-
-  HotwordModelUpdater(const HotwordModelUpdater&) = delete;
-  HotwordModelUpdater& operator=(const HotwordModelUpdater&) = delete;
-  ~HotwordModelUpdater() = default;
-
- private:
-  void SendUpdate() {
-    std::string hotword_model =
-        ToHotwordModel(locale_).value_or(kDefaultLocale);
-
-    VLOG(2) << "Changing audio hotword model of device " << hotword_device_
-            << " to '" << hotword_model << "'";
-
-    cras_audio_handler_->SetHotwordModel(
-        hotword_device_, hotword_model,
-        base::BindOnce(&HotwordModelUpdater::SetDspHotwordLocaleCallback,
-                       weak_factory_.GetWeakPtr(), hotword_model));
-  }
-
-  void SetDspHotwordLocaleCallback(std::string pref_locale, bool success) {
-    base::UmaHistogramBoolean("Assistant.SetDspHotwordLocale", success);
-    if (success) {
-      VLOG(2) << "Successfully changed audio hotword model";
-      return;
-    }
-
-    LOG(ERROR) << "Set " << pref_locale
-               << " hotword model failed, fallback to default locale.";
-    // Reset the locale to the default value if we failed to sync it to the
-    // locale stored in user's pref.
-    cras_audio_handler_->SetHotwordModel(
-        hotword_device_, /* hotword_model */ kDefaultLocale,
-        base::BindOnce([](bool success) {
-          if (!success)
-            LOG(ERROR) << "Reset to default hotword model failed.";
-        }));
-  }
-
-  const raw_ptr<CrasAudioHandler> cras_audio_handler_;
-  uint64_t hotword_device_;
-  std::string locale_;
-
-  base::WeakPtrFactory<HotwordModelUpdater> weak_factory_{this};
-};
-
-AudioDevices::AudioDevices(CrasAudioHandler* cras_audio_handler,
-                           const std::string& locale)
-    : cras_audio_handler_(cras_audio_handler),
-      locale_(locale),
-      scoped_cras_audio_handler_observer_(
-          std::make_unique<ScopedCrasAudioHandlerObserver>(cras_audio_handler,
-                                                           this)) {
-  // Note we can only start the observer here, at the end of the constructor,
-  // to ensure this class is properly initialized when we receive the current
-  // list of audio devices.
-  scoped_cras_audio_handler_observer_->StartObserving();
-}
-
-AudioDevices::~AudioDevices() = default;
-
-void AudioDevices::AddAndFireObserver(Observer* observer) {
-  DCHECK(observer);
-  observers_.AddObserver(observer);
-
-  observer->SetHotwordDeviceId(ToString(hotword_device_id_));
-  observer->SetDeviceId(ToString(device_id_));
-}
-
-void AudioDevices::RemoveObserver(Observer* observer) {
-  observers_.RemoveObserver(observer);
-}
-
-void AudioDevices::SetLocale(const std::string& locale) {
-  locale_ = locale;
-  UpdateHotwordModel();
-}
-
-void AudioDevices::SetAudioDevicesForTest(
-    const AudioDeviceList& audio_devices) {
-  SetAudioDevices(audio_devices);
-}
-
-void AudioDevices::SetAudioDevices(const AudioDeviceList& devices) {
-  UpdateHotwordDeviceId(devices);
-  UpdateDeviceId(devices);
-  UpdateHotwordModel();
-}
-
-void AudioDevices::UpdateHotwordDeviceId(const AudioDeviceList& devices) {
-  hotword_device_id_ = GetHotwordDeviceId(devices);
-
-  VLOG(2) << "Changed audio hotword input device to "
-          << ToString(hotword_device_id_).value_or("<none>");
-
-  for (auto& observer : observers_)
-    observer.SetHotwordDeviceId(ToString(hotword_device_id_));
-}
-
-void AudioDevices::UpdateDeviceId(const AudioDeviceList& devices) {
-  device_id_ = GetPreferredDeviceId(devices);
-
-  VLOG(2) << "Changed audio input device to "
-          << ToString(device_id_).value_or("<none>");
-
-  for (auto& observer : observers_)
-    observer.SetDeviceId(ToString(device_id_));
-}
-
-void AudioDevices::UpdateHotwordModel() {
-  if (!hotword_device_id_)
-    return;
-
-  if (!features::IsDspHotwordEnabled())
-    return;
-
-  hotword_model_updater_ = std::make_unique<HotwordModelUpdater>(
-      cras_audio_handler_, hotword_device_id_.value(), locale_);
-}
-
-}  // namespace ash::assistant
diff --git a/chromeos/ash/services/assistant/platform/audio_devices.h b/chromeos/ash/services/assistant/platform/audio_devices.h
deleted file mode 100644
index a51c773..0000000
--- a/chromeos/ash/services/assistant/platform/audio_devices.h
+++ /dev/null
@@ -1,105 +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.
-
-#ifndef CHROMEOS_ASH_SERVICES_ASSISTANT_PLATFORM_AUDIO_DEVICES_H_
-#define CHROMEOS_ASH_SERVICES_ASSISTANT_PLATFORM_AUDIO_DEVICES_H_
-
-#include <cstdint>
-#include <optional>
-
-#include "base/component_export.h"
-#include "base/memory/raw_ptr.h"
-#include "base/observer_list.h"
-#include "base/scoped_observation_traits.h"
-#include "chromeos/ash/components/audio/audio_device.h"
-
-namespace ash {
-
-class CrasAudioHandler;
-
-namespace assistant {
-
-// This class will monitor the available audio devices (through
-// |CrasAudioHandler|), and select the devices to use for audio input (both
-// regular input and hotword detection).
-// When the selected devices change, this class will:
-//     - Inform the observers.
-//     - Find the hotword model to use, and send it to
-//       CrasAudioHandler::SetHotwordModel().
-class COMPONENT_EXPORT(ASSISTANT_SERVICE) AudioDevices {
- public:
-  class Observer : public base::CheckedObserver {
-   public:
-    ~Observer() override = default;
-
-    // Set the input device to use for audio capture.
-    virtual void SetDeviceId(const std::optional<std::string>& device_id) = 0;
-    // Set the input device to use for hardware based hotword detection.
-    virtual void SetHotwordDeviceId(
-        const std::optional<std::string>& device_id) = 0;
-  };
-
-  AudioDevices(CrasAudioHandler* cras_audio_handler, const std::string& locale);
-  AudioDevices(const AudioDevices&) = delete;
-  AudioDevices& operator=(const AudioDevices&) = delete;
-  ~AudioDevices();
-
-  void AddAndFireObserver(Observer*);
-  void RemoveObserver(Observer*);
-
-  void SetLocale(const std::string& locale);
-
-  // Used during unittests to simulate an update to the list of available audio
-  // devices.
-  void SetAudioDevicesForTest(const AudioDeviceList& audio_devices);
-
- private:
-  class ScopedCrasAudioHandlerObserver;
-  class HotwordModelUpdater;
-
-  void SetAudioDevices(const AudioDeviceList& audio_devices);
-  void UpdateHotwordDeviceId(const AudioDeviceList& devices);
-  void UpdateDeviceId(const AudioDeviceList& devices);
-  void UpdateHotwordModel();
-
-  // Handles the asynchronous nature of sending a new hotword model to
-  // |cras_audio_handler_|.
-  std::unique_ptr<HotwordModelUpdater> hotword_model_updater_;
-
-  base::ObserverList<Observer> observers_;
-
-  // Owned by |AssistantManagerServiceImpl|.
-  const raw_ptr<CrasAudioHandler> cras_audio_handler_;
-
-  std::string locale_;
-  std::optional<uint64_t> hotword_device_id_;
-  std::optional<uint64_t> device_id_;
-
-  // Observes changes to the available audio devices, and sends the list of
-  // devices to SetAudioDevices().
-  std::unique_ptr<ScopedCrasAudioHandlerObserver>
-      scoped_cras_audio_handler_observer_;
-};
-
-}  // namespace assistant
-}  // namespace ash
-
-namespace base {
-
-template <>
-struct ScopedObservationTraits<ash::assistant::AudioDevices,
-                               ash::assistant::AudioDevices::Observer> {
-  static void AddObserver(ash::assistant::AudioDevices* source,
-                          ash::assistant::AudioDevices::Observer* observer) {
-    source->AddAndFireObserver(observer);
-  }
-  static void RemoveObserver(ash::assistant::AudioDevices* source,
-                             ash::assistant::AudioDevices::Observer* observer) {
-    source->RemoveObserver(observer);
-  }
-};
-
-}  // namespace base
-
-#endif  // CHROMEOS_ASH_SERVICES_ASSISTANT_PLATFORM_AUDIO_DEVICES_H_
diff --git a/chromeos/ash/services/assistant/platform/audio_devices_unittest.cc b/chromeos/ash/services/assistant/platform/audio_devices_unittest.cc
deleted file mode 100644
index a5b2982a..0000000
--- a/chromeos/ash/services/assistant/platform/audio_devices_unittest.cc
+++ /dev/null
@@ -1,433 +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.
-
-#include "chromeos/ash/services/assistant/platform/audio_devices.h"
-
-#include "base/run_loop.h"
-#include "base/test/scoped_feature_list.h"
-#include "base/test/task_environment.h"
-#include "chromeos/ash/components/audio/audio_device.h"
-#include "chromeos/ash/components/audio/cras_audio_handler.h"
-#include "chromeos/ash/components/dbus/audio/fake_cras_audio_client.h"
-#include "chromeos/ash/services/assistant/public/cpp/features.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace ash::assistant {
-
-namespace {
-
-using ::testing::_;
-
-constexpr const char kDefaultLocale[] = "en_us";
-
-class FakeAudioDevicesObserver : public AudioDevices::Observer {
- public:
-  FakeAudioDevicesObserver() = default;
-  FakeAudioDevicesObserver(const FakeAudioDevicesObserver&) = delete;
-  FakeAudioDevicesObserver& operator=(const FakeAudioDevicesObserver&) = delete;
-  ~FakeAudioDevicesObserver() override = default;
-
-  // AudioDevices::Observer implementation
-  void SetDeviceId(const std::optional<std::string>& device_id) override {
-    preferred_device_id_ = device_id;
-  }
-  void SetHotwordDeviceId(
-      const std::optional<std::string>& device_id) override {
-    hotword_device_id_ = device_id;
-  }
-
-  std::string preferred_device_id() {
-    return preferred_device_id_.value_or("<none>");
-  }
-
-  std::string hotword_device_id() {
-    return hotword_device_id_.value_or("<none>");
-  }
-
- private:
-  std::optional<std::string> preferred_device_id_;
-  std::optional<std::string> hotword_device_id_;
-};
-
-class DeviceBuilder {
- public:
-  explicit DeviceBuilder(AudioDeviceType type) {
-    result_.type = type;
-    result_.is_input = true;
-  }
-  DeviceBuilder(const DeviceBuilder&) = delete;
-  DeviceBuilder& operator=(const DeviceBuilder&) = delete;
-  ~DeviceBuilder() = default;
-
-  DeviceBuilder& WithId(int id) {
-    result_.id = id;
-    return *this;
-  }
-
-  DeviceBuilder& WithPriority(int priority) {
-    result_.priority = priority;
-    return *this;
-  }
-
-  DeviceBuilder& WithIsInput(bool is_input) {
-    result_.is_input = is_input;
-    return *this;
-  }
-
-  AudioDevice Build() const { return result_; }
-
- private:
-  AudioDevice result_;
-};
-
-// Mock for |CrosAudioClient|. This inherits from |FakeCrasAudioClient| so we
-// only have to mock the methods we're interested in.
-// It will automatically be installed as the global singleton in its
-// constructor, and removed in the destructor.
-class ScopedCrasAudioClientMock : public FakeCrasAudioClient {
- public:
-  ScopedCrasAudioClientMock() = default;
-  ScopedCrasAudioClientMock(ScopedCrasAudioClientMock&) = delete;
-  ScopedCrasAudioClientMock& operator=(ScopedCrasAudioClientMock&) = delete;
-  ~ScopedCrasAudioClientMock() override = default;
-
-  MOCK_METHOD(void,
-              SetHotwordModel,
-              (uint64_t node_id,
-               const std::string& hotword_model,
-               chromeos::VoidDBusMethodCallback callback));
-};
-
-class ScopedCrasAudioHandler {
- public:
-  ScopedCrasAudioHandler() { CrasAudioHandler::InitializeForTesting(); }
-  ScopedCrasAudioHandler(const ScopedCrasAudioHandler&) = delete;
-  ScopedCrasAudioHandler& operator=(const ScopedCrasAudioHandler&) = delete;
-  ~ScopedCrasAudioHandler() { CrasAudioHandler::Shutdown(); }
-
-  CrasAudioHandler* Get() { return CrasAudioHandler::Get(); }
-};
-
-}  // namespace
-
-class AssistantAudioDevicesTest : public testing::Test {
- public:
-  AssistantAudioDevicesTest()
-      : audio_devices_(cras_audio_handler_.Get(), "pref-locale") {
-    // Enable DSP feature flag.
-    scoped_feature_list_.InitAndEnableFeature(features::kEnableDspHotword);
-  }
-
-  AssistantAudioDevicesTest(const AssistantAudioDevicesTest&) = delete;
-  AssistantAudioDevicesTest& operator=(const AssistantAudioDevicesTest&) =
-      delete;
-  ~AssistantAudioDevicesTest() override = default;
-
-  AudioDevices& audio_devices() { return audio_devices_; }
-
-  ScopedCrasAudioClientMock& cras_audio_client_mock() {
-    return cras_audio_client_mock_;
-  }
-
-  void UpdateDeviceList(const AudioDeviceList& devices) {
-    audio_devices().SetAudioDevicesForTest(devices);
-  }
-
- private:
-  base::test::TaskEnvironment task_environment_;
-  base::test::ScopedFeatureList scoped_feature_list_;
-  testing::NiceMock<ScopedCrasAudioClientMock> cras_audio_client_mock_;
-  ScopedCrasAudioHandler cras_audio_handler_;
-  AudioDevices audio_devices_;
-};
-
-TEST_F(AssistantAudioDevicesTest, ShouldSendHotwordDeviceToObserver) {
-  FakeAudioDevicesObserver observer;
-  audio_devices().AddAndFireObserver(&observer);
-
-  UpdateDeviceList(
-      {DeviceBuilder(AudioDeviceType::kHotword).WithId(111).Build()});
-
-  EXPECT_EQ("111", observer.hotword_device_id());
-  EXPECT_EQ("<none>", observer.preferred_device_id());
-}
-
-TEST_F(AssistantAudioDevicesTest, ShouldSendMicDeviceToObserver) {
-  FakeAudioDevicesObserver observer;
-  audio_devices().AddAndFireObserver(&observer);
-
-  UpdateDeviceList({DeviceBuilder(AudioDeviceType::kMic).WithId(221).Build()});
-
-  EXPECT_EQ("<none>", observer.hotword_device_id());
-  EXPECT_EQ("221", observer.preferred_device_id());
-}
-
-TEST_F(AssistantAudioDevicesTest, ShouldSendUsbDeviceToObserver) {
-  FakeAudioDevicesObserver observer;
-  audio_devices().AddAndFireObserver(&observer);
-
-  UpdateDeviceList({DeviceBuilder(AudioDeviceType::kUsb).WithId(222).Build()});
-
-  EXPECT_EQ("<none>", observer.hotword_device_id());
-  EXPECT_EQ("222", observer.preferred_device_id());
-}
-
-TEST_F(AssistantAudioDevicesTest, ShouldSendHeadphonesDeviceToObserver) {
-  FakeAudioDevicesObserver observer;
-  audio_devices().AddAndFireObserver(&observer);
-
-  UpdateDeviceList(
-      {DeviceBuilder(AudioDeviceType::kHeadphone).WithId(333).Build()});
-
-  EXPECT_EQ("<none>", observer.hotword_device_id());
-  EXPECT_EQ("333", observer.preferred_device_id());
-}
-
-TEST_F(AssistantAudioDevicesTest, ShouldSendInternalMicDeviceToObserver) {
-  FakeAudioDevicesObserver observer;
-  audio_devices().AddAndFireObserver(&observer);
-
-  UpdateDeviceList(
-      {DeviceBuilder(AudioDeviceType::kInternalMic).WithId(444).Build()});
-
-  EXPECT_EQ("<none>", observer.hotword_device_id());
-  EXPECT_EQ("444", observer.preferred_device_id());
-}
-
-TEST_F(AssistantAudioDevicesTest, ShouldSendFrontMicDeviceToObserver) {
-  FakeAudioDevicesObserver observer;
-  audio_devices().AddAndFireObserver(&observer);
-
-  UpdateDeviceList(
-      {DeviceBuilder(AudioDeviceType::kFrontMic).WithId(555).Build()});
-
-  EXPECT_EQ("<none>", observer.hotword_device_id());
-  EXPECT_EQ("555", observer.preferred_device_id());
-}
-
-TEST_F(AssistantAudioDevicesTest, ShouldSendRearMicDeviceToObserver) {
-  FakeAudioDevicesObserver observer;
-  audio_devices().AddAndFireObserver(&observer);
-
-  UpdateDeviceList(
-      {DeviceBuilder(AudioDeviceType::kRearMic).WithId(666).Build()});
-
-  EXPECT_EQ("<none>", observer.hotword_device_id());
-  EXPECT_EQ("666", observer.preferred_device_id());
-}
-
-TEST_F(AssistantAudioDevicesTest, ShouldSendKeyboardMicDeviceToObserver) {
-  FakeAudioDevicesObserver observer;
-  audio_devices().AddAndFireObserver(&observer);
-
-  UpdateDeviceList(
-      {DeviceBuilder(AudioDeviceType::kKeyboardMic).WithId(777).Build()});
-
-  EXPECT_EQ("<none>", observer.hotword_device_id());
-  EXPECT_EQ("777", observer.preferred_device_id());
-}
-
-TEST_F(AssistantAudioDevicesTest, ShouldUseHighestPriorityHotwordDevice) {
-  FakeAudioDevicesObserver observer;
-  audio_devices().AddAndFireObserver(&observer);
-
-  UpdateDeviceList({
-      DeviceBuilder(AudioDeviceType::kHotword)
-          .WithId(111)
-          .WithPriority(1)
-          .Build(),
-      DeviceBuilder(AudioDeviceType::kHotword)
-          .WithId(555)
-          .WithPriority(5)
-          .Build(),
-      DeviceBuilder(AudioDeviceType::kHotword)
-          .WithId(222)
-          .WithPriority(2)
-          .Build(),
-  });
-
-  EXPECT_EQ("555", observer.hotword_device_id());
-  EXPECT_EQ("<none>", observer.preferred_device_id());
-}
-
-TEST_F(AssistantAudioDevicesTest, ShouldIgnoreNonInputHotwordDevices) {
-  FakeAudioDevicesObserver observer;
-  audio_devices().AddAndFireObserver(&observer);
-
-  UpdateDeviceList({
-      DeviceBuilder(AudioDeviceType::kHotword)
-          .WithId(111)
-          .WithIsInput(false)
-          .Build(),
-      DeviceBuilder(AudioDeviceType::kHotword)
-          .WithId(222)
-          .WithIsInput(true)
-          .Build(),
-      DeviceBuilder(AudioDeviceType::kHotword)
-          .WithId(333)
-          .WithIsInput(false)
-          .Build(),
-  });
-
-  EXPECT_EQ("222", observer.hotword_device_id());
-  EXPECT_EQ("<none>", observer.preferred_device_id());
-}
-
-TEST_F(AssistantAudioDevicesTest, ShouldUseHighestPriorityDevice) {
-  FakeAudioDevicesObserver observer;
-  audio_devices().AddAndFireObserver(&observer);
-
-  UpdateDeviceList({
-      DeviceBuilder(AudioDeviceType::kUsb).WithId(111).WithPriority(1).Build(),
-      DeviceBuilder(AudioDeviceType::kUsb).WithId(555).WithPriority(5).Build(),
-      DeviceBuilder(AudioDeviceType::kUsb).WithId(222).WithPriority(2).Build(),
-  });
-
-  EXPECT_EQ("<none>", observer.hotword_device_id());
-  EXPECT_EQ("555", observer.preferred_device_id());
-}
-
-TEST_F(AssistantAudioDevicesTest, ShouldIgnoreNonInputDevices) {
-  FakeAudioDevicesObserver observer;
-  audio_devices().AddAndFireObserver(&observer);
-
-  UpdateDeviceList({
-      DeviceBuilder(AudioDeviceType::kUsb)
-          .WithId(111)
-          .WithIsInput(false)
-          .Build(),
-      DeviceBuilder(AudioDeviceType::kUsb)
-          .WithId(222)
-          .WithIsInput(true)
-          .Build(),
-      DeviceBuilder(AudioDeviceType::kUsb)
-          .WithId(333)
-          .WithIsInput(false)
-          .Build(),
-  });
-
-  EXPECT_EQ("<none>", observer.hotword_device_id());
-  EXPECT_EQ("222", observer.preferred_device_id());
-}
-
-TEST_F(AssistantAudioDevicesTest, ShouldIgnoreUnsupportedDeviceTypes) {
-  FakeAudioDevicesObserver observer;
-  audio_devices().AddAndFireObserver(&observer);
-
-  UpdateDeviceList({
-      DeviceBuilder(AudioDeviceType::kBluetooth).WithId(2).Build(),
-      DeviceBuilder(AudioDeviceType::kBluetoothNbMic).WithId(3).Build(),
-      DeviceBuilder(AudioDeviceType::kHdmi).WithId(4).Build(),
-      DeviceBuilder(AudioDeviceType::kInternalSpeaker).WithId(5).Build(),
-      DeviceBuilder(AudioDeviceType::kLineout).WithId(8).Build(),
-      DeviceBuilder(AudioDeviceType::kPostMixLoopback).WithId(9).Build(),
-      DeviceBuilder(AudioDeviceType::kPostDspLoopback).WithId(10).Build(),
-      DeviceBuilder(AudioDeviceType::kAlsaLoopback).WithId(11).Build(),
-      DeviceBuilder(AudioDeviceType::kOther).WithId(12).Build(),
-  });
-
-  EXPECT_EQ("<none>", observer.hotword_device_id());
-  EXPECT_EQ("<none>", observer.preferred_device_id());
-}
-
-TEST_F(AssistantAudioDevicesTest, ShouldFireObserverWhenAdded) {
-  UpdateDeviceList({
-      DeviceBuilder(AudioDeviceType::kHotword).WithId(111).Build(),
-      DeviceBuilder(AudioDeviceType::kUsb).WithId(222).Build(),
-  });
-
-  FakeAudioDevicesObserver observer;
-  audio_devices().AddAndFireObserver(&observer);
-
-  EXPECT_EQ("111", observer.hotword_device_id());
-  EXPECT_EQ("222", observer.preferred_device_id());
-}
-
-TEST_F(AssistantAudioDevicesTest, ShouldNotFireObserverAfterItsRemoved) {
-  FakeAudioDevicesObserver observer;
-  audio_devices().AddAndFireObserver(&observer);
-  audio_devices().RemoveObserver(&observer);
-
-  UpdateDeviceList({
-      DeviceBuilder(AudioDeviceType::kHotword).WithId(111).Build(),
-      DeviceBuilder(AudioDeviceType::kUsb).WithId(222).Build(),
-  });
-
-  EXPECT_EQ("<none>", observer.hotword_device_id());
-  EXPECT_EQ("<none>", observer.preferred_device_id());
-}
-
-TEST_F(AssistantAudioDevicesTest,
-       ShouldUpdateHotwordModelWhenHotwordDeviceIsAdded) {
-  EXPECT_CALL(cras_audio_client_mock(), SetHotwordModel);
-
-  UpdateDeviceList({DeviceBuilder(AudioDeviceType::kHotword).Build()});
-}
-
-TEST_F(AssistantAudioDevicesTest, ShouldFormatLocaleToHotwordModel) {
-  UpdateDeviceList(
-      {DeviceBuilder(AudioDeviceType::kHotword).WithId(111).Build()});
-
-  // Normal case
-  EXPECT_CALL(cras_audio_client_mock(), SetHotwordModel(111, "nl_be", _));
-  audio_devices().SetLocale("nl-BE");
-  base::RunLoop().RunUntilIdle();
-
-  // Handle the case where country code and language code are the same
-  EXPECT_CALL(cras_audio_client_mock(), SetHotwordModel(111, "fr_fr", _));
-  audio_devices().SetLocale("fr");
-  base::RunLoop().RunUntilIdle();
-
-  // use "en_all" for all english locales
-  EXPECT_CALL(cras_audio_client_mock(), SetHotwordModel(111, "en_all", _));
-  audio_devices().SetLocale("en-US");
-  base::RunLoop().RunUntilIdle();
-}
-
-TEST_F(AssistantAudioDevicesTest, ShouldUseDefaultLocaleIfUserPrefIsRejected) {
-  UpdateDeviceList(
-      {DeviceBuilder(AudioDeviceType::kHotword).WithId(222).Build()});
-
-  EXPECT_CALL(cras_audio_client_mock(),
-              SetHotwordModel(_, "rejected_locale", _))
-      .WillOnce([](uint64_t node_id, const std::string&,
-                   chromeos::VoidDBusMethodCallback callback) {
-        // Report failure to change the locale
-        std::move(callback).Run(/*success=*/false);
-      });
-
-  EXPECT_CALL(cras_audio_client_mock(),
-              SetHotwordModel(222, kDefaultLocale, _));
-
-  audio_devices().SetLocale("rejected-LOCALE");
-}
-
-TEST_F(AssistantAudioDevicesTest, ShouldUseDefaultLocaleIfUserPrefIsEmpty) {
-  UpdateDeviceList({DeviceBuilder(AudioDeviceType::kHotword).Build()});
-
-  EXPECT_CALL(cras_audio_client_mock(), SetHotwordModel(_, kDefaultLocale, _));
-
-  audio_devices().SetLocale("");
-}
-
-TEST_F(AssistantAudioDevicesTest, ShouldDoNothingIfUserPrefIsAccepted) {
-  UpdateDeviceList({DeviceBuilder(AudioDeviceType::kHotword).Build()});
-
-  EXPECT_CALL(cras_audio_client_mock(),
-              SetHotwordModel(_, "accepted_locale", _))
-      .WillOnce([](uint64_t node_id, const std::string&,
-                   chromeos::VoidDBusMethodCallback callback) {
-        // Accept the change to the locale.
-        std::move(callback).Run(/*success=*/true);
-      });
-
-  // Do not expect a second call if change of locale is accepted
-  EXPECT_CALL(cras_audio_client_mock(), SetHotwordModel(_, kDefaultLocale, _))
-      .Times(0);
-
-  audio_devices().SetLocale("accepted-LOCALE");
-}
-
-}  // namespace ash::assistant
diff --git a/chromeos/ash/services/assistant/platform/audio_input_host.h b/chromeos/ash/services/assistant/platform/audio_input_host.h
deleted file mode 100644
index bb3c023b..0000000
--- a/chromeos/ash/services/assistant/platform/audio_input_host.h
+++ /dev/null
@@ -1,27 +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.
-
-#ifndef CHROMEOS_ASH_SERVICES_ASSISTANT_PLATFORM_AUDIO_INPUT_HOST_H_
-#define CHROMEOS_ASH_SERVICES_ASSISTANT_PLATFORM_AUDIO_INPUT_HOST_H_
-
-namespace ash::assistant {
-
-// Class that provides the bridge between the ChromeOS UI thread and the
-// Libassistant audio input class.
-class AudioInputHost {
- public:
-  virtual ~AudioInputHost() = default;
-
-  // Called when the mic state associated with the interaction is changed.
-  virtual void SetMicState(bool mic_open) = 0;
-
-  // Called when hotword enabled status changed.
-  virtual void OnHotwordEnabled(bool enable) = 0;
-
-  virtual void OnConversationTurnStarted() = 0;
-};
-
-}  // namespace ash::assistant
-
-#endif  // CHROMEOS_ASH_SERVICES_ASSISTANT_PLATFORM_AUDIO_INPUT_HOST_H_
diff --git a/chromeos/ash/services/assistant/platform/audio_input_host_impl.cc b/chromeos/ash/services/assistant/platform/audio_input_host_impl.cc
deleted file mode 100644
index 8002d25..0000000
--- a/chromeos/ash/services/assistant/platform/audio_input_host_impl.cc
+++ /dev/null
@@ -1,97 +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.
-
-#include "chromeos/ash/services/assistant/platform/audio_input_host_impl.h"
-
-#include <optional>
-
-#include "base/check.h"
-#include "chromeos/ash/services/assistant/platform/audio_devices.h"
-#include "chromeos/ash/services/assistant/public/cpp/assistant_browser_delegate.h"
-#include "chromeos/ash/services/assistant/public/cpp/features.h"
-
-namespace ash::assistant {
-
-namespace {
-
-using MojomLidState = libassistant::mojom::LidState;
-
-MojomLidState ConvertLidState(chromeos::PowerManagerClient::LidState state) {
-  switch (state) {
-    case chromeos::PowerManagerClient::LidState::CLOSED:
-      return MojomLidState::kClosed;
-    case chromeos::PowerManagerClient::LidState::OPEN:
-      return MojomLidState::kOpen;
-    case chromeos::PowerManagerClient::LidState::NOT_PRESENT:
-      // If there is no lid, it can't be closed.
-      return MojomLidState::kOpen;
-  }
-}
-
-}  // namespace
-
-AudioInputHostImpl::AudioInputHostImpl(
-    mojo::PendingRemote<libassistant::mojom::AudioInputController>
-        pending_remote,
-    CrasAudioHandler* cras_audio_handler,
-    chromeos::PowerManagerClient* power_manager_client,
-    const std::string& locale)
-    : remote_(std::move(pending_remote)),
-      power_manager_client_(power_manager_client),
-      power_manager_client_observer_(this),
-      audio_devices_(cras_audio_handler, locale) {
-  DCHECK(power_manager_client_);
-
-  audio_devices_observation_.Observe(&audio_devices_);
-  power_manager_client_observer_.Observe(power_manager_client_.get());
-  power_manager_client_->GetSwitchStates(
-      base::BindOnce(&AudioInputHostImpl::OnInitialLidStateReceived,
-                     weak_factory_.GetWeakPtr()));
-}
-
-AudioInputHostImpl::~AudioInputHostImpl() = default;
-
-void AudioInputHostImpl::SetMicState(bool mic_open) {
-  remote_->SetMicOpen(mic_open);
-}
-
-void AudioInputHostImpl::SetDeviceId(
-    const std::optional<std::string>& device_id) {
-  remote_->SetDeviceId(device_id);
-}
-
-void AudioInputHostImpl::OnConversationTurnStarted() {
-  remote_->OnConversationTurnStarted();
-  // Inform power manager of a wake notification when Libassistant
-  // recognized hotword and started a conversation. We intentionally
-  // avoid using |NotifyUserActivity| because it is not suitable for
-  // this case according to the Platform team.
-  power_manager_client_->NotifyWakeNotification();
-}
-
-void AudioInputHostImpl::OnHotwordEnabled(bool enable) {
-  remote_->SetHotwordEnabled(enable);
-}
-
-void AudioInputHostImpl::SetHotwordDeviceId(
-    const std::optional<std::string>& device_id) {
-  remote_->SetHotwordDeviceId(device_id);
-}
-
-void AudioInputHostImpl::LidEventReceived(
-    chromeos::PowerManagerClient::LidState state,
-    base::TimeTicks timestamp) {
-  // Lid switch event still gets fired during system suspend, which enables
-  // us to stop DSP recording correctly when user closes lid after the device
-  // goes to sleep.
-  remote_->SetLidState(ConvertLidState(state));
-}
-
-void AudioInputHostImpl::OnInitialLidStateReceived(
-    std::optional<chromeos::PowerManagerClient::SwitchStates> switch_states) {
-  if (switch_states.has_value())
-    remote_->SetLidState(ConvertLidState(switch_states->lid_state));
-}
-
-}  // namespace ash::assistant
diff --git a/chromeos/ash/services/assistant/platform/audio_input_host_impl.h b/chromeos/ash/services/assistant/platform/audio_input_host_impl.h
deleted file mode 100644
index 10d6708..0000000
--- a/chromeos/ash/services/assistant/platform/audio_input_host_impl.h
+++ /dev/null
@@ -1,75 +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.
-
-#ifndef CHROMEOS_ASH_SERVICES_ASSISTANT_PLATFORM_AUDIO_INPUT_HOST_IMPL_H_
-#define CHROMEOS_ASH_SERVICES_ASSISTANT_PLATFORM_AUDIO_INPUT_HOST_IMPL_H_
-
-#include <optional>
-#include <string>
-
-#include "base/component_export.h"
-#include "base/memory/raw_ptr.h"
-#include "base/memory/weak_ptr.h"
-#include "base/scoped_observation.h"
-#include "base/time/time.h"
-#include "chromeos/ash/services/assistant/platform/audio_devices.h"
-#include "chromeos/ash/services/assistant/platform/audio_input_host.h"
-#include "chromeos/ash/services/libassistant/public/mojom/audio_input_controller.mojom.h"
-#include "chromeos/dbus/power/power_manager_client.h"
-#include "mojo/public/cpp/bindings/remote.h"
-
-namespace ash::assistant {
-
-// Class that provides the bridge between the ChromeOS Browser thread and the
-// Libassistant audio input mojom service.
-class COMPONENT_EXPORT(ASSISTANT_SERVICE) AudioInputHostImpl
-    : public AudioInputHost,
-      private chromeos::PowerManagerClient::Observer,
-      private AudioDevices::Observer {
- public:
-  AudioInputHostImpl(
-      mojo::PendingRemote<libassistant::mojom::AudioInputController>
-          pending_remote,
-      CrasAudioHandler* cras_audio_handler,
-      chromeos::PowerManagerClient* power_manager_client,
-      const std::string& locale);
-  AudioInputHostImpl(const AudioInputHost&) = delete;
-  AudioInputHostImpl& operator=(const AudioInputHostImpl&) = delete;
-  ~AudioInputHostImpl() override;
-
-  // AudioInputHost implementation:
-  void SetMicState(bool mic_open) override;
-  void OnHotwordEnabled(bool enable) override;
-  void OnConversationTurnStarted() override;
-
-  // AudioDevices::Observer implementation:
-  void SetDeviceId(const std::optional<std::string>& device_id) override;
-  void SetHotwordDeviceId(const std::optional<std::string>& device_id) override;
-
- private:
-  // chromeos::PowerManagerClient::Observer overrides:
-  void LidEventReceived(chromeos::PowerManagerClient::LidState state,
-                        base::TimeTicks timestamp) override;
-
-  void OnInitialLidStateReceived(
-      std::optional<chromeos::PowerManagerClient::SwitchStates> switch_states);
-
-  mojo::Remote<libassistant::mojom::AudioInputController> remote_;
-  const raw_ptr<chromeos::PowerManagerClient> power_manager_client_;
-  base::ScopedObservation<chromeos::PowerManagerClient,
-                          chromeos::PowerManagerClient::Observer>
-      power_manager_client_observer_;
-
-  // Observes available audio devices and will set device-id/hotword-device-id
-  // accordingly.
-  AudioDevices audio_devices_;
-  base::ScopedObservation<AudioDevices, AudioDevices::Observer>
-      audio_devices_observation_{this};
-
-  base::WeakPtrFactory<AudioInputHostImpl> weak_factory_{this};
-};
-
-}  // namespace ash::assistant
-
-#endif  // CHROMEOS_ASH_SERVICES_ASSISTANT_PLATFORM_AUDIO_INPUT_HOST_IMPL_H_
diff --git a/chromeos/ash/services/assistant/platform/audio_input_host_unittest.cc b/chromeos/ash/services/assistant/platform/audio_input_host_unittest.cc
deleted file mode 100644
index 7c37ec88..0000000
--- a/chromeos/ash/services/assistant/platform/audio_input_host_unittest.cc
+++ /dev/null
@@ -1,228 +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.
-
-#include <optional>
-
-#include "base/run_loop.h"
-#include "base/test/mock_callback.h"
-#include "base/test/scoped_feature_list.h"
-#include "base/test/task_environment.h"
-#include "chromeos/ash/components/audio/cras_audio_handler.h"
-#include "chromeos/ash/services/assistant/platform/audio_input_host_impl.h"
-#include "chromeos/ash/services/assistant/public/cpp/features.h"
-#include "chromeos/ash/services/libassistant/public/mojom/audio_input_controller.mojom.h"
-#include "chromeos/dbus/power/fake_power_manager_client.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace ash::assistant {
-
-namespace {
-
-using LidState = chromeos::PowerManagerClient::LidState;
-using MojomLidState = libassistant::mojom::LidState;
-using MojomAudioInputController = libassistant::mojom::AudioInputController;
-using ::testing::_;
-using ::testing::NiceMock;
-
-class AudioInputControllerMock : public MojomAudioInputController {
- public:
-  AudioInputControllerMock() = default;
-  AudioInputControllerMock(const AudioInputControllerMock&) = delete;
-  AudioInputControllerMock& operator=(const AudioInputControllerMock&) = delete;
-  ~AudioInputControllerMock() override = default;
-
-  mojo::PendingRemote<MojomAudioInputController> BindNewPipeAndPassRemote() {
-    receiver_.reset();
-    return receiver_.BindNewPipeAndPassRemote();
-  }
-
-  MOCK_METHOD(void, SetMicOpen, (bool mic_open));
-  MOCK_METHOD(void, SetHotwordEnabled, (bool enable));
-  MOCK_METHOD(void, SetDeviceId, (const std::optional<std::string>& device_id));
-  MOCK_METHOD(void,
-              SetHotwordDeviceId,
-              (const std::optional<std::string>& device_id));
-  MOCK_METHOD(void, SetLidState, (MojomLidState new_state));
-  MOCK_METHOD(void, OnConversationTurnStarted, ());
-
- private:
-  mojo::Receiver<MojomAudioInputController> receiver_{this};
-};
-
-class AssistantAudioInputHostTest : public testing::Test {
- public:
-  AssistantAudioInputHostTest() {
-    chromeos::PowerManagerClient::InitializeFake();
-  }
-
-  AssistantAudioInputHostTest(const AssistantAudioInputHostTest&) = delete;
-  AssistantAudioInputHostTest& operator=(const AssistantAudioInputHostTest&) =
-      delete;
-  ~AssistantAudioInputHostTest() override {
-    // |audio_input_host_| uses the fake power manager client, so must be
-    // destroyed before the power manager client.
-    audio_input_host_.reset();
-    chromeos::PowerManagerClient::Shutdown();
-  }
-
-  void SetUp() override {
-    // Enable DSP Hotword
-    scoped_feature_list_.InitAndEnableFeature(features::kEnableDspHotword);
-
-    CreateNewAudioInputHost();
-  }
-
-  AudioInputControllerMock& mojom_audio_input_controller() {
-    return audio_input_controller_;
-  }
-
-  AudioInputHostImpl& audio_input_host() {
-    CHECK(audio_input_host_);
-    return *audio_input_host_;
-  }
-
-  void CreateNewAudioInputHost() {
-    audio_input_host_ = std::make_unique<AudioInputHostImpl>(
-        audio_input_controller_.BindNewPipeAndPassRemote(),
-        &cras_audio_handler_.Get(), chromeos::FakePowerManagerClient::Get(),
-        "default-locale");
-
-    FlushPendingMojomCalls();
-  }
-
-  void DestroyAudioInputHost() { audio_input_host_ = nullptr; }
-
-  void ReportLidEvent(LidState state) {
-    chromeos::FakePowerManagerClient::Get()->SetLidState(
-        state, base::TimeTicks::UnixEpoch());
-    FlushPendingMojomCalls();
-  }
-
-  void SetLidState(LidState state) { ReportLidEvent(state); }
-
-  void SetDeviceId(const std::optional<std::string>& device_id) {
-    audio_input_host().SetDeviceId(device_id);
-    FlushPendingMojomCalls();
-  }
-
-  void SetHotwordDeviceId(const std::optional<std::string>& device_id) {
-    audio_input_host().SetHotwordDeviceId(device_id);
-    FlushPendingMojomCalls();
-  }
-
-  void OnHotwordEnabled(bool enabled) {
-    audio_input_host().OnHotwordEnabled(enabled);
-    FlushPendingMojomCalls();
-  }
-
-  void SetMicState(bool mic_open) {
-    audio_input_host().SetMicState(mic_open);
-    FlushPendingMojomCalls();
-  }
-
-  void OnConversationTurnStarted() {
-    audio_input_host().OnConversationTurnStarted();
-    FlushPendingMojomCalls();
-  }
-
-  void FlushPendingMojomCalls() { base::RunLoop().RunUntilIdle(); }
-
- private:
-  base::test::TaskEnvironment task_environment_;
-  base::test::ScopedFeatureList scoped_feature_list_;
-  ScopedCrasAudioHandlerForTesting cras_audio_handler_;
-  NiceMock<AudioInputControllerMock> audio_input_controller_;
-  std::unique_ptr<AudioInputHostImpl> audio_input_host_;
-};
-
-}  // namespace
-
-TEST_F(AssistantAudioInputHostTest, ShouldSendLidOpenEventsToMojom) {
-  EXPECT_CALL(mojom_audio_input_controller(),
-              SetLidState(MojomLidState::kOpen));
-  ReportLidEvent(LidState::OPEN);
-}
-
-TEST_F(AssistantAudioInputHostTest, ShouldSendLidClosedEventsToMojom) {
-  EXPECT_CALL(mojom_audio_input_controller(),
-              SetLidState(MojomLidState::kClosed));
-  ReportLidEvent(LidState::CLOSED);
-}
-
-TEST_F(AssistantAudioInputHostTest, ShouldSendLidNotPresentEventsToMojom) {
-  // If there is no lid it can not be closed by the user so we consider it to be
-  // open.
-  EXPECT_CALL(mojom_audio_input_controller(),
-              SetLidState(MojomLidState::kOpen));
-  ReportLidEvent(LidState::NOT_PRESENT);
-}
-
-TEST_F(AssistantAudioInputHostTest, ShouldReadCurrentLidStateWhenLaunching) {
-  DestroyAudioInputHost();
-  SetLidState(LidState::OPEN);
-  EXPECT_CALL(mojom_audio_input_controller(),
-              SetLidState(MojomLidState::kOpen));
-  CreateNewAudioInputHost();
-
-  DestroyAudioInputHost();
-  SetLidState(LidState::CLOSED);
-  EXPECT_CALL(mojom_audio_input_controller(),
-              SetLidState(MojomLidState::kClosed));
-  CreateNewAudioInputHost();
-}
-
-TEST_F(AssistantAudioInputHostTest, ShouldSendDeviceIdToMojom) {
-  EXPECT_CALL(mojom_audio_input_controller(),
-              SetDeviceId(std::optional<std::string>("device-id")));
-  SetDeviceId("device-id");
-}
-
-TEST_F(AssistantAudioInputHostTest, ShouldUnsetDeviceIdWhenItsEmpty) {
-  // Note this variable is required as directly passing std::nullopt into the
-  // EXPECT_CALL doesn't compile.
-  const std::optional<std::string> expected = std::nullopt;
-  EXPECT_CALL(mojom_audio_input_controller(), SetDeviceId(expected));
-
-  SetDeviceId(std::nullopt);
-}
-
-TEST_F(AssistantAudioInputHostTest, ShouldSendHotwordDeviceIdToMojom) {
-  EXPECT_CALL(
-      mojom_audio_input_controller(),
-      SetHotwordDeviceId(std::optional<std::string>("hotword-device-id")));
-  SetHotwordDeviceId("hotword-device-id");
-}
-
-TEST_F(AssistantAudioInputHostTest, ShouldUnsetHotwordDeviceIdWhenItsEmpty) {
-  // Note this variable is required as directly passing std::nullopt into the
-  // EXPECT_CALL doesn't compile.
-  const std::optional<std::string> expected = std::nullopt;
-  EXPECT_CALL(mojom_audio_input_controller(), SetHotwordDeviceId(expected));
-
-  SetHotwordDeviceId(std::nullopt);
-}
-TEST_F(AssistantAudioInputHostTest, ShouldSendHotwordEnabledToMojom) {
-  EXPECT_CALL(mojom_audio_input_controller(), SetHotwordEnabled(true));
-  OnHotwordEnabled(true);
-
-  EXPECT_CALL(mojom_audio_input_controller(), SetHotwordEnabled(false));
-  OnHotwordEnabled(false);
-}
-
-TEST_F(AssistantAudioInputHostTest, ShouldSendMicOpenToMojom) {
-  EXPECT_CALL(mojom_audio_input_controller(), SetMicOpen(true));
-  SetMicState(/*mic_open=*/true);
-
-  EXPECT_CALL(mojom_audio_input_controller(), SetMicOpen(false));
-  SetMicState(/*mic_open=*/false);
-}
-
-TEST_F(AssistantAudioInputHostTest,
-       ShouldSendOnConversationTurnStartedToMojom) {
-  EXPECT_CALL(mojom_audio_input_controller(), OnConversationTurnStarted);
-  OnConversationTurnStarted();
-}
-
-}  // namespace ash::assistant
diff --git a/chromeos/ash/services/assistant/platform/audio_output_delegate_impl.cc b/chromeos/ash/services/assistant/platform/audio_output_delegate_impl.cc
deleted file mode 100644
index 2045798..0000000
--- a/chromeos/ash/services/assistant/platform/audio_output_delegate_impl.cc
+++ /dev/null
@@ -1,56 +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.
-
-#include "chromeos/ash/services/assistant/platform/audio_output_delegate_impl.h"
-#include "chromeos/ash/services/assistant/media_session/assistant_media_session.h"
-
-namespace ash::assistant {
-
-AudioOutputDelegateImpl::AudioOutputDelegateImpl(
-    AssistantMediaSession* media_session)
-    : media_session_(media_session) {}
-
-AudioOutputDelegateImpl::~AudioOutputDelegateImpl() = default;
-
-void AudioOutputDelegateImpl::Bind(
-    mojo::PendingReceiver<AudioOutputDelegate> pending_receiver) {
-  receiver_.Bind(std::move(pending_receiver));
-}
-
-void AudioOutputDelegateImpl::Stop() {
-  receiver_.reset();
-}
-
-void AudioOutputDelegateImpl::RequestAudioFocus(
-    libassistant::mojom::AudioOutputStreamType stream_type) {
-  // TODO(wutao): Fix the libassistant behavior.
-  // Currently this is called with |STREAM_TTS| and |STREAM_ALARM| when
-  // requesting focus. When releasing focus it calls with |STREAM_MEDIA|.
-  // libassistant media code path does not request focus.
-  switch (stream_type) {
-    case libassistant::mojom::AudioOutputStreamType::kAlarmStream:
-      media_session_->RequestAudioFocus(
-          media_session::mojom::AudioFocusType::kGainTransientMayDuck);
-      break;
-    case libassistant::mojom::AudioOutputStreamType::kTtsStream:
-      media_session_->RequestAudioFocus(
-          media_session::mojom::AudioFocusType::kGainTransient);
-      break;
-    case libassistant::mojom::AudioOutputStreamType::kMediaStream:
-      media_session_->AbandonAudioFocusIfNeeded();
-      break;
-  }
-}
-
-void AudioOutputDelegateImpl::AbandonAudioFocusIfNeeded() {
-  media_session_->AbandonAudioFocusIfNeeded();
-}
-
-void AudioOutputDelegateImpl::AddMediaSessionObserver(
-    mojo::PendingRemote<::media_session::mojom::MediaSessionObserver>
-        observer) {
-  media_session_->AddObserver(std::move(observer));
-}
-
-}  // namespace ash::assistant
diff --git a/chromeos/ash/services/assistant/platform/audio_output_delegate_impl.h b/chromeos/ash/services/assistant/platform/audio_output_delegate_impl.h
deleted file mode 100644
index 59450826..0000000
--- a/chromeos/ash/services/assistant/platform/audio_output_delegate_impl.h
+++ /dev/null
@@ -1,44 +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.
-
-#ifndef CHROMEOS_ASH_SERVICES_ASSISTANT_PLATFORM_AUDIO_OUTPUT_DELEGATE_IMPL_H_
-#define CHROMEOS_ASH_SERVICES_ASSISTANT_PLATFORM_AUDIO_OUTPUT_DELEGATE_IMPL_H_
-
-#include "base/memory/raw_ptr.h"
-#include "chromeos/ash/services/libassistant/public/mojom/audio_output_delegate.mojom.h"
-
-#include "mojo/public/cpp/bindings/pending_remote.h"
-#include "mojo/public/cpp/bindings/receiver.h"
-
-namespace ash::assistant {
-
-class AssistantMediaSession;
-
-class AudioOutputDelegateImpl
-    : public libassistant::mojom::AudioOutputDelegate {
- public:
-  explicit AudioOutputDelegateImpl(AssistantMediaSession* media_session);
-  AudioOutputDelegateImpl(const AudioOutputDelegateImpl&) = delete;
-  AudioOutputDelegateImpl& operator=(const AudioOutputDelegateImpl&) = delete;
-  ~AudioOutputDelegateImpl() override;
-
-  void Bind(mojo::PendingReceiver<AudioOutputDelegate> pending_receiver);
-  void Stop();
-
-  // libassistant::mojom::AudioOutputDelegate implementation:
-  void RequestAudioFocus(
-      libassistant::mojom::AudioOutputStreamType stream_type) override;
-  void AbandonAudioFocusIfNeeded() override;
-  void AddMediaSessionObserver(
-      mojo::PendingRemote<::media_session::mojom::MediaSessionObserver>
-          observer) override;
-
- private:
-  mojo::Receiver<AudioOutputDelegate> receiver_{this};
-  const raw_ptr<AssistantMediaSession> media_session_;
-};
-
-}  // namespace ash::assistant
-
-#endif  // CHROMEOS_ASH_SERVICES_ASSISTANT_PLATFORM_AUDIO_OUTPUT_DELEGATE_IMPL_H_
diff --git a/chromeos/ash/services/assistant/platform/platform_delegate_impl.cc b/chromeos/ash/services/assistant/platform/platform_delegate_impl.cc
deleted file mode 100644
index bd214d3..0000000
--- a/chromeos/ash/services/assistant/platform/platform_delegate_impl.cc
+++ /dev/null
@@ -1,56 +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.
-
-#include "chromeos/ash/services/assistant/platform/platform_delegate_impl.h"
-#include "chromeos/ash/services/assistant/public/cpp/assistant_browser_delegate.h"
-
-namespace ash::assistant {
-
-PlatformDelegateImpl::~PlatformDelegateImpl() = default;
-PlatformDelegateImpl::PlatformDelegateImpl() = default;
-
-void PlatformDelegateImpl::Bind(
-    mojo::PendingReceiver<PlatformDelegate> pending_receiver) {
-  receiver_.Bind(std::move(pending_receiver));
-}
-
-void PlatformDelegateImpl::Stop() {
-  receiver_.reset();
-}
-
-void PlatformDelegateImpl::BindAudioStreamFactory(
-    mojo::PendingReceiver<media::mojom::AudioStreamFactory> receiver) {
-  AssistantBrowserDelegate::Get()->RequestAudioStreamFactory(
-      std::move(receiver));
-}
-
-void PlatformDelegateImpl::BindAudioDecoderFactory(
-    mojo::PendingReceiver<mojom::AssistantAudioDecoderFactory> receiver) {
-  AssistantBrowserDelegate::Get()->RequestAudioDecoderFactory(
-      std::move(receiver));
-}
-
-void PlatformDelegateImpl::BindBatteryMonitor(
-    mojo::PendingReceiver<::device::mojom::BatteryMonitor> receiver) {
-  AssistantBrowserDelegate::Get()->RequestBatteryMonitor(std::move(receiver));
-}
-
-void PlatformDelegateImpl::BindNetworkConfig(
-    mojo::PendingReceiver<chromeos::network_config::mojom::CrosNetworkConfig>
-        receiver) {
-  AssistantBrowserDelegate::Get()->RequestNetworkConfig(std::move(receiver));
-}
-
-void PlatformDelegateImpl::BindAssistantVolumeControl(
-    mojo::PendingReceiver<::ash::mojom::AssistantVolumeControl> receiver) {
-  AssistantBrowserDelegate::Get()->RequestAssistantVolumeControl(
-      std::move(receiver));
-}
-
-void PlatformDelegateImpl::BindWakeLockProvider(
-    mojo::PendingReceiver<::device::mojom::WakeLockProvider> receiver) {
-  AssistantBrowserDelegate::Get()->RequestWakeLockProvider(std::move(receiver));
-}
-
-}  // namespace ash::assistant
diff --git a/chromeos/ash/services/assistant/platform/platform_delegate_impl.h b/chromeos/ash/services/assistant/platform/platform_delegate_impl.h
deleted file mode 100644
index 3372099..0000000
--- a/chromeos/ash/services/assistant/platform/platform_delegate_impl.h
+++ /dev/null
@@ -1,49 +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.
-
-#ifndef CHROMEOS_ASH_SERVICES_ASSISTANT_PLATFORM_PLATFORM_DELEGATE_IMPL_H_
-#define CHROMEOS_ASH_SERVICES_ASSISTANT_PLATFORM_PLATFORM_DELEGATE_IMPL_H_
-
-#include "chromeos/ash/services/libassistant/public/mojom/platform_delegate.mojom.h"
-#include "mojo/public/cpp/bindings/receiver.h"
-
-namespace ash::assistant {
-
-// Delegate that will fetch all instances from the |AssistantBrowserDelegate|.
-class PlatformDelegateImpl : public libassistant::mojom::PlatformDelegate {
- public:
-  PlatformDelegateImpl();
-  PlatformDelegateImpl(const PlatformDelegateImpl&) = delete;
-  PlatformDelegateImpl& operator=(const PlatformDelegateImpl&) = delete;
-  ~PlatformDelegateImpl() override;
-
-  void Bind(mojo::PendingReceiver<PlatformDelegate> pending_receiver);
-  void Stop();
-
-  // libassistant::mojom::PlatformDelegate implementation:
-  void BindAudioStreamFactory(
-      mojo::PendingReceiver<media::mojom::AudioStreamFactory> receiver)
-      override;
-  void BindAudioDecoderFactory(
-      mojo::PendingReceiver<assistant::mojom::AssistantAudioDecoderFactory>
-          receiver) override;
-  void BindBatteryMonitor(
-      mojo::PendingReceiver<::device::mojom::BatteryMonitor> receiver) override;
-  void BindNetworkConfig(
-      mojo::PendingReceiver<chromeos::network_config::mojom::CrosNetworkConfig>
-          receiver) override;
-  void BindAssistantVolumeControl(
-      mojo::PendingReceiver<::ash::mojom::AssistantVolumeControl> receiver)
-      override;
-  void BindWakeLockProvider(
-      mojo::PendingReceiver<::device::mojom::WakeLockProvider> receiver)
-      override;
-
- private:
-  mojo::Receiver<PlatformDelegate> receiver_{this};
-};
-
-}  // namespace ash::assistant
-
-#endif  // CHROMEOS_ASH_SERVICES_ASSISTANT_PLATFORM_PLATFORM_DELEGATE_IMPL_H_
diff --git a/chromeos/ash/services/assistant/service.cc b/chromeos/ash/services/assistant/service.cc
deleted file mode 100644
index 53d115e..0000000
--- a/chromeos/ash/services/assistant/service.cc
+++ /dev/null
@@ -1,812 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chromeos/ash/services/assistant/service.h"
-
-#include <algorithm>
-#include <memory>
-#include <optional>
-#include <utility>
-
-#include "ash/constants/ash_features.h"
-#include "ash/constants/ash_switches.h"
-#include "ash/public/cpp/assistant/assistant_state.h"
-#include "ash/public/cpp/assistant/controller/assistant_alarm_timer_controller.h"
-#include "ash/public/cpp/assistant/controller/assistant_controller.h"
-#include "ash/public/cpp/assistant/controller/assistant_notification_controller.h"
-#include "ash/public/cpp/session/session_controller.h"
-#include "base/command_line.h"
-#include "base/functional/bind.h"
-#include "base/logging.h"
-#include "base/memory/raw_ptr.h"
-#include "base/metrics/histogram_functions.h"
-#include "base/rand_util.h"
-#include "base/task/sequenced_task_runner.h"
-#include "base/task/single_thread_task_runner.h"
-#include "base/time/time.h"
-#include "base/timer/timer.h"
-#include "build/buildflag.h"
-#include "chromeos/ash/components/assistant/buildflags.h"
-#include "chromeos/ash/components/audio/cras_audio_handler.h"
-#include "chromeos/ash/components/dbus/dbus_thread_manager.h"
-#include "chromeos/ash/services/assistant/assistant_interaction_logger.h"
-#include "chromeos/ash/services/assistant/assistant_manager_service.h"
-#include "chromeos/ash/services/assistant/assistant_manager_service_impl.h"
-#include "chromeos/ash/services/assistant/public/cpp/assistant_browser_delegate.h"
-#include "chromeos/ash/services/assistant/public/cpp/assistant_prefs.h"
-#include "chromeos/ash/services/assistant/public/cpp/device_actions.h"
-#include "chromeos/ash/services/assistant/public/cpp/features.h"
-#include "chromeos/ash/services/assistant/service_context.h"
-#include "chromeos/ash/services/libassistant/public/cpp/libassistant_loader.h"
-#include "chromeos/dbus/power_manager/power_supply_properties.pb.h"
-#include "components/account_id/account_id.h"
-#include "components/prefs/pref_service.h"
-#include "components/signin/public/base/consent_level.h"
-#include "components/signin/public/identity_manager/access_token_fetcher.h"
-#include "components/signin/public/identity_manager/access_token_info.h"
-#include "components/signin/public/identity_manager/identity_manager.h"
-#include "components/signin/public/identity_manager/scope_set.h"
-#include "google_apis/gaia/gaia_constants.h"
-#include "google_apis/gaia/google_service_auth_error.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-
-#if BUILDFLAG(ENABLE_CROS_LIBASSISTANT)
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/task/task_traits.h"
-#include "base/task/thread_pool.h"
-#include "chromeos/ash/services/libassistant/constants.h"
-#endif  // BUILDFLAG(ENABLE_CROS_LIBASSISTANT)
-
-namespace ash::assistant {
-
-namespace {
-
-constexpr char kScopeAssistant[] =
-    "https://www.googleapis.com/auth/assistant-sdk-prototype";
-
-constexpr char kServiceStateHistogram[] = "Assistant.ServiceState";
-
-constexpr base::TimeDelta kMinTokenRefreshDelay = base::Milliseconds(1000);
-constexpr base::TimeDelta kMaxTokenRefreshDelay = base::Milliseconds(60 * 1000);
-
-// Testing override for the URI used to contact the s3 server.
-const char* g_s3_server_uri_override = nullptr;
-// Testing override for the device-id used by Libassistant to identify this
-// device.
-const char* g_device_id_override = nullptr;
-
-#if BUILDFLAG(ENABLE_CROS_LIBASSISTANT)
-base::TaskTraits GetTaskTraits() {
-  return {base::MayBlock(), base::TaskPriority::USER_BLOCKING,
-          base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN};
-}
-#endif  // BUILDFLAG(ENABLE_CROS_LIBASSISTANT)
-
-// The max number of tries to start service.
-// We decide whether to start service based on two counters:
-// 1. the backoff `failure_count`, and
-// 2. the pref value `kAssistantNumFailuresSinceLastServiceRun`.
-//
-// 1.   Will not restart service if the `failure_count` is larger than
-//      `kMaxStartServiceRetries`. Note that the `failure_count` will change:
-// 1.a. Increment by 1 for every service disconnected.
-// 1.b. Reset to 0 when explicitly re-enable the Assistant from the Settings.
-// 1.c. Reset to 0 when re-login the device.
-// 1.d. Decrement by 1 when it has been `kAutoRecoverTime`.
-//
-// 2.   Will not restart service if the pref value
-//      `kAssistantNumFailuresSinceLastServiceRun` is larger than
-//      `kMaxStartServiceRetries`, unless `failure_count` is 0, e.g. the first
-//      time login. Note that the `kAssistantNumFailuresSinceLastServiceRun`
-//      will change:
-// 2.a. Increment by 1 for every service disconnected.
-// 2.b. Reset to 0 when every service running.
-constexpr int kMaxStartServiceRetries = 1;
-
-// An interval used to gradually reduce the failure_count so that we could
-// restart.
-constexpr base::TimeDelta kAutoRecoverTime = base::Hours(24);
-
-constexpr net::BackoffEntry::Policy kRetryStartServiceBackoffPolicy = {
-    0,          // Number of initial errors to ignore.
-    1000,       // Initial delay in ms.
-    2.0,        // Factor by which the waiting time will be multiplied.
-    0.2,        // Fuzzing percentage.
-    60 * 1000,  // Maximum delay in ms.
-    -1,         // Never discard the entry.
-    true,       // Use initial delay.
-};
-
-AssistantStatus ToAssistantStatus(AssistantManagerService::State state) {
-  using State = AssistantManagerService::State;
-
-  switch (state) {
-    case State::STOPPED:
-    case State::STOPPING:
-    case State::STARTING:
-    case State::STARTED:
-    case State::DISCONNECTED:
-      return AssistantStatus::NOT_READY;
-    case State::RUNNING:
-      return AssistantStatus::READY;
-  }
-}
-
-std::optional<std::string> GetS3ServerUriOverride() {
-  if (g_s3_server_uri_override)
-    return g_s3_server_uri_override;
-  return std::nullopt;
-}
-
-std::optional<std::string> GetDeviceIdOverride() {
-  if (g_device_id_override)
-    return g_device_id_override;
-  return std::nullopt;
-}
-
-// In the signed-out mode, we are going to run Assistant service without
-// using user's signed in account information.
-bool IsSignedOutMode() {
-  // One example of using fake gaia login is in our automation tests, i.e.
-  // Assistant Tast tests.
-  return base::CommandLine::ForCurrentProcess()->HasSwitch(
-      switches::kDisableGaiaServices);
-}
-
-void RecordServiceState(AssistantManagerService::State state) {
-  base::UmaHistogramEnumeration(kServiceStateHistogram, state);
-}
-
-}  // namespace
-
-// Scoped observer that will subscribe |Service| as an Ash session observer,
-// and will unsubscribe in its destructor.
-class ScopedAshSessionObserver {
- public:
-  ScopedAshSessionObserver(SessionActivationObserver* observer,
-                           const AccountId& account_id)
-      : observer_(observer), account_id_(account_id) {
-    DCHECK(account_id_.is_valid());
-    DCHECK(controller());
-    controller()->AddSessionActivationObserverForAccountId(account_id_,
-                                                           observer_);
-  }
-
-  ~ScopedAshSessionObserver() {
-    if (controller())
-      controller()->RemoveSessionActivationObserverForAccountId(account_id_,
-                                                                observer_);
-  }
-
- private:
-  SessionController* controller() const { return SessionController::Get(); }
-
-  const raw_ptr<SessionActivationObserver> observer_;
-  const AccountId account_id_;
-};
-
-class Service::Context : public ServiceContext {
- public:
-  explicit Context(Service* parent) : parent_(parent) {}
-
-  Context(const Context&) = delete;
-  Context& operator=(const Context&) = delete;
-
-  ~Context() override = default;
-
-  // ServiceContext:
-  AssistantAlarmTimerController* assistant_alarm_timer_controller() override {
-    return AssistantAlarmTimerController::Get();
-  }
-
-  AssistantController* assistant_controller() override {
-    return AssistantController::Get();
-  }
-
-  AssistantNotificationController* assistant_notification_controller()
-      override {
-    return AssistantNotificationController::Get();
-  }
-
-  AssistantScreenContextController* assistant_screen_context_controller()
-      override {
-    return AssistantScreenContextController::Get();
-  }
-
-  AssistantStateBase* assistant_state() override {
-    return AssistantState::Get();
-  }
-
-  CrasAudioHandler* cras_audio_handler() override {
-    return CrasAudioHandler::Get();
-  }
-
-  DeviceActions* device_actions() override { return DeviceActions::Get(); }
-
-  scoped_refptr<base::SequencedTaskRunner> main_task_runner() override {
-    return parent_->main_task_runner_;
-  }
-
-  chromeos::PowerManagerClient* power_manager_client() override {
-    return chromeos::PowerManagerClient::Get();
-  }
-
-  GaiaId primary_account_gaia_id() override {
-    return parent_->RetrievePrimaryAccountInfo().gaia;
-  }
-
- private:
-  const raw_ptr<Service> parent_;  // |this| is owned by |parent_|.
-};
-
-Service::Service(std::unique_ptr<network::PendingSharedURLLoaderFactory>
-                     pending_url_loader_factory,
-                 signin::IdentityManager* identity_manager,
-                 PrefService* pref_service)
-    : context_(std::make_unique<Context>(this)),
-      identity_manager_(identity_manager),
-      pref_service_(pref_service),
-      token_refresh_timer_(std::make_unique<base::OneShotTimer>()),
-      main_task_runner_(base::SequencedTaskRunner::GetCurrentDefault()),
-      pending_url_loader_factory_(std::move(pending_url_loader_factory)),
-      start_service_retry_backoff_(&kRetryStartServiceBackoffPolicy),
-      auto_service_recover_timer_(std::make_unique<base::OneShotTimer>()) {
-  DCHECK(identity_manager_);
-  chromeos::PowerManagerClient* power_manager_client =
-      context_->power_manager_client();
-  power_manager_observation_.Observe(power_manager_client);
-  power_manager_client->RequestStatusUpdate();
-}
-
-Service::~Service() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  AssistantState::Get()->RemoveObserver(this);
-  AssistantController::Get()->SetAssistant(nullptr);
-}
-
-// static
-void Service::OverrideS3ServerUriForTesting(const char* uri) {
-  g_s3_server_uri_override = uri;
-}
-
-// static
-void Service::OverrideDeviceIdForTesting(const char* device_id) {
-  g_device_id_override = device_id;
-}
-
-void Service::SetAssistantManagerServiceForTesting(
-    std::unique_ptr<AssistantManagerService> assistant_manager_service) {
-  DCHECK(assistant_manager_service_ == nullptr);
-  assistant_manager_service_for_testing_ = std::move(assistant_manager_service);
-}
-
-void Service::Init() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  AssistantState::Get()->AddObserver(this);
-
-  DCHECK(!assistant_manager_service_);
-
-  RequestAccessToken();
-  LoadLibassistant();
-}
-
-void Service::Shutdown() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  if (assistant_manager_service_)
-    StopAssistantManagerService();
-}
-
-Assistant* Service::GetAssistant() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(assistant_manager_service_);
-  return assistant_manager_service_.get();
-}
-
-void Service::PowerChanged(const power_manager::PowerSupplyProperties& prop) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  const bool power_source_connected =
-      prop.external_power() == power_manager::PowerSupplyProperties::AC;
-  if (power_source_connected == power_source_connected_)
-    return;
-
-  power_source_connected_ = power_source_connected;
-  UpdateAssistantManagerState();
-}
-
-void Service::SuspendDone(base::TimeDelta sleep_duration) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  // |token_refresh_timer_| may become stale during sleeping, so we immediately
-  // request a new token to make sure it is fresh.
-  if (token_refresh_timer_->IsRunning()) {
-    token_refresh_timer_->Stop();
-    RequestAccessToken();
-  }
-}
-
-void Service::OnSessionActivated(bool activated) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  session_active_ = activated;
-
-  AssistantBrowserDelegate::Get()->OnAssistantStatusChanged(
-      ToAssistantStatus(assistant_manager_service_->GetState()));
-  UpdateListeningState();
-}
-
-void Service::OnLockStateChanged(bool locked) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  locked_ = locked;
-  UpdateListeningState();
-}
-
-void Service::OnAssistantConsentStatusChanged(int consent_status) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  // Notify device apps status when user accepts activity control.
-  if (assistant_manager_service_ &&
-      assistant_manager_service_->GetState() ==
-          AssistantManagerService::State::RUNNING) {
-    assistant_manager_service_->SyncDeviceAppsStatus();
-  }
-}
-
-void Service::OnAssistantContextEnabled(bool enabled) {
-  UpdateAssistantManagerState();
-}
-
-void Service::OnAssistantHotwordAlwaysOn(bool hotword_always_on) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  // No need to update hotword status if power source is connected.
-  if (power_source_connected_)
-    return;
-
-  UpdateAssistantManagerState();
-}
-
-void Service::OnAssistantSettingsEnabled(bool enabled) {
-  // Reset the failure count and backoff delay when the Settings is re-enabled.
-  start_service_retry_backoff_.Reset();
-  UpdateAssistantManagerState();
-}
-
-void Service::OnAssistantHotwordEnabled(bool enabled) {
-  UpdateAssistantManagerState();
-}
-
-void Service::OnLocaleChanged(const std::string& locale) {
-  UpdateAssistantManagerState();
-}
-
-void Service::OnArcPlayStoreEnabledChanged(bool enabled) {
-  UpdateAssistantManagerState();
-}
-
-void Service::OnLockedFullScreenStateChanged(bool enabled) {
-  UpdateListeningState();
-}
-
-void Service::OnAuthenticationError() {
-  RequestAccessToken();
-}
-
-void Service::OnStateChanged(AssistantManagerService::State new_state) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  switch (new_state) {
-    case AssistantManagerService::State::STARTED:
-      FinalizeAssistantManagerService();
-      break;
-    case AssistantManagerService::State::RUNNING:
-      OnLibassistantServiceRunning();
-      break;
-    case AssistantManagerService::State::STOPPED:
-      OnLibassistantServiceStopped();
-      break;
-    case AssistantManagerService::State::DISCONNECTED:
-      OnLibassistantServiceDisconnected();
-      break;
-    case AssistantManagerService::State::STARTING:
-    case AssistantManagerService::State::STOPPING:
-      // No action.
-      break;
-  }
-
-  RecordServiceState(new_state);
-  AssistantBrowserDelegate::Get()->OnAssistantStatusChanged(
-      ToAssistantStatus(new_state));
-
-  UpdateListeningState();
-}
-
-void Service::UpdateAssistantManagerState() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  auto* assistant_state = AssistantState::Get();
-
-  if (!assistant_state->hotword_enabled().has_value() ||
-      !assistant_state->settings_enabled().has_value() ||
-      !assistant_state->locale().has_value() ||
-      (!access_token_.has_value() && !IsSignedOutMode()) ||
-      !assistant_state->arc_play_store_enabled().has_value() ||
-      !libassistant_loaded_ || is_deleting_data_) {
-    // Assistant state has not finished initialization, let's wait.
-    return;
-  }
-
-  if (IsSignedOutMode()) {
-    // Clear |access_token_| in signed-out mode to keep it synced with what we
-    // will pass to the |assistant_manager_service_|.
-    access_token_ = std::nullopt;
-  }
-
-  if (!assistant_manager_service_)
-    CreateAssistantManagerService();
-
-  auto state = assistant_manager_service_->GetState();
-  switch (state) {
-    case AssistantManagerService::State::STOPPED:
-    case AssistantManagerService::State::DISCONNECTED:
-      if (!CanStartService()) {
-        return;
-      }
-
-      if (assistant_state->settings_enabled().value()) {
-        assistant_manager_service_->Start(GetUserInfo(), ShouldEnableHotword());
-
-        // Re-add observers every time when starting.
-        assistant_manager_service_->AddAuthenticationStateObserver(this);
-        assistant_manager_service_->AddAndFireStateObserver(this);
-
-        if (AssistantInteractionLogger::IsLoggingEnabled()) {
-          interaction_logger_ = std::make_unique<AssistantInteractionLogger>();
-          assistant_manager_service_->AddAssistantInteractionSubscriber(
-              interaction_logger_.get());
-        }
-
-        DVLOG(1) << "Request Assistant start";
-      }
-      break;
-    case AssistantManagerService::State::STARTING:
-    case AssistantManagerService::State::STARTED:
-      // If the Assistant is disabled by domain policy, the libassistant will
-      // never becomes ready. Stop waiting for the state change and stop the
-      // service.
-      if (assistant_state->allowed_state() ==
-          AssistantAllowedState::DISALLOWED_BY_POLICY) {
-        StopAssistantManagerService();
-        return;
-      }
-      // Wait if |assistant_manager_service_| is not at a stable state.
-      ScheduleUpdateAssistantManagerState(/*should_backoff=*/false);
-      break;
-    case AssistantManagerService::State::STOPPING:
-      ScheduleUpdateAssistantManagerState(/*should_backoff=*/false);
-      break;
-    case AssistantManagerService::State::RUNNING:
-      if (assistant_state->settings_enabled().value()) {
-        assistant_manager_service_->SetUser(GetUserInfo());
-        assistant_manager_service_->EnableHotword(ShouldEnableHotword());
-        assistant_manager_service_->SetArcPlayStoreEnabled(
-            assistant_state->arc_play_store_enabled().value());
-        assistant_manager_service_->SetAssistantContextEnabled(
-            assistant_state->IsScreenContextAllowed());
-      } else {
-        StopAssistantManagerService();
-      }
-      break;
-  }
-}
-
-void Service::ScheduleUpdateAssistantManagerState(bool should_backoff) {
-  update_assistant_manager_callback_.Cancel();
-  update_assistant_manager_callback_.Reset(base::BindOnce(
-      &Service::UpdateAssistantManagerState, weak_ptr_factory_.GetWeakPtr()));
-
-  base::TimeDelta delay =
-      should_backoff ? start_service_retry_backoff_.GetTimeUntilRelease()
-                     : kUpdateAssistantManagerDelay;
-  main_task_runner_->PostDelayedTask(
-      FROM_HERE, update_assistant_manager_callback_.callback(), delay);
-}
-
-CoreAccountInfo Service::RetrievePrimaryAccountInfo() const {
-  CoreAccountInfo account_info =
-      identity_manager_->GetPrimaryAccountInfo(signin::ConsentLevel::kSignin);
-  CHECK(!account_info.account_id.empty());
-  CHECK(!account_info.gaia.empty());
-  return account_info;
-}
-
-void Service::RequestAccessToken() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  // Bypass access token fetching when service is running in signed-out mode.
-  if (IsSignedOutMode()) {
-    VLOG(1) << "Signed out mode detected, bypass access token fetching.";
-    return;
-  }
-
-  if (access_token_fetcher_) {
-    LOG(WARNING) << "Access token already requested.";
-    return;
-  }
-
-  VLOG(1) << "Start requesting access token.";
-  CoreAccountInfo account_info = RetrievePrimaryAccountInfo();
-  if (!identity_manager_->HasAccountWithRefreshToken(account_info.account_id)) {
-    LOG(ERROR) << "Failed to retrieve primary account info. Retrying.";
-    RetryRefreshToken();
-    return;
-  }
-
-  signin::ScopeSet scopes;
-  scopes.insert(kScopeAssistant);
-  scopes.insert(GaiaConstants::kGCMGroupServerOAuth2Scope);
-
-  access_token_fetcher_ = identity_manager_->CreateAccessTokenFetcherForAccount(
-      account_info.account_id, "cros_assistant", scopes,
-      base::BindOnce(&Service::GetAccessTokenCallback, base::Unretained(this)),
-      signin::AccessTokenFetcher::Mode::kImmediate);
-}
-
-void Service::GetAccessTokenCallback(
-    GoogleServiceAuthError error,
-    signin::AccessTokenInfo access_token_info) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  // It's safe to delete AccessTokenFetcher from inside its own callback.
-  access_token_fetcher_.reset();
-
-  if (error.state() != GoogleServiceAuthError::NONE) {
-    LOG(ERROR) << "Failed to retrieve token, error: " << error.ToString();
-    RetryRefreshToken();
-    return;
-  }
-
-  access_token_ = access_token_info.token;
-  UpdateAssistantManagerState();
-  token_refresh_timer_->Start(
-      FROM_HERE, access_token_info.expiration_time - base::Time::Now(), this,
-      &Service::RequestAccessToken);
-}
-
-void Service::RetryRefreshToken() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  base::TimeDelta backoff_delay =
-      std::min(kMinTokenRefreshDelay *
-                   (1 << (token_refresh_error_backoff_factor - 1)),
-               kMaxTokenRefreshDelay) +
-      base::RandDouble() * kMinTokenRefreshDelay;
-  if (backoff_delay < kMaxTokenRefreshDelay)
-    ++token_refresh_error_backoff_factor;
-  token_refresh_timer_->Start(FROM_HERE, backoff_delay, this,
-                              &Service::RequestAccessToken);
-}
-
-void Service::CreateAssistantManagerService() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  assistant_manager_service_ = CreateAndReturnAssistantManagerService();
-}
-
-std::unique_ptr<AssistantManagerService>
-Service::CreateAndReturnAssistantManagerService() {
-  if (assistant_manager_service_for_testing_)
-    return std::move(assistant_manager_service_for_testing_);
-
-  // |assistant_manager_service_| is only created once.
-  DCHECK(pending_url_loader_factory_);
-  return std::make_unique<AssistantManagerServiceImpl>(
-      context(), std::move(pending_url_loader_factory_),
-      GetS3ServerUriOverride(), GetDeviceIdOverride());
-}
-
-void Service::FinalizeAssistantManagerService() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(assistant_manager_service_->GetState() ==
-             AssistantManagerService::STARTED ||
-         assistant_manager_service_->GetState() ==
-             AssistantManagerService::RUNNING);
-
-  // Ensure one-time mojom initialization.
-  if (is_assistant_manager_service_finalized_)
-    return;
-  is_assistant_manager_service_finalized_ = true;
-
-  AddAshSessionObserver();
-  AssistantController::Get()->SetAssistant(assistant_manager_service_.get());
-}
-
-void Service::StopAssistantManagerService() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  assistant_manager_service_->Stop();
-  weak_ptr_factory_.InvalidateWeakPtrs();
-}
-
-void Service::OnLibassistantServiceRunning() {
-  DVLOG(1) << "Assistant is running";
-  pref_service_->SetInteger(prefs::kAssistantNumFailuresSinceLastServiceRun, 0);
-}
-
-void Service::OnLibassistantServiceStopped() {
-  ClearAfterStop();
-}
-
-void Service::OnLibassistantServiceDisconnected() {
-  ClearAfterStop();
-
-  if (auto_service_recover_timer_->IsRunning()) {
-    auto_service_recover_timer_->Stop();
-  }
-
-  // Increase the failure count for both the backoff and pref.
-  start_service_retry_backoff_.InformOfRequest(/*succeeded=*/false);
-  int num_failures = pref_service_->GetInteger(
-      prefs::kAssistantNumFailuresSinceLastServiceRun);
-  pref_service_->SetInteger(prefs::kAssistantNumFailuresSinceLastServiceRun,
-                            num_failures + 1);
-  if (CanStartService()) {
-    LOG(WARNING) << "LibAssistant service disconnected. Re-starting...";
-
-    // Restarts LibassistantService.
-    ScheduleUpdateAssistantManagerState(/*should_backoff=*/true);
-  } else {
-    // Start auto recover timer.
-    auto delay = GetAutoRecoverTime();
-    auto_service_recover_timer_->Start(FROM_HERE, delay, this,
-                                       &Service::DecreaseStartServiceBackoff);
-    LOG(ERROR)
-        << "LibAssistant service keeps disconnected. All retries attempted.";
-  }
-}
-
-void Service::AddAshSessionObserver() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  // No session controller in unittest.
-  if (SessionController::Get()) {
-    // Note that this account can either be a regular account using real gaia,
-    // or a fake gaia account.
-    CoreAccountInfo account_info = RetrievePrimaryAccountInfo();
-    AccountId account_id = AccountId::FromNonCanonicalEmail(
-        account_info.email, account_info.gaia, AccountType::GOOGLE);
-    scoped_ash_session_observer_ =
-        std::make_unique<ScopedAshSessionObserver>(this, account_id);
-  }
-}
-
-void Service::UpdateListeningState() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  if (!assistant_manager_service_) {
-    return;
-  }
-
-  bool should_listen =
-      !locked_ &&
-      !AssistantState::Get()->locked_full_screen_enabled().value_or(false) &&
-      session_active_;
-  DVLOG(1) << "Update assistant listening state: " << should_listen;
-  assistant_manager_service_->EnableListening(should_listen);
-  assistant_manager_service_->EnableHotword(should_listen &&
-                                            ShouldEnableHotword());
-}
-
-std::optional<AssistantManagerService::UserInfo> Service::GetUserInfo() const {
-  if (access_token_) {
-    return AssistantManagerService::UserInfo(RetrievePrimaryAccountInfo().gaia,
-                                             access_token_.value());
-  }
-  return std::nullopt;
-}
-
-bool Service::ShouldEnableHotword() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  bool dsp_available = context()->cras_audio_handler()->HasHotwordDevice();
-  auto* assistant_state = AssistantState::Get();
-
-  // Disable hotword if hotword is not set to always on and power source is not
-  // connected.
-  if (!dsp_available && !assistant_state->hotword_always_on().value_or(false) &&
-      !power_source_connected_) {
-    return false;
-  }
-
-  return assistant_state->hotword_enabled().value();
-}
-
-void Service::LoadLibassistant() {
-  libassistant::LibassistantLoader::Load(base::BindOnce(
-      &Service::OnLibassistantLoaded, weak_ptr_factory_.GetWeakPtr()));
-}
-
-void Service::OnLibassistantLoaded(bool success) {
-  libassistant_loaded_ = success;
-
-  if (success) {
-    UpdateAssistantManagerState();
-  }
-}
-
-void Service::ClearAfterStop() {
-  is_assistant_manager_service_finalized_ = false;
-  scoped_ash_session_observer_.reset();
-
-#if BUILDFLAG(ENABLE_CROS_LIBASSISTANT)
-  // When user disables the Assistant, we also delete all data.
-  if (!AssistantState::Get()->settings_enabled().value()) {
-    is_deleting_data_ = true;
-    base::ThreadPool::CreateSequencedTaskRunner(GetTaskTraits())
-        ->PostTaskAndReply(
-            FROM_HERE, base::BindOnce([]() {
-              base::DeletePathRecursively(base::FilePath(
-                  FILE_PATH_LITERAL(libassistant::kAssistantBaseDirPath)));
-            }),
-            base::BindOnce(&Service::OnDataDeleted,
-                           weak_ptr_factory_.GetWeakPtr()));
-  }
-#endif  // BUILDFLAG(ENABLE_CROS_LIBASSISTANT)
-
-  ResetAuthenticationStateObserver();
-}
-
-void Service::DecreaseStartServiceBackoff() {
-  // Reduce the failure_count by one to allow restart.
-  start_service_retry_backoff_.InformOfRequest(/*succeeded=*/true);
-
-  // It is ok to try to reset service if the service is running.
-  ScheduleUpdateAssistantManagerState(/*should_backoff=*/true);
-
-  // Start auto recover timer.
-  if (start_service_retry_backoff_.failure_count() > 0) {
-    auto delay = GetAutoRecoverTime();
-    auto_service_recover_timer_->Start(FROM_HERE, delay, this,
-                                       &Service::DecreaseStartServiceBackoff);
-  }
-}
-
-base::TimeDelta Service::GetAutoRecoverTime() {
-  if (!auto_recover_time_for_testing_.is_zero()) {
-    return auto_recover_time_for_testing_;
-  }
-  return kAutoRecoverTime;
-}
-
-bool Service::CanStartService() const {
-  // Please see comments on `kMaxStartServiceRetries`.
-  // We can start service if the failure count is zero:
-  // 1.b. Reset to 0 when explicitly re-enable the Assistant from the Settings.
-  // 1.c. Reset to 0 when re-login the device.
-  // 1.d. Decrement by 1 when it has been `kAutoRecoverTime`.
-  if (start_service_retry_backoff_.failure_count() == 0) {
-    return true;
-  }
-
-  // Do not start service if it has retried `kMaxStartServiceRetries` times in
-  // one chrome session or since the last time enable in Settings.
-  if (start_service_retry_backoff_.failure_count() > kMaxStartServiceRetries) {
-    return false;
-  }
-
-  // Do not start service if `kAssistantNumFailuresSinceLastServiceRun` failed
-  // `kMaxStartServiceRetries` times.
-  int num_failures_since_last_service_run = pref_service_->GetInteger(
-      prefs::kAssistantNumFailuresSinceLastServiceRun);
-  if (num_failures_since_last_service_run > kMaxStartServiceRetries) {
-    return false;
-  }
-
-  return true;
-}
-
-void Service::OnDataDeleted() {
-  is_deleting_data_ = false;
-  UpdateAssistantManagerState();
-}
-
-}  // namespace ash::assistant
diff --git a/chromeos/ash/services/assistant/service.h b/chromeos/ash/services/assistant/service.h
deleted file mode 100644
index 4f1cb51d..0000000
--- a/chromeos/ash/services/assistant/service.h
+++ /dev/null
@@ -1,244 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROMEOS_ASH_SERVICES_ASSISTANT_SERVICE_H_
-#define CHROMEOS_ASH_SERVICES_ASSISTANT_SERVICE_H_
-
-#include <memory>
-#include <optional>
-#include <string>
-
-#include "ash/public/cpp/assistant/assistant_state.h"
-#include "ash/public/cpp/session/session_activation_observer.h"
-#include "base/cancelable_callback.h"
-#include "base/component_export.h"
-#include "base/functional/callback.h"
-#include "base/memory/raw_ptr.h"
-#include "base/memory/weak_ptr.h"
-#include "base/scoped_observation.h"
-#include "base/sequence_checker.h"
-#include "base/task/sequenced_task_runner.h"
-#include "base/task/single_thread_task_runner.h"
-#include "base/time/time.h"
-#include "chromeos/ash/services/assistant/assistant_manager_service.h"
-#include "chromeos/ash/services/assistant/public/cpp/assistant_service.h"
-#include "chromeos/dbus/power/power_manager_client.h"
-#include "components/signin/public/identity_manager/account_info.h"
-#include "mojo/public/cpp/bindings/remote.h"
-#include "net/base/backoff_entry.h"
-
-class GoogleServiceAuthError;
-class PrefService;
-
-namespace base {
-class OneShotTimer;
-}  // namespace base
-
-namespace network {
-class PendingSharedURLLoaderFactory;
-}  // namespace network
-
-namespace power_manager {
-class PowerSupplyProperties;
-}  // namespace power_manager
-
-namespace signin {
-class AccessTokenFetcher;
-struct AccessTokenInfo;
-class IdentityManager;
-}  // namespace signin
-
-namespace ash::assistant {
-
-class AssistantInteractionLogger;
-class ScopedAshSessionObserver;
-class ServiceContext;
-
-// |AssistantManagerService|'s state won't update if it's currently in the
-// process of starting up. This is the delay before we will try to update
-// |AssistantManagerService| again.
-constexpr auto kUpdateAssistantManagerDelay = base::Seconds(1);
-
-class COMPONENT_EXPORT(ASSISTANT_SERVICE) Service
-    : public AssistantService,
-      public chromeos::PowerManagerClient::Observer,
-      public SessionActivationObserver,
-      public AssistantStateObserver,
-      public AssistantManagerService::StateObserver,
-      public AuthenticationStateObserver {
- public:
-  Service(std::unique_ptr<network::PendingSharedURLLoaderFactory>
-              pending_url_loader_factory,
-          signin::IdentityManager* identity_manager,
-          PrefService* pref_service);
-
-  Service(const Service&) = delete;
-  Service& operator=(const Service&) = delete;
-
-  ~Service() override;
-
-  // Allows tests to override the S3 server URI used by the service.
-  // The caller must ensure the memory passed in remains valid.
-  // This override can be removed by passing in a nullptr.
-  // Note: This would look nicer if it was a class method and not static,
-  // but unfortunately this must be called before |Service| tries to create the
-  // |AssistantManagerService|, which happens really soon after the service
-  // itself is created, so we do not have time in our tests to grab a handle
-  // to |Service| and set this before it is too late.
-  static void OverrideS3ServerUriForTesting(const char* uri);
-  static void OverrideDeviceIdForTesting(const char* device_id);
-
-  void SetAssistantManagerServiceForTesting(
-      std::unique_ptr<AssistantManagerService> assistant_manager_service);
-
-  // AssistantService overrides:
-  void Init() override;
-  void Shutdown() override;
-  Assistant* GetAssistant() override;
-
- private:
-  friend class AssistantServiceTest;
-
-  class Context;
-
-  // chromeos::PowerManagerClient::Observer overrides:
-  void PowerChanged(const power_manager::PowerSupplyProperties& prop) override;
-  void SuspendDone(base::TimeDelta sleep_duration) override;
-
-  // SessionActivationObserver overrides:
-  void OnSessionActivated(bool activated) override;
-  void OnLockStateChanged(bool locked) override;
-
-  // AssistantStateObserver overrides:
-  void OnAssistantConsentStatusChanged(int consent_status) override;
-  void OnAssistantContextEnabled(bool enabled) override;
-  void OnAssistantHotwordAlwaysOn(bool hotword_always_on) override;
-  void OnAssistantSettingsEnabled(bool enabled) override;
-  void OnAssistantHotwordEnabled(bool enabled) override;
-  void OnLocaleChanged(const std::string& locale) override;
-  void OnArcPlayStoreEnabledChanged(bool enabled) override;
-  void OnLockedFullScreenStateChanged(bool enabled) override;
-
-  // AuthenticationStateObserver overrides:
-  void OnAuthenticationError() override;
-
-  // AssistantManagerService::StateObserver overrides:
-  void OnStateChanged(AssistantManagerService::State new_state) override;
-
-  void UpdateAssistantManagerState();
-  void ScheduleUpdateAssistantManagerState(bool should_backoff);
-
-  CoreAccountInfo RetrievePrimaryAccountInfo() const;
-  void RequestAccessToken();
-  void GetAccessTokenCallback(GoogleServiceAuthError error,
-                              signin::AccessTokenInfo access_token_info);
-  void RetryRefreshToken();
-
-  void CreateAssistantManagerService();
-  std::unique_ptr<AssistantManagerService>
-  CreateAndReturnAssistantManagerService();
-
-  void FinalizeAssistantManagerService();
-
-  void StopAssistantManagerService();
-
-  void OnLibassistantServiceRunning();
-  void OnLibassistantServiceStopped();
-  void OnLibassistantServiceDisconnected();
-
-  void AddAshSessionObserver();
-
-  void UpdateListeningState();
-
-  std::optional<AssistantManagerService::UserInfo> GetUserInfo() const;
-
-  ServiceContext* context() { return context_.get(); }
-
-  // Returns the "actual" hotword status. In addition to the hotword pref, this
-  // method also take power status into account if dsp support is not available
-  // for the device.
-  bool ShouldEnableHotword();
-
-  void LoadLibassistant();
-  void OnLibassistantLoaded(bool success);
-
-  void ClearAfterStop();
-
-  void DecreaseStartServiceBackoff();
-
-  base::TimeDelta GetAutoRecoverTime();
-  void SetAutoRecoverTimeForTesting(base::TimeDelta delay) {
-    auto_recover_time_for_testing_ = delay;
-  }
-
-  bool CanStartService() const;
-
-  void OnDataDeleted();
-
-  // |ServiceContext| object passed to child classes so they can access some of
-  // our functionality without depending on us.
-  // Note: this is used by the other members here, so it must be defined first
-  // so it is destroyed last.
-  std::unique_ptr<ServiceContext> context_;
-
-  const raw_ptr<signin::IdentityManager> identity_manager_;
-  const raw_ptr<PrefService> pref_service_;
-  std::unique_ptr<ScopedAshSessionObserver> scoped_ash_session_observer_;
-  std::unique_ptr<AssistantManagerService> assistant_manager_service_;
-  std::unique_ptr<base::OneShotTimer> token_refresh_timer_;
-  int token_refresh_error_backoff_factor = 1;
-  scoped_refptr<base::SequencedTaskRunner> main_task_runner_;
-  base::ScopedObservation<chromeos::PowerManagerClient,
-                          chromeos::PowerManagerClient::Observer>
-      power_manager_observation_{this};
-
-  // Flag to guard the one-time mojom initialization.
-  bool is_assistant_manager_service_finalized_ = false;
-  // Whether the current user session is active.
-  bool session_active_ = false;
-  // Whether the lock screen is on.
-  bool locked_ = false;
-  // Whether the power source is connected.
-  bool power_source_connected_ = false;
-  // Whether the libassistant library is loaded.
-  bool libassistant_loaded_ = false;
-  // Whether is deleting data.
-  bool is_deleting_data_ = false;
-
-  // The value passed into |SetAssistantManagerServiceForTesting|.
-  // Will be moved into |assistant_manager_service_| when the service is
-  // supposed to be created.
-  std::unique_ptr<AssistantManagerService>
-      assistant_manager_service_for_testing_;
-
-  std::optional<std::string> access_token_;
-
-  // non-null until |assistant_manager_service_| is created.
-  std::unique_ptr<network::PendingSharedURLLoaderFactory>
-      pending_url_loader_factory_;
-
-  // If Libassistant service is disconnected, will use this backoff entry to
-  // restart the service.
-  net::BackoffEntry start_service_retry_backoff_;
-
-  // A timer used to slowly recover from previous crashes by reducing the
-  // `start_service_retry_backoff_` failure_count by one for every
-  // `kAutoRecoverTime`.
-  std::unique_ptr<base::OneShotTimer> auto_service_recover_timer_;
-  base::TimeDelta auto_recover_time_for_testing_;
-
-  base::CancelableOnceClosure update_assistant_manager_callback_;
-
-  std::unique_ptr<signin::AccessTokenFetcher> access_token_fetcher_;
-
-  std::unique_ptr<AssistantInteractionLogger> interaction_logger_;
-
-  SEQUENCE_CHECKER(sequence_checker_);
-
-  base::WeakPtrFactory<Service> weak_ptr_factory_{this};
-};
-
-}  // namespace ash::assistant
-
-#endif  // CHROMEOS_ASH_SERVICES_ASSISTANT_SERVICE_H_
diff --git a/chromeos/ash/services/assistant/service_context.h b/chromeos/ash/services/assistant/service_context.h
deleted file mode 100644
index aa9d7fdf..0000000
--- a/chromeos/ash/services/assistant/service_context.h
+++ /dev/null
@@ -1,64 +0,0 @@
-// 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.
-
-#ifndef CHROMEOS_ASH_SERVICES_ASSISTANT_SERVICE_CONTEXT_H_
-#define CHROMEOS_ASH_SERVICES_ASSISTANT_SERVICE_CONTEXT_H_
-
-#include "ash/public/cpp/assistant/controller/assistant_screen_context_controller.h"
-#include "base/memory/scoped_refptr.h"
-#include "google_apis/gaia/gaia_id.h"
-
-namespace base {
-class SequencedTaskRunner;
-}  // namespace base
-
-namespace chromeos {
-class PowerManagerClient;
-}
-
-namespace ash {
-
-class AssistantAlarmTimerController;
-class AssistantController;
-class AssistantNotificationController;
-class AssistantStateBase;
-class CrasAudioHandler;
-class DeviceActions;
-
-namespace assistant {
-
-// Context object passed around so classes can access some of the |Service|
-// functionality without directly depending on the |Service| class.
-class ServiceContext {
- public:
-  virtual ~ServiceContext() = default;
-
-  virtual AssistantAlarmTimerController* assistant_alarm_timer_controller() = 0;
-
-  virtual AssistantController* assistant_controller() = 0;
-
-  virtual AssistantNotificationController*
-  assistant_notification_controller() = 0;
-
-  virtual AssistantScreenContextController*
-  assistant_screen_context_controller() = 0;
-
-  virtual AssistantStateBase* assistant_state() = 0;
-
-  virtual CrasAudioHandler* cras_audio_handler() = 0;
-
-  virtual DeviceActions* device_actions() = 0;
-
-  virtual scoped_refptr<base::SequencedTaskRunner> main_task_runner() = 0;
-
-  virtual chromeos::PowerManagerClient* power_manager_client() = 0;
-
-  // Returns the Gaia ID of the primary account (which is used by the
-  // Assistant).
-  virtual GaiaId primary_account_gaia_id() = 0;
-};
-}  // namespace assistant
-}  // namespace chromeos
-
-#endif  // CHROMEOS_ASH_SERVICES_ASSISTANT_SERVICE_CONTEXT_H_
diff --git a/chromeos/ash/services/assistant/service_unittest.cc b/chromeos/ash/services/assistant/service_unittest.cc
deleted file mode 100644
index 3cedb50..0000000
--- a/chromeos/ash/services/assistant/service_unittest.cc
+++ /dev/null
@@ -1,751 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chromeos/ash/services/assistant/service.h"
-
-#include <memory>
-#include <utility>
-#include <vector>
-
-#include "ash/public/cpp/assistant/test_support/mock_assistant_controller.h"
-#include "base/check.h"
-#include "base/memory/scoped_refptr.h"
-#include "base/run_loop.h"
-#include "base/test/task_environment.h"
-#include "base/test/test_mock_time_task_runner.h"
-#include "base/time/tick_clock.h"
-#include "base/time/time.h"
-#include "base/timer/timer.h"
-#include "chromeos/ash/components/audio/cras_audio_handler.h"
-#include "chromeos/ash/services/assistant/public/cpp/assistant_prefs.h"
-#include "chromeos/ash/services/assistant/public/cpp/features.h"
-#include "chromeos/ash/services/assistant/test_support/fake_assistant_manager_service_impl.h"
-#include "chromeos/ash/services/assistant/test_support/fully_initialized_assistant_state.h"
-#include "chromeos/ash/services/assistant/test_support/scoped_assistant_browser_delegate.h"
-#include "chromeos/ash/services/assistant/test_support/scoped_device_actions.h"
-#include "chromeos/dbus/power/fake_power_manager_client.h"
-#include "components/prefs/testing_pref_service.h"
-#include "components/signin/public/identity_manager/identity_manager.h"
-#include "components/signin/public/identity_manager/identity_test_environment.h"
-#include "google_apis/gaia/gaia_id.h"
-#include "mojo/public/cpp/bindings/pending_remote.h"
-#include "net/base/backoff_entry.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
-#include "services/network/test/test_url_loader_factory.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace ash::assistant {
-
-namespace {
-
-constexpr base::TimeDelta kDefaultTokenExpirationDelay =
-    base::Milliseconds(60000);
-
-constexpr base::TimeDelta kAutoRecoverTime = base::Seconds(60);
-
-#define EXPECT_STATE(_state) EXPECT_EQ(_state, assistant_manager()->GetState())
-
-constexpr char kAccessToken[] = "fake access token";
-constexpr GaiaId::Literal kGaiaId("gaia_id_for_user_gmail.com");
-constexpr char kEmailAddress[] = "user@gmail.com";
-
-// Should be the same value as the one in service.cc.
-constexpr int kMaxStartServiceRetries = 1;
-
-}  // namespace
-
-class ScopedFakeAssistantBrowserDelegate
-    : public ScopedAssistantBrowserDelegate {
- public:
-  explicit ScopedFakeAssistantBrowserDelegate(AssistantState* assistant_state)
-      : status_(AssistantStatus::NOT_READY) {}
-
-  AssistantStatus status() { return status_; }
-
- private:
-  // ScopedAssistantBrowserDelegate:
-  void OnAssistantStatusChanged(AssistantStatus new_status) override {
-    status_ = new_status;
-  }
-
-  AssistantStatus status_;
-};
-
-class AssistantServiceTest : public testing::Test {
- public:
-  AssistantServiceTest() = default;
-  AssistantServiceTest(const AssistantServiceTest&) = delete;
-  AssistantServiceTest& operator=(const AssistantServiceTest&) = delete;
-  ~AssistantServiceTest() override = default;
-
-  void SetUp() override {
-    chromeos::PowerManagerClient::InitializeFake();
-    chromeos::FakePowerManagerClient::Get()->SetTabletMode(
-        chromeos::PowerManagerClient::TabletMode::OFF, base::TimeTicks());
-
-    shared_url_loader_factory_ =
-        base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
-            &url_loader_factory_);
-
-    prefs::RegisterProfilePrefs(pref_service_.registry());
-    pref_service_.SetBoolean(prefs::kAssistantEnabled, true);
-    pref_service_.SetBoolean(prefs::kAssistantHotwordEnabled, true);
-
-    assistant_state_.RegisterPrefChanges(&pref_service_);
-
-    // In production the primary account is set before the service is created.
-    identity_test_env_.MakePrimaryAccountAvailable(
-        kEmailAddress, signin::ConsentLevel::kSignin);
-
-    service_ = std::make_unique<Service>(shared_url_loader_factory_->Clone(),
-                                         identity_test_env_.identity_manager(),
-                                         pref_service());
-    service_->SetAssistantManagerServiceForTesting(
-        std::make_unique<FakeAssistantManagerServiceImpl>());
-    service_->SetAutoRecoverTimeForTesting(kAutoRecoverTime);
-
-    service_->Init();
-    // Wait for AssistantManagerService to be set.
-    base::RunLoop().RunUntilIdle();
-
-    IssueAccessToken(kAccessToken);
-    // Simulate that the DLC library is loaded.
-    service_->OnLibassistantLoaded(/*success=*/true);
-  }
-
-  void TearDown() override {
-    service_.reset();
-    chromeos::PowerManagerClient::Shutdown();
-    CrasAudioHandler::Shutdown();
-  }
-
-  void StartAssistantAndWait() {
-    pref_service()->SetBoolean(prefs::kAssistantEnabled, true);
-    base::RunLoop().RunUntilIdle();
-  }
-
-  void StopAssistantAndWait() {
-    pref_service()->SetBoolean(prefs::kAssistantEnabled, false);
-    base::RunLoop().RunUntilIdle();
-  }
-
-  void IssueAccessToken(const std::string& access_token) {
-    identity_test_env_.WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
-        access_token, base::Time::Now() + kDefaultTokenExpirationDelay);
-  }
-
-  Service* service() { return service_.get(); }
-
-  FakeAssistantManagerServiceImpl* assistant_manager() {
-    auto* result = static_cast<FakeAssistantManagerServiceImpl*>(
-        service_->assistant_manager_service_.get());
-    DCHECK(result);
-    return result;
-  }
-
-  void ResetFakeAssistantManager() {
-    assistant_manager()->SetUser(std::nullopt);
-  }
-
-  signin::IdentityTestEnvironment* identity_test_env() {
-    return &identity_test_env_;
-  }
-
-  PrefService* pref_service() { return &pref_service_; }
-
-  AssistantState* assistant_state() { return &assistant_state_; }
-
-  ScopedFakeAssistantBrowserDelegate* client() { return &fake_delegate_; }
-
-  base::test::TaskEnvironment* task_environment() { return &task_environment_; }
-
-  net::BackoffEntry* GetRestartServiceBackoff() {
-    return &service_->start_service_retry_backoff_;
-  }
-
-  void DecreaseStartServiceBackoff() {
-    service_->DecreaseStartServiceBackoff();
-  }
-
-  int GetNumberOfFailuresSinceLastServiceRun() {
-    return pref_service()->GetInteger(
-        prefs::kAssistantNumFailuresSinceLastServiceRun);
-  }
-
-  void SetNumberOfFailuresSinceLastServiceRun(int number) {
-    pref_service()->SetInteger(prefs::kAssistantNumFailuresSinceLastServiceRun,
-                               number);
-  }
-
- private:
-  base::test::TaskEnvironment task_environment_{
-      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
-
-  TestingPrefServiceSimple pref_service_;
-
-  std::unique_ptr<Service> service_;
-
-  ScopedCrasAudioHandlerForTesting cras_audio_handler_;
-  FullyInitializedAssistantState assistant_state_;
-  signin::IdentityTestEnvironment identity_test_env_;
-  ScopedFakeAssistantBrowserDelegate fake_delegate_{&assistant_state_};
-  ScopedDeviceActions fake_device_actions_;
-  testing::NiceMock<MockAssistantController> mock_assistant_controller;
-
-  network::TestURLLoaderFactory url_loader_factory_;
-  scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory_;
-};
-
-TEST_F(AssistantServiceTest, RefreshTokenAfterExpire) {
-  ASSERT_FALSE(identity_test_env()->IsAccessTokenRequestPending());
-  task_environment()->FastForwardBy(kDefaultTokenExpirationDelay / 2);
-
-  // Before token expire, should not request new token.
-  EXPECT_FALSE(identity_test_env()->IsAccessTokenRequestPending());
-
-  task_environment()->FastForwardBy(kDefaultTokenExpirationDelay);
-
-  // After token expire, should request once.
-  EXPECT_TRUE(identity_test_env()->IsAccessTokenRequestPending());
-}
-
-TEST_F(AssistantServiceTest, RetryRefreshTokenAfterFailure) {
-  ASSERT_FALSE(identity_test_env()->IsAccessTokenRequestPending());
-
-  // Let the first token expire. Another will be requested.
-  task_environment()->FastForwardBy(kDefaultTokenExpirationDelay);
-  EXPECT_TRUE(identity_test_env()->IsAccessTokenRequestPending());
-
-  // Reply with an error.
-  identity_test_env()->WaitForAccessTokenRequestIfNecessaryAndRespondWithError(
-      GoogleServiceAuthError(GoogleServiceAuthError::State::CONNECTION_FAILED));
-  EXPECT_FALSE(identity_test_env()->IsAccessTokenRequestPending());
-
-  // Token request automatically retry.
-  // The failure delay has jitter so fast forward a bit more, but before
-  // the returned token would expire again.
-  task_environment()->FastForwardBy(kDefaultTokenExpirationDelay / 2);
-
-  EXPECT_TRUE(identity_test_env()->IsAccessTokenRequestPending());
-}
-
-TEST_F(AssistantServiceTest, RetryRefreshTokenAfterDeviceWakeup) {
-  ASSERT_FALSE(identity_test_env()->IsAccessTokenRequestPending());
-
-  chromeos::FakePowerManagerClient::Get()->SendSuspendDone();
-  // Token requested immediately after suspend done.
-  EXPECT_TRUE(identity_test_env()->IsAccessTokenRequestPending());
-}
-
-TEST_F(AssistantServiceTest, StopImmediatelyIfAssistantIsRunning) {
-  // Test is set up as |State::STARTED|.
-  assistant_manager()->FinishStart();
-  EXPECT_STATE(AssistantManagerService::State::RUNNING);
-
-  StopAssistantAndWait();
-
-  EXPECT_STATE(AssistantManagerService::State::STOPPED);
-}
-
-TEST_F(AssistantServiceTest, StopDelayedIfAssistantNotFinishedStarting) {
-  EXPECT_STATE(AssistantManagerService::State::STARTING);
-
-  // Turning settings off will trigger logic to try to stop it.
-  StopAssistantAndWait();
-
-  EXPECT_STATE(AssistantManagerService::State::STARTING);
-
-  task_environment()->FastForwardBy(kUpdateAssistantManagerDelay);
-
-  // No change of state because it is still starting.
-  EXPECT_STATE(AssistantManagerService::State::STARTING);
-
-  assistant_manager()->FinishStart();
-
-  task_environment()->FastForwardBy(kUpdateAssistantManagerDelay);
-
-  EXPECT_STATE(AssistantManagerService::State::STOPPED);
-}
-
-TEST_F(AssistantServiceTest, ShouldSendUserInfoWhenStarting) {
-  // First stop the service and reset the AssistantManagerService
-  assistant_manager()->FinishStart();
-  StopAssistantAndWait();
-  ResetFakeAssistantManager();
-
-  // Now start the service
-  StartAssistantAndWait();
-
-  ASSERT_TRUE(assistant_manager()->access_token().has_value());
-  EXPECT_EQ(kAccessToken, assistant_manager()->access_token().value());
-  ASSERT_TRUE(assistant_manager()->gaia_id().has_value());
-  EXPECT_EQ(kGaiaId, assistant_manager()->gaia_id());
-}
-
-TEST_F(AssistantServiceTest, ShouldSendUserInfoWhenAccessTokenIsRefreshed) {
-  assistant_manager()->FinishStart();
-
-  // Reset the AssistantManagerService so it forgets the user info sent when
-  // starting the service.
-  ResetFakeAssistantManager();
-
-  // Now force an access token refresh
-  task_environment()->FastForwardBy(kDefaultTokenExpirationDelay);
-  IssueAccessToken("new token");
-
-  ASSERT_TRUE(assistant_manager()->access_token().has_value());
-  EXPECT_EQ("new token", assistant_manager()->access_token());
-  ASSERT_TRUE(assistant_manager()->gaia_id().has_value());
-  EXPECT_EQ(kGaiaId, assistant_manager()->gaia_id());
-}
-
-TEST_F(AssistantServiceTest, ShouldSetClientStatusToNotReadyWhenStarting) {
-  assistant_manager()->SetStateAndInformObservers(
-      AssistantManagerService::State::STARTING);
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_EQ(client()->status(), AssistantStatus::NOT_READY);
-}
-
-TEST_F(AssistantServiceTest, ShouldKeepClientStatusNotReadyWhenStarted) {
-  // Note: even though we've started, we are not ready to handle the queries
-  // until LibAssistant tells us we are.
-  assistant_manager()->SetStateAndInformObservers(
-      AssistantManagerService::State::STARTED);
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_EQ(client()->status(), AssistantStatus::NOT_READY);
-}
-
-TEST_F(AssistantServiceTest, ShouldSetClientStatusToNewReadyWhenRunning) {
-  assistant_manager()->SetStateAndInformObservers(
-      AssistantManagerService::State::RUNNING);
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_EQ(client()->status(), AssistantStatus::READY);
-}
-
-TEST_F(AssistantServiceTest, ShouldSetClientStatusToNotReadyWhenStopped) {
-  assistant_manager()->SetStateAndInformObservers(
-      AssistantManagerService::State::RUNNING);
-  base::RunLoop().RunUntilIdle();
-
-  StopAssistantAndWait();
-
-  EXPECT_EQ(client()->status(), AssistantStatus::NOT_READY);
-}
-
-TEST_F(AssistantServiceTest, StopImmediatelyIfAssistantIsDisconnected) {
-  // Test is set up as |State::STARTED|.
-  assistant_manager()->FinishStart();
-  EXPECT_STATE(AssistantManagerService::State::RUNNING);
-
-  assistant_manager()->Disconnected();
-  EXPECT_STATE(AssistantManagerService::State::DISCONNECTED);
-  EXPECT_EQ(client()->status(), AssistantStatus::NOT_READY);
-
-  task_environment()->FastForwardBy(
-      GetRestartServiceBackoff()->GetTimeUntilRelease() * 1.2);
-  EXPECT_STATE(AssistantManagerService::State::STARTING);
-}
-
-TEST_F(AssistantServiceTest,
-       IncreaseBackoffIfAssistantIsDisconnectedAfterStarting) {
-  StartAssistantAndWait();
-  EXPECT_STATE(AssistantManagerService::State::STARTING);
-  EXPECT_EQ(GetRestartServiceBackoff()->failure_count(), 0);
-
-  assistant_manager()->Disconnected();
-  EXPECT_STATE(AssistantManagerService::State::DISCONNECTED);
-  EXPECT_EQ(GetRestartServiceBackoff()->failure_count(), 1);
-}
-
-TEST_F(AssistantServiceTest,
-       IncreaseBackoffIfAssistantIsDisconnectedAfterStarted) {
-  assistant_manager()->SetStateAndInformObservers(
-      AssistantManagerService::State::STARTED);
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(GetRestartServiceBackoff()->failure_count(), 0);
-
-  assistant_manager()->Disconnected();
-  EXPECT_STATE(AssistantManagerService::State::DISCONNECTED);
-  EXPECT_EQ(GetRestartServiceBackoff()->failure_count(), 1);
-}
-
-TEST_F(AssistantServiceTest,
-       IncreaseBackoffIfAssistantIsDisconnectedAfterRunning) {
-  assistant_manager()->FinishStart();
-  EXPECT_STATE(AssistantManagerService::State::RUNNING);
-  EXPECT_EQ(GetRestartServiceBackoff()->failure_count(), 0);
-
-  assistant_manager()->Disconnected();
-  EXPECT_STATE(AssistantManagerService::State::DISCONNECTED);
-  EXPECT_EQ(GetRestartServiceBackoff()->failure_count(), 1);
-}
-
-TEST_F(AssistantServiceTest, WillRetryIfAssistantIsDisconnectedAfterRunning) {
-  assistant_manager()->FinishStart();
-  EXPECT_STATE(AssistantManagerService::State::RUNNING);
-  EXPECT_EQ(GetRestartServiceBackoff()->failure_count(), 0);
-
-  assistant_manager()->Disconnected();
-  EXPECT_STATE(AssistantManagerService::State::DISCONNECTED);
-  EXPECT_EQ(GetRestartServiceBackoff()->failure_count(), 1);
-
-  task_environment()->FastForwardBy(
-      GetRestartServiceBackoff()->GetTimeUntilRelease() * 1.2);
-  EXPECT_STATE(AssistantManagerService::State::STARTING);
-}
-
-TEST_F(AssistantServiceTest,
-       WillNotRetryIfAssistantIsDisconnectedAfterRunning) {
-  assistant_manager()->FinishStart();
-  EXPECT_STATE(AssistantManagerService::State::RUNNING);
-  EXPECT_EQ(GetRestartServiceBackoff()->failure_count(), 0);
-
-  // Will retry start for the first `kMaxStartServiceRetries` times.
-  for (int i = 1; i <= kMaxStartServiceRetries; ++i) {
-    assistant_manager()->Disconnected();
-    EXPECT_STATE(AssistantManagerService::State::DISCONNECTED);
-    EXPECT_EQ(GetRestartServiceBackoff()->failure_count(), i);
-
-    task_environment()->FastForwardBy(
-        GetRestartServiceBackoff()->GetTimeUntilRelease() * 1.2);
-    EXPECT_STATE(AssistantManagerService::State::STARTING);
-  }
-
-  // Will not retry start after disconnected `kMaxStartServiceRetries` times.
-  assistant_manager()->Disconnected();
-  EXPECT_STATE(AssistantManagerService::State::DISCONNECTED);
-  EXPECT_EQ(GetRestartServiceBackoff()->failure_count(),
-            kMaxStartServiceRetries + 1);
-
-  task_environment()->FastForwardBy(
-      GetRestartServiceBackoff()->GetTimeUntilRelease() * 1.2);
-  EXPECT_STATE(AssistantManagerService::State::DISCONNECTED);
-}
-
-TEST_F(AssistantServiceTest, DecreaseBackoff) {
-  assistant_manager()->FinishStart();
-  EXPECT_STATE(AssistantManagerService::State::RUNNING);
-  EXPECT_EQ(GetRestartServiceBackoff()->failure_count(), 0);
-
-  for (int i = 1; i <= kMaxStartServiceRetries; ++i) {
-    GetRestartServiceBackoff()->InformOfRequest(/*succeeded=*/false);
-  }
-  EXPECT_EQ(GetRestartServiceBackoff()->failure_count(),
-            kMaxStartServiceRetries);
-
-  assistant_manager()->Disconnected();
-  EXPECT_STATE(AssistantManagerService::State::DISCONNECTED);
-  EXPECT_EQ(GetRestartServiceBackoff()->failure_count(),
-            kMaxStartServiceRetries + 1);
-
-  for (int i = kMaxStartServiceRetries; i >= 0; --i) {
-    task_environment()->FastForwardBy(kAutoRecoverTime);
-    EXPECT_EQ(GetRestartServiceBackoff()->failure_count(), i);
-  }
-
-  // The `failure_count` will not be less than 0.
-  task_environment()->FastForwardBy(kAutoRecoverTime);
-  EXPECT_EQ(GetRestartServiceBackoff()->failure_count(), 0);
-}
-
-TEST_F(AssistantServiceTest, WillRetryAfterDecreaseBackoff) {
-  assistant_manager()->FinishStart();
-  EXPECT_STATE(AssistantManagerService::State::RUNNING);
-  EXPECT_EQ(GetRestartServiceBackoff()->failure_count(), 0);
-
-  for (int i = 1; i <= kMaxStartServiceRetries; ++i) {
-    GetRestartServiceBackoff()->InformOfRequest(/*succeeded=*/false);
-  }
-  EXPECT_EQ(GetRestartServiceBackoff()->failure_count(),
-            kMaxStartServiceRetries);
-
-  assistant_manager()->Disconnected();
-  EXPECT_STATE(AssistantManagerService::State::DISCONNECTED);
-  EXPECT_EQ(GetRestartServiceBackoff()->failure_count(),
-            kMaxStartServiceRetries + 1);
-
-  task_environment()->FastForwardBy(kAutoRecoverTime);
-  EXPECT_EQ(GetRestartServiceBackoff()->failure_count(),
-            kMaxStartServiceRetries);
-
-  task_environment()->FastForwardBy(
-      GetRestartServiceBackoff()->GetTimeUntilRelease() * 1.2);
-  EXPECT_STATE(AssistantManagerService::State::STARTING);
-}
-
-TEST_F(AssistantServiceTest, NoOpWhenRetryStartAfterDecreaseBackoff) {
-  for (int i = 1; i <= kMaxStartServiceRetries; ++i) {
-    GetRestartServiceBackoff()->InformOfRequest(/*succeeded=*/false);
-  }
-  EXPECT_EQ(GetRestartServiceBackoff()->failure_count(),
-            kMaxStartServiceRetries);
-
-  assistant_manager()->FinishStart();
-  EXPECT_STATE(AssistantManagerService::State::RUNNING);
-
-  DecreaseStartServiceBackoff();
-  EXPECT_EQ(GetRestartServiceBackoff()->failure_count(),
-            kMaxStartServiceRetries - 1);
-
-  task_environment()->FastForwardBy(
-      GetRestartServiceBackoff()->GetTimeUntilRelease() * 1.2);
-  EXPECT_STATE(AssistantManagerService::State::RUNNING);
-}
-
-TEST_F(AssistantServiceTest, ResetBackoffAfterReEnableSettings) {
-  assistant_manager()->FinishStart();
-  EXPECT_STATE(AssistantManagerService::State::RUNNING);
-  EXPECT_EQ(GetRestartServiceBackoff()->failure_count(), 0);
-
-  for (int i = 1; i <= kMaxStartServiceRetries; ++i) {
-    GetRestartServiceBackoff()->InformOfRequest(/*succeeded=*/false);
-  }
-  EXPECT_EQ(GetRestartServiceBackoff()->failure_count(),
-            kMaxStartServiceRetries);
-
-  assistant_manager()->Disconnected();
-  EXPECT_STATE(AssistantManagerService::State::DISCONNECTED);
-  EXPECT_EQ(GetRestartServiceBackoff()->failure_count(),
-            kMaxStartServiceRetries + 1);
-
-  StopAssistantAndWait();
-  StartAssistantAndWait();
-  EXPECT_EQ(GetRestartServiceBackoff()->failure_count(), 0);
-}
-
-TEST_F(AssistantServiceTest, WillStartAfterReEnableSettings) {
-  for (int i = 1; i <= kMaxStartServiceRetries + 1; ++i) {
-    GetRestartServiceBackoff()->InformOfRequest(/*succeeded=*/false);
-  }
-  EXPECT_EQ(GetRestartServiceBackoff()->failure_count(),
-            kMaxStartServiceRetries + 1);
-
-  StopAssistantAndWait();
-  StartAssistantAndWait();
-  EXPECT_EQ(GetRestartServiceBackoff()->failure_count(), 0);
-  EXPECT_STATE(AssistantManagerService::State::STARTING);
-}
-
-TEST_F(AssistantServiceTest, WillNotStartAfterMaxRetry_OnTokenRefreshed) {
-  ResetFakeAssistantManager();
-  // Now force an access token refresh.
-  task_environment()->FastForwardBy(kDefaultTokenExpirationDelay);
-  IssueAccessToken("new token");
-  EXPECT_STATE(AssistantManagerService::State::STARTING);
-
-  // Now force an access token refresh.
-  task_environment()->FastForwardBy(kDefaultTokenExpirationDelay);
-  for (int i = 1; i <= kMaxStartServiceRetries; ++i) {
-    GetRestartServiceBackoff()->InformOfRequest(/*succeeded=*/false);
-  }
-  EXPECT_EQ(GetRestartServiceBackoff()->failure_count(),
-            kMaxStartServiceRetries);
-
-  assistant_manager()->Disconnected();
-  EXPECT_STATE(AssistantManagerService::State::DISCONNECTED);
-  EXPECT_EQ(GetRestartServiceBackoff()->failure_count(),
-            kMaxStartServiceRetries + 1);
-
-  IssueAccessToken("new token");
-  EXPECT_STATE(AssistantManagerService::State::DISCONNECTED);
-}
-
-TEST_F(AssistantServiceTest,
-       IncreaseFailuresPrefIfAssistantIsDisconnectedAfterStarting) {
-  StartAssistantAndWait();
-  EXPECT_STATE(AssistantManagerService::State::STARTING);
-  EXPECT_EQ(GetNumberOfFailuresSinceLastServiceRun(), 0);
-
-  assistant_manager()->Disconnected();
-  EXPECT_STATE(AssistantManagerService::State::DISCONNECTED);
-  EXPECT_EQ(GetNumberOfFailuresSinceLastServiceRun(), 1);
-}
-
-TEST_F(AssistantServiceTest,
-       IncreaseFailuresPrefIfAssistantIsDisconnectedAfterStarted) {
-  assistant_manager()->SetStateAndInformObservers(
-      AssistantManagerService::State::STARTED);
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(GetNumberOfFailuresSinceLastServiceRun(), 0);
-
-  assistant_manager()->Disconnected();
-  EXPECT_STATE(AssistantManagerService::State::DISCONNECTED);
-  EXPECT_EQ(GetNumberOfFailuresSinceLastServiceRun(), 1);
-}
-
-TEST_F(AssistantServiceTest,
-       IncreaseFailuresPrefIfAssistantIsDisconnectedAfterRunning) {
-  assistant_manager()->FinishStart();
-  EXPECT_STATE(AssistantManagerService::State::RUNNING);
-  EXPECT_EQ(GetNumberOfFailuresSinceLastServiceRun(), 0);
-
-  assistant_manager()->Disconnected();
-  EXPECT_STATE(AssistantManagerService::State::DISCONNECTED);
-  EXPECT_EQ(GetNumberOfFailuresSinceLastServiceRun(), 1);
-}
-
-TEST_F(AssistantServiceTest, ShouldRetryBasedOnNumberOfFailures) {
-  assistant_manager()->FinishStart();
-  EXPECT_STATE(AssistantManagerService::State::RUNNING);
-  EXPECT_EQ(GetNumberOfFailuresSinceLastServiceRun(), 0);
-
-  // Set pref kNumFailuresSinceLastServiceRun to `kMaxStartServiceRetries - 1`,
-  // disconnect will retry.
-  SetNumberOfFailuresSinceLastServiceRun(kMaxStartServiceRetries - 1);
-  assistant_manager()->Disconnected();
-  EXPECT_STATE(AssistantManagerService::State::DISCONNECTED);
-  EXPECT_EQ(GetRestartServiceBackoff()->failure_count(), 1);
-  EXPECT_EQ(GetNumberOfFailuresSinceLastServiceRun(), kMaxStartServiceRetries);
-
-  task_environment()->FastForwardBy(
-      GetRestartServiceBackoff()->GetTimeUntilRelease() * 1.2);
-  EXPECT_STATE(AssistantManagerService::State::STARTING);
-
-  // Pref kNumFailuresSinceLastServiceRun is kMaxStartServiceRetries now,
-  // disconnect will not retry.
-  assistant_manager()->Disconnected();
-  EXPECT_STATE(AssistantManagerService::State::DISCONNECTED);
-  EXPECT_EQ(GetRestartServiceBackoff()->failure_count(), 2);
-  EXPECT_EQ(GetNumberOfFailuresSinceLastServiceRun(),
-            kMaxStartServiceRetries + 1);
-
-  task_environment()->FastForwardBy(
-      GetRestartServiceBackoff()->GetTimeUntilRelease() * 1.2);
-  EXPECT_STATE(AssistantManagerService::State::DISCONNECTED);
-}
-
-TEST_F(AssistantServiceTest,
-       DecreaseBackoffRetryWillNotBasedOnNumberOfFailures) {
-  assistant_manager()->FinishStart();
-  EXPECT_STATE(AssistantManagerService::State::RUNNING);
-  EXPECT_EQ(GetRestartServiceBackoff()->failure_count(), 0);
-  EXPECT_EQ(GetNumberOfFailuresSinceLastServiceRun(), 0);
-
-  for (int i = 1; i <= kMaxStartServiceRetries; ++i) {
-    GetRestartServiceBackoff()->InformOfRequest(/*succeeded=*/false);
-  }
-  EXPECT_EQ(GetRestartServiceBackoff()->failure_count(),
-            kMaxStartServiceRetries);
-
-  assistant_manager()->Disconnected();
-  EXPECT_STATE(AssistantManagerService::State::DISCONNECTED);
-  EXPECT_EQ(GetRestartServiceBackoff()->failure_count(),
-            kMaxStartServiceRetries + 1);
-  EXPECT_EQ(GetNumberOfFailuresSinceLastServiceRun(), 1);
-
-  // Decreasing backoff will retry.
-  task_environment()->FastForwardBy(kAutoRecoverTime);
-  EXPECT_EQ(GetRestartServiceBackoff()->failure_count(),
-            kMaxStartServiceRetries);
-
-  task_environment()->FastForwardBy(
-      GetRestartServiceBackoff()->GetTimeUntilRelease() * 1.2);
-  EXPECT_STATE(AssistantManagerService::State::STARTING);
-
-  // Set pref kNumFailuresSinceLastServiceRun to > `kMaxStartServiceRetries`,
-  // decreasing backoff still will retry.
-  SetNumberOfFailuresSinceLastServiceRun(kMaxStartServiceRetries + 1);
-  task_environment()->FastForwardBy(kAutoRecoverTime);
-  EXPECT_EQ(GetRestartServiceBackoff()->failure_count(),
-            kMaxStartServiceRetries - 1);
-  EXPECT_EQ(GetNumberOfFailuresSinceLastServiceRun(),
-            kMaxStartServiceRetries + 1);
-
-  task_environment()->FastForwardBy(
-      GetRestartServiceBackoff()->GetTimeUntilRelease() * 1.2);
-  EXPECT_STATE(AssistantManagerService::State::STARTING);
-}
-
-TEST_F(AssistantServiceTest,
-       WillNotResetNumberOfFailuresAfterReEnableSettings) {
-  assistant_manager()->FinishStart();
-  EXPECT_STATE(AssistantManagerService::State::RUNNING);
-  EXPECT_EQ(GetRestartServiceBackoff()->failure_count(), 0);
-  EXPECT_EQ(GetNumberOfFailuresSinceLastServiceRun(), 0);
-
-  assistant_manager()->Disconnected();
-  EXPECT_STATE(AssistantManagerService::State::DISCONNECTED);
-  EXPECT_EQ(GetRestartServiceBackoff()->failure_count(), 1);
-  EXPECT_EQ(GetNumberOfFailuresSinceLastServiceRun(), 1);
-
-  StopAssistantAndWait();
-  StartAssistantAndWait();
-  EXPECT_EQ(GetRestartServiceBackoff()->failure_count(), 0);
-  EXPECT_EQ(GetNumberOfFailuresSinceLastServiceRun(), 1);
-}
-
-TEST_F(AssistantServiceTest,
-       WillStartAfterReEnableSettingsWithMaxNumberOfFailures) {
-  SetNumberOfFailuresSinceLastServiceRun(kMaxStartServiceRetries + 1);
-  EXPECT_EQ(GetNumberOfFailuresSinceLastServiceRun(),
-            kMaxStartServiceRetries + 1);
-
-  StopAssistantAndWait();
-  StartAssistantAndWait();
-  EXPECT_EQ(GetNumberOfFailuresSinceLastServiceRun(),
-            kMaxStartServiceRetries + 1);
-  EXPECT_STATE(AssistantManagerService::State::STARTING);
-}
-
-TEST_F(AssistantServiceTest, ResetNumberOfFailuresAfterRunning) {
-  SetNumberOfFailuresSinceLastServiceRun(kMaxStartServiceRetries + 1);
-  EXPECT_EQ(GetNumberOfFailuresSinceLastServiceRun(),
-            kMaxStartServiceRetries + 1);
-
-  assistant_manager()->FinishStart();
-  EXPECT_STATE(AssistantManagerService::State::RUNNING);
-  EXPECT_EQ(GetNumberOfFailuresSinceLastServiceRun(), 0);
-}
-
-TEST_F(AssistantServiceTest,
-       WillStartAfterMaxNumberOfFailures_OnTokenRefreshed) {
-  ResetFakeAssistantManager();
-  // Now force an access token refresh.
-  task_environment()->FastForwardBy(kDefaultTokenExpirationDelay);
-  IssueAccessToken("new token");
-  EXPECT_STATE(AssistantManagerService::State::STARTING);
-
-  // Now force an access token refresh.
-  task_environment()->FastForwardBy(kDefaultTokenExpirationDelay);
-  GetRestartServiceBackoff()->InformOfRequest(/*succeeded=*/false);
-  EXPECT_EQ(GetRestartServiceBackoff()->failure_count(), 1);
-  SetNumberOfFailuresSinceLastServiceRun(kMaxStartServiceRetries + 1);
-  EXPECT_EQ(GetNumberOfFailuresSinceLastServiceRun(),
-            kMaxStartServiceRetries + 1);
-
-  assistant_manager()->Disconnected();
-  EXPECT_STATE(AssistantManagerService::State::DISCONNECTED);
-  EXPECT_EQ(GetRestartServiceBackoff()->failure_count(), 2);
-  EXPECT_EQ(GetNumberOfFailuresSinceLastServiceRun(),
-            kMaxStartServiceRetries + 2);
-
-  IssueAccessToken("new token");
-  EXPECT_STATE(AssistantManagerService::State::DISCONNECTED);
-
-  // First decreasing backoff will not restart service.
-  task_environment()->FastForwardBy(kAutoRecoverTime);
-  EXPECT_EQ(GetRestartServiceBackoff()->failure_count(), 1);
-  EXPECT_EQ(GetNumberOfFailuresSinceLastServiceRun(),
-            kMaxStartServiceRetries + 2);
-
-  task_environment()->FastForwardBy(
-      GetRestartServiceBackoff()->GetTimeUntilRelease() * 1.2);
-  EXPECT_STATE(AssistantManagerService::State::DISCONNECTED);
-
-  // Second decreasing backoff will not restart service.
-  task_environment()->FastForwardBy(kAutoRecoverTime);
-  EXPECT_EQ(GetRestartServiceBackoff()->failure_count(), 0);
-  EXPECT_EQ(GetNumberOfFailuresSinceLastServiceRun(),
-            kMaxStartServiceRetries + 2);
-
-  task_environment()->FastForwardBy(
-      GetRestartServiceBackoff()->GetTimeUntilRelease() * 1.2);
-  EXPECT_STATE(AssistantManagerService::State::STARTING);
-}
-
-}  // namespace ash::assistant
diff --git a/chromeos/ash/services/assistant/test_support/fake_assistant_manager_service_impl.cc b/chromeos/ash/services/assistant/test_support/fake_assistant_manager_service_impl.cc
deleted file mode 100644
index cd3b94a..0000000
--- a/chromeos/ash/services/assistant/test_support/fake_assistant_manager_service_impl.cc
+++ /dev/null
@@ -1,162 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chromeos/ash/services/assistant/test_support/fake_assistant_manager_service_impl.h"
-
-#include <utility>
-
-namespace ash::assistant {
-
-FakeAssistantManagerServiceImpl::FakeAssistantManagerServiceImpl() = default;
-
-FakeAssistantManagerServiceImpl::~FakeAssistantManagerServiceImpl() = default;
-
-void FakeAssistantManagerServiceImpl::FinishStart() {
-  SetStateAndInformObservers(State::RUNNING);
-}
-
-void FakeAssistantManagerServiceImpl::Start(const std::optional<UserInfo>& user,
-                                            bool enable_hotword) {
-  SetStateAndInformObservers(State::STARTING);
-  SetUser(user);
-}
-
-void FakeAssistantManagerServiceImpl::Stop() {
-  SetStateAndInformObservers(State::STOPPING);
-  SetStateAndInformObservers(State::STOPPED);
-  state_observers_.Clear();
-}
-
-void FakeAssistantManagerServiceImpl::Disconnected() {
-  SetStateAndInformObservers(State::DISCONNECTED);
-  state_observers_.Clear();
-}
-
-void FakeAssistantManagerServiceImpl::SetUser(
-    const std::optional<UserInfo>& user) {
-  if (user) {
-    gaia_id_ = user.value().gaia_id;
-    access_token_ = user.value().access_token;
-  } else {
-    gaia_id_ = std::nullopt;
-    access_token_ = std::nullopt;
-  }
-}
-
-void FakeAssistantManagerServiceImpl::EnableListening(bool enable) {}
-
-void FakeAssistantManagerServiceImpl::EnableHotword(bool enable) {}
-
-void FakeAssistantManagerServiceImpl::SetArcPlayStoreEnabled(bool enabled) {}
-
-void FakeAssistantManagerServiceImpl::SetAssistantContextEnabled(bool enabled) {
-}
-
-AssistantManagerService::State FakeAssistantManagerServiceImpl::GetState()
-    const {
-  return state_;
-}
-
-AssistantSettings* FakeAssistantManagerServiceImpl::GetAssistantSettings() {
-  return &assistant_settings_;
-}
-
-void FakeAssistantManagerServiceImpl::AddAndFireStateObserver(
-    StateObserver* observer) {
-  state_observers_.AddObserver(observer);
-  observer->OnStateChanged(GetState());
-}
-
-void FakeAssistantManagerServiceImpl::RemoveStateObserver(
-    const StateObserver* observer) {
-  state_observers_.RemoveObserver(observer);
-}
-
-void FakeAssistantManagerServiceImpl::UpdateInternalMediaPlayerStatus(
-    MediaSessionAction action) {}
-
-void FakeAssistantManagerServiceImpl::StartEditReminderInteraction(
-    const std::string& client_id) {}
-
-void FakeAssistantManagerServiceImpl::StartTextInteraction(
-    const std::string& query,
-    AssistantQuerySource source,
-    bool allow_tts) {}
-
-void FakeAssistantManagerServiceImpl::StartVoiceInteraction() {}
-
-void FakeAssistantManagerServiceImpl::StopActiveInteraction(
-    bool cancel_conversation) {}
-
-void FakeAssistantManagerServiceImpl::AddAssistantInteractionSubscriber(
-    AssistantInteractionSubscriber* subscriber) {}
-
-void FakeAssistantManagerServiceImpl::RemoveAssistantInteractionSubscriber(
-    AssistantInteractionSubscriber* subscriber) {}
-
-mojo::PendingReceiver<libassistant::mojom::NotificationDelegate>
-FakeAssistantManagerServiceImpl::GetPendingNotificationDelegate() {
-  return mojo::PendingReceiver<libassistant::mojom::NotificationDelegate>();
-}
-
-void FakeAssistantManagerServiceImpl::RetrieveNotification(
-    const AssistantNotification& notification,
-    int action_index) {}
-
-void FakeAssistantManagerServiceImpl::DismissNotification(
-    const AssistantNotification& notification) {}
-
-void FakeAssistantManagerServiceImpl::OnAccessibilityStatusChanged(
-    bool spoken_feedback_enabled) {}
-
-void FakeAssistantManagerServiceImpl::OnColorModeChanged(
-    bool dark_mode_enabled) {}
-
-void FakeAssistantManagerServiceImpl::SendAssistantFeedback(
-    const AssistantFeedback& feedback) {}
-
-void FakeAssistantManagerServiceImpl::AddTimeToTimer(const std::string& id,
-                                                     base::TimeDelta duration) {
-}
-
-void FakeAssistantManagerServiceImpl::PauseTimer(const std::string& id) {}
-
-void FakeAssistantManagerServiceImpl::RemoveAlarmOrTimer(
-    const std::string& id) {}
-
-void FakeAssistantManagerServiceImpl::ResumeTimer(const std::string& id) {}
-
-void FakeAssistantManagerServiceImpl::SetStateAndInformObservers(
-    State new_state) {
-  State old_state = state_;
-  state_ = new_state;
-
-  // In reality we will not skip states, i.e. we will always get |STARTING|
-  // before ever encountering |STARTED|. As such our fake implementation will
-  // send out all intermediate states between |old_state| and |new_state|.
-  // |DISCONNECTED| is different, could be sent before |STARTED| and |RUNNING|.
-  if (new_state == State::DISCONNECTED) {
-    for (auto& observer : state_observers_) {
-      observer.OnStateChanged(state_);
-    }
-    return;
-  }
-
-  MaybeSendStateChange(State::STOPPED, old_state, new_state);
-  MaybeSendStateChange(State::STARTING, old_state, new_state);
-  MaybeSendStateChange(State::STARTED, old_state, new_state);
-  MaybeSendStateChange(State::RUNNING, old_state, new_state);
-  MaybeSendStateChange(State::STOPPING, old_state, new_state);
-}
-
-void FakeAssistantManagerServiceImpl::MaybeSendStateChange(State state,
-                                                           State old_state,
-                                                           State target_state) {
-  if (state > old_state && state <= target_state) {
-    for (auto& observer : state_observers_)
-      observer.OnStateChanged(state);
-  }
-}
-
-}  // namespace ash::assistant
diff --git a/chromeos/ash/services/assistant/test_support/fake_assistant_manager_service_impl.h b/chromeos/ash/services/assistant/test_support/fake_assistant_manager_service_impl.h
deleted file mode 100644
index 61ac3a4..0000000
--- a/chromeos/ash/services/assistant/test_support/fake_assistant_manager_service_impl.h
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROMEOS_ASH_SERVICES_ASSISTANT_TEST_SUPPORT_FAKE_ASSISTANT_MANAGER_SERVICE_IMPL_H_
-#define CHROMEOS_ASH_SERVICES_ASSISTANT_TEST_SUPPORT_FAKE_ASSISTANT_MANAGER_SERVICE_IMPL_H_
-
-#include <memory>
-#include <optional>
-#include <string>
-#include <vector>
-
-#include "base/component_export.h"
-#include "base/observer_list.h"
-#include "chromeos/ash/services/assistant/assistant_manager_service.h"
-#include "chromeos/ash/services/assistant/test_support/fake_assistant_settings_impl.h"
-#include "chromeos/ash/services/libassistant/public/mojom/notification_delegate.mojom-forward.h"
-#include "google_apis/gaia/gaia_id.h"
-#include "mojo/public/cpp/bindings/pending_receiver.h"
-#include "mojo/public/cpp/bindings/pending_remote.h"
-
-namespace ash::assistant {
-
-using media_session::mojom::MediaSessionAction;
-
-// Stub implementation of AssistantManagerService.
-// Will return deterministic result for testing.
-class COMPONENT_EXPORT(ASSISTANT_SERVICE) FakeAssistantManagerServiceImpl
-    : public AssistantManagerService {
- public:
-  FakeAssistantManagerServiceImpl();
-
-  FakeAssistantManagerServiceImpl(const FakeAssistantManagerServiceImpl&) =
-      delete;
-  FakeAssistantManagerServiceImpl& operator=(
-      const FakeAssistantManagerServiceImpl&) = delete;
-
-  ~FakeAssistantManagerServiceImpl() override;
-
-  void FinishStart();
-
-  // assistant::AssistantManagerService overrides
-  void Start(const std::optional<UserInfo>& user, bool enable_hotword) override;
-  void Stop() override;
-  void SetUser(const std::optional<UserInfo>& user) override;
-  void EnableListening(bool enable) override;
-  void EnableHotword(bool enable) override;
-  void SetArcPlayStoreEnabled(bool enabled) override;
-  void SetAssistantContextEnabled(bool enable) override;
-  State GetState() const override;
-  AssistantSettings* GetAssistantSettings() override;
-  void AddAuthenticationStateObserver(
-      AuthenticationStateObserver* observer) override {}
-  void AddAndFireStateObserver(StateObserver* observer) override;
-  void RemoveStateObserver(const StateObserver* observer) override;
-  void SyncDeviceAppsStatus() override {}
-  void UpdateInternalMediaPlayerStatus(MediaSessionAction action) override;
-
-  // Assistant overrides:
-  void StartEditReminderInteraction(const std::string& client_id) override;
-  void StartTextInteraction(const std::string& query,
-                            AssistantQuerySource source,
-                            bool allow_tts) override;
-  void StartVoiceInteraction() override;
-  void StopActiveInteraction(bool cancel_conversation) override;
-  void AddAssistantInteractionSubscriber(
-      AssistantInteractionSubscriber* subscriber) override;
-  void AddRemoteConversationObserver(ConversationObserver* observer) override {}
-  void RemoveAssistantInteractionSubscriber(
-      AssistantInteractionSubscriber* subscriber) override;
-  mojo::PendingReceiver<libassistant::mojom::NotificationDelegate>
-  GetPendingNotificationDelegate() override;
-  void RetrieveNotification(const AssistantNotification& notification,
-                            int action_index) override;
-  void DismissNotification(const AssistantNotification& notification) override;
-  void OnAccessibilityStatusChanged(bool spoken_feedback_enabled) override;
-  void OnColorModeChanged(bool dark_mode_enabled) override;
-  void SendAssistantFeedback(const AssistantFeedback& feedback) override;
-  void AddTimeToTimer(const std::string& id, base::TimeDelta duration) override;
-  void PauseTimer(const std::string& id) override;
-  void RemoveAlarmOrTimer(const std::string& id) override;
-  void ResumeTimer(const std::string& id) override;
-
-  // Update the state to the corresponding value, and inform the
-  // |AssistantStateObserver| of the change.
-  void SetStateAndInformObservers(State new_state);
-
-  // Return the access token that was passed to |SetUser|.
-  std::optional<std::string> access_token() { return access_token_; }
-  // Return the Gaia ID that was passed to |SetUser|.
-  const std::optional<GaiaId>& gaia_id() const { return gaia_id_; }
-
-  void Disconnected();
-
- private:
-  // Send out a |AssistantStateObserver::OnStateChange(state)| event if we are
-  // transitioning from a prior state to a later state.
-  void MaybeSendStateChange(State state, State old_state, State target_state);
-
-  State state_ = State::STOPPED;
-  std::optional<GaiaId> gaia_id_;
-  std::optional<std::string> access_token_;
-  FakeAssistantSettingsImpl assistant_settings_;
-  base::ObserverList<StateObserver> state_observers_;
-};
-
-}  // namespace ash::assistant
-
-#endif  // CHROMEOS_ASH_SERVICES_ASSISTANT_TEST_SUPPORT_FAKE_ASSISTANT_MANAGER_SERVICE_IMPL_H_
diff --git a/chromeos/ash/services/assistant/test_support/fake_assistant_settings_impl.cc b/chromeos/ash/services/assistant/test_support/fake_assistant_settings_impl.cc
deleted file mode 100644
index c33c8cb..0000000
--- a/chromeos/ash/services/assistant/test_support/fake_assistant_settings_impl.cc
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chromeos/ash/services/assistant/test_support/fake_assistant_settings_impl.h"
-
-#include <utility>
-
-#include "base/functional/callback.h"
-#include "chromeos/ash/services/assistant/public/proto/get_settings_ui.pb.h"
-#include "chromeos/ash/services/assistant/public/proto/settings_ui.pb.h"
-
-namespace ash::assistant {
-
-FakeAssistantSettingsImpl::FakeAssistantSettingsImpl() = default;
-
-FakeAssistantSettingsImpl::~FakeAssistantSettingsImpl() = default;
-
-void FakeAssistantSettingsImpl::GetSettings(const std::string& selector,
-                                            GetSettingsCallback callback) {
-  // Create a fake response
-  SettingsUi settings_ui;
-  settings_ui.mutable_consent_flow_ui()->set_consent_status(
-      ConsentFlowUi_ConsentStatus_ALREADY_CONSENTED);
-  std::move(callback).Run(settings_ui.SerializeAsString());
-}
-
-void FakeAssistantSettingsImpl::GetSettingsWithHeader(
-    const std::string& selector,
-    GetSettingsCallback callback) {
-  // Create a fake response
-  assistant::GetSettingsUiResponse response;
-  response.mutable_settings()->mutable_consent_flow_ui()->set_consent_status(
-      ConsentFlowUi_ConsentStatus_ALREADY_CONSENTED);
-  std::move(callback).Run(response.SerializeAsString());
-}
-
-void FakeAssistantSettingsImpl::UpdateSettings(
-    const std::string& update,
-    UpdateSettingsCallback callback) {
-  std::move(callback).Run(std::string());
-}
-
-void FakeAssistantSettingsImpl::StartSpeakerIdEnrollment(
-    bool skip_cloud_enrollment,
-    base::WeakPtr<SpeakerIdEnrollmentClient> client) {
-  client->OnSpeakerIdEnrollmentDone();
-}
-
-void FakeAssistantSettingsImpl::StopSpeakerIdEnrollment() {}
-
-}  // namespace ash::assistant
diff --git a/chromeos/ash/services/assistant/test_support/fake_assistant_settings_impl.h b/chromeos/ash/services/assistant/test_support/fake_assistant_settings_impl.h
deleted file mode 100644
index dcef2cd..0000000
--- a/chromeos/ash/services/assistant/test_support/fake_assistant_settings_impl.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROMEOS_ASH_SERVICES_ASSISTANT_TEST_SUPPORT_FAKE_ASSISTANT_SETTINGS_IMPL_H_
-#define CHROMEOS_ASH_SERVICES_ASSISTANT_TEST_SUPPORT_FAKE_ASSISTANT_SETTINGS_IMPL_H_
-
-#include <memory>
-#include <string>
-
-#include "chromeos/ash/services/assistant/public/cpp/assistant_settings.h"
-
-namespace ash::assistant {
-
-// TODO(jeroendh): Can be removed once FakeAssistantManagerServiceImpl is gone.
-class FakeAssistantSettingsImpl : public AssistantSettings {
- public:
-  FakeAssistantSettingsImpl();
-  ~FakeAssistantSettingsImpl() override;
-
-  // AssistantSettings overrides:
-  void GetSettings(const std::string& selector,
-                   GetSettingsCallback callback) override;
-  void GetSettingsWithHeader(const std::string& selector,
-                             GetSettingsCallback callback) override;
-  void UpdateSettings(const std::string& update,
-                      UpdateSettingsCallback callback) override;
-  void StartSpeakerIdEnrollment(
-      bool skip_cloud_enrollment,
-      base::WeakPtr<SpeakerIdEnrollmentClient> client) override;
-  void StopSpeakerIdEnrollment() override;
-  void SyncSpeakerIdEnrollmentStatus() override {}
-};
-
-}  // namespace ash::assistant
-
-#endif  // CHROMEOS_ASH_SERVICES_ASSISTANT_TEST_SUPPORT_FAKE_ASSISTANT_SETTINGS_IMPL_H_
diff --git a/chromeos/ash/services/assistant/test_support/fake_libassistant_service.cc b/chromeos/ash/services/assistant/test_support/fake_libassistant_service.cc
deleted file mode 100644
index cfb593dd..0000000
--- a/chromeos/ash/services/assistant/test_support/fake_libassistant_service.cc
+++ /dev/null
@@ -1,89 +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.
-
-#include "chromeos/ash/services/assistant/test_support/fake_libassistant_service.h"
-
-#include "chromeos/ash/services/libassistant/public/mojom/notification_delegate.mojom-forward.h"
-#include "mojo/public/cpp/bindings/pending_remote.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace ash::assistant {
-
-FakeLibassistantService::FakeLibassistantService() : receiver_(this) {}
-
-FakeLibassistantService::~FakeLibassistantService() = default;
-
-void FakeLibassistantService::Bind(
-    mojo::PendingReceiver<libassistant::mojom::LibassistantService>
-        pending_receiver) {
-  EXPECT_FALSE(receiver_.is_bound())
-      << "Cannot bind the LibassistantService twice";
-  receiver_.Bind(std::move(pending_receiver));
-}
-
-void FakeLibassistantService::Unbind() {
-  receiver_.reset();
-  service_controller().Unbind();
-}
-
-mojo::PendingReceiver<libassistant::mojom::MediaController>
-FakeLibassistantService::GetMediaControllerPendingReceiver() {
-  EXPECT_TRUE(media_controller_pending_receiver_.is_valid());
-  return std::move(media_controller_pending_receiver_);
-}
-
-mojo::PendingRemote<libassistant::mojom::MediaDelegate>
-FakeLibassistantService::GetMediaDelegatePendingRemote() {
-  EXPECT_TRUE(media_delegate_pending_remote_.is_valid());
-  return std::move(media_delegate_pending_remote_);
-}
-
-mojo::PendingReceiver<libassistant::mojom::SpeakerIdEnrollmentController>
-FakeLibassistantService::GetSpeakerIdEnrollmentControllerPendingReceiver() {
-  EXPECT_TRUE(speaker_id_enrollment_controller_pending_receiver_.is_valid());
-  return std::move(speaker_id_enrollment_controller_pending_receiver_);
-}
-
-void FakeLibassistantService::Bind(
-    mojo::PendingReceiver<libassistant::mojom::AudioInputController>
-        audio_input_controller,
-    mojo::PendingReceiver<libassistant::mojom::ConversationController>
-        conversation_controller,
-    mojo::PendingReceiver<libassistant::mojom::DisplayController>
-        display_controller,
-    mojo::PendingReceiver<libassistant::mojom::MediaController>
-        media_controller,
-    mojo::PendingReceiver<libassistant::mojom::ServiceController>
-        service_controller,
-    mojo::PendingReceiver<libassistant::mojom::SettingsController>
-        settings_controller,
-    mojo::PendingReceiver<libassistant::mojom::SpeakerIdEnrollmentController>
-        speaker_id_enrollment_controller,
-    mojo::PendingReceiver<libassistant::mojom::TimerController>
-        timer_controller,
-    mojo::PendingRemote<libassistant::mojom::AudioOutputDelegate>
-        audio_output_delegate,
-    mojo::PendingRemote<libassistant::mojom::DeviceSettingsDelegate>
-        device_settings_delegate,
-    mojo::PendingRemote<libassistant::mojom::MediaDelegate> media_delegate,
-    mojo::PendingRemote<libassistant::mojom::NotificationDelegate>
-        notification_delegate,
-    mojo::PendingRemote<libassistant::mojom::PlatformDelegate>
-        platform_delegate,
-    mojo::PendingRemote<libassistant::mojom::TimerDelegate> timer_delegate) {
-  service_controller_.Bind(std::move(service_controller),
-                           std::move(settings_controller));
-  media_controller_pending_receiver_ = std::move(media_controller);
-  media_delegate_pending_remote_ = std::move(media_delegate);
-  speaker_id_enrollment_controller_pending_receiver_ =
-      std::move(speaker_id_enrollment_controller);
-}
-
-void FakeLibassistantService::FlushForTesting() {
-  if (receiver_.is_bound()) {
-    receiver_.FlushForTesting();
-  }
-}
-
-}  // namespace ash::assistant
diff --git a/chromeos/ash/services/assistant/test_support/fake_libassistant_service.h b/chromeos/ash/services/assistant/test_support/fake_libassistant_service.h
deleted file mode 100644
index 335cd9a..0000000
--- a/chromeos/ash/services/assistant/test_support/fake_libassistant_service.h
+++ /dev/null
@@ -1,96 +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.
-
-#ifndef CHROMEOS_ASH_SERVICES_ASSISTANT_TEST_SUPPORT_FAKE_LIBASSISTANT_SERVICE_H_
-#define CHROMEOS_ASH_SERVICES_ASSISTANT_TEST_SUPPORT_FAKE_LIBASSISTANT_SERVICE_H_
-
-#include "chromeos/ash/services/assistant/test_support/fake_service_controller.h"
-#include "chromeos/ash/services/libassistant/public/mojom/notification_delegate.mojom-forward.h"
-#include "chromeos/ash/services/libassistant/public/mojom/service.mojom.h"
-#include "chromeos/ash/services/libassistant/public/mojom/speaker_id_enrollment_controller.mojom-forward.h"
-#include "mojo/public/cpp/bindings/pending_receiver.h"
-#include "mojo/public/cpp/bindings/pending_remote.h"
-#include "mojo/public/cpp/bindings/receiver.h"
-
-namespace ash::assistant {
-
-// Fake implementation of the Libassistant Mojom service.
-// It allows hooks to read and control the state of the service.
-class FakeLibassistantService
-    : public libassistant::mojom::LibassistantService {
- public:
-  FakeLibassistantService();
-  FakeLibassistantService(FakeLibassistantService&) = delete;
-  FakeLibassistantService& operator=(FakeLibassistantService&) = delete;
-  ~FakeLibassistantService() override;
-
-  void Bind(mojo::PendingReceiver<libassistant::mojom::LibassistantService>
-                pending_receiver);
-  void Unbind();
-
-  FakeServiceController& service_controller() { return service_controller_; }
-  FakeServiceController& settings_controller() { return service_controller_; }
-
-  // Return the receiver that was passed into the last Bind() call.
-  mojo::PendingReceiver<libassistant::mojom::MediaController>
-  GetMediaControllerPendingReceiver();
-  mojo::PendingRemote<libassistant::mojom::MediaDelegate>
-  GetMediaDelegatePendingRemote();
-  mojo::PendingReceiver<libassistant::mojom::SpeakerIdEnrollmentController>
-  GetSpeakerIdEnrollmentControllerPendingReceiver();
-
-  // mojom::LibassistantService implementation:
-  void Bind(
-      mojo::PendingReceiver<libassistant::mojom::AudioInputController>
-          audio_input_controller,
-      mojo::PendingReceiver<libassistant::mojom::ConversationController>
-          conversation_controller,
-      mojo::PendingReceiver<libassistant::mojom::DisplayController>
-          display_controller,
-      mojo::PendingReceiver<libassistant::mojom::MediaController>
-          media_controller,
-      mojo::PendingReceiver<libassistant::mojom::ServiceController>
-          service_controller,
-      mojo::PendingReceiver<libassistant::mojom::SettingsController>
-          settings_controller,
-      mojo::PendingReceiver<libassistant::mojom::SpeakerIdEnrollmentController>
-          speaker_id_enrollment_controller,
-      mojo::PendingReceiver<libassistant::mojom::TimerController>
-          timer_controller,
-      mojo::PendingRemote<libassistant::mojom::AudioOutputDelegate>
-          audio_output_delegate,
-      mojo::PendingRemote<libassistant::mojom::DeviceSettingsDelegate>
-          device_settings_delegate,
-      mojo::PendingRemote<libassistant::mojom::MediaDelegate> media_delegate,
-      mojo::PendingRemote<libassistant::mojom::NotificationDelegate>
-          notification_delegate,
-      mojo::PendingRemote<libassistant::mojom::PlatformDelegate>
-          platform_delegate,
-      mojo::PendingRemote<libassistant::mojom::TimerDelegate> timer_delegate)
-      override;
-  void AddSpeechRecognitionObserver(
-      mojo::PendingRemote<libassistant::mojom::SpeechRecognitionObserver>
-          observer) override {}
-  void AddAuthenticationStateObserver(
-      mojo::PendingRemote<libassistant::mojom::AuthenticationStateObserver>
-          observer) override {}
-
-  void FlushForTesting();
-
- private:
-  mojo::Receiver<libassistant::mojom::LibassistantService> receiver_;
-
-  mojo::PendingReceiver<libassistant::mojom::MediaController>
-      media_controller_pending_receiver_;
-  mojo::PendingReceiver<libassistant::mojom::SpeakerIdEnrollmentController>
-      speaker_id_enrollment_controller_pending_receiver_;
-  mojo::PendingRemote<libassistant::mojom::MediaDelegate>
-      media_delegate_pending_remote_;
-
-  FakeServiceController service_controller_;
-};
-
-}  // namespace ash::assistant
-
-#endif  // CHROMEOS_ASH_SERVICES_ASSISTANT_TEST_SUPPORT_FAKE_LIBASSISTANT_SERVICE_H_
diff --git a/chromeos/ash/services/assistant/test_support/fake_service_context.cc b/chromeos/ash/services/assistant/test_support/fake_service_context.cc
deleted file mode 100644
index f94338f2..0000000
--- a/chromeos/ash/services/assistant/test_support/fake_service_context.cc
+++ /dev/null
@@ -1,115 +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.
-
-#include <string>
-
-#include "base/task/sequenced_task_runner.h"
-#include "base/task/single_thread_task_runner.h"
-#include "chromeos/ash/services/assistant/public/cpp/device_actions.h"
-#include "chromeos/ash/services/assistant/test_support/fake_service_context.h"
-
-namespace ash::assistant {
-
-/*static*/
-constexpr GaiaId::Literal FakeServiceContext::kGaiaId;
-
-FakeServiceContext::FakeServiceContext() = default;
-
-FakeServiceContext::~FakeServiceContext() = default;
-
-FakeServiceContext& FakeServiceContext::set_assistant_alarm_timer_controller(
-    AssistantAlarmTimerController* value) {
-  assistant_alarm_timer_controller_ = value;
-  return *this;
-}
-
-FakeServiceContext& FakeServiceContext::set_main_task_runner(
-    scoped_refptr<base::SingleThreadTaskRunner> value) {
-  main_task_runner_ = value;
-  return *this;
-}
-
-FakeServiceContext& FakeServiceContext::set_power_manager_client(
-    chromeos::PowerManagerClient* value) {
-  power_manager_client_ = value;
-  return *this;
-}
-
-FakeServiceContext& FakeServiceContext::set_primary_account_gaia_id(
-    const GaiaId& value) {
-  gaia_id_ = value;
-  return *this;
-}
-
-FakeServiceContext& FakeServiceContext::set_assistant_state(
-    AssistantStateBase* value) {
-  assistant_state_ = value;
-  return *this;
-}
-
-FakeServiceContext& FakeServiceContext::set_assistant_notification_controller(
-    AssistantNotificationController* value) {
-  assistant_notification_controller_ = value;
-  return *this;
-}
-
-FakeServiceContext& FakeServiceContext::set_cras_audio_handler(
-    CrasAudioHandler* value) {
-  cras_audio_handler_ = value;
-  return *this;
-}
-
-AssistantAlarmTimerController*
-FakeServiceContext::assistant_alarm_timer_controller() {
-  DCHECK(assistant_alarm_timer_controller_ != nullptr);
-  return assistant_alarm_timer_controller_;
-}
-
-AssistantController* FakeServiceContext::assistant_controller() {
-  NOTIMPLEMENTED();
-  return nullptr;
-}
-
-AssistantNotificationController*
-FakeServiceContext::assistant_notification_controller() {
-  DCHECK(assistant_notification_controller_ != nullptr);
-  return assistant_notification_controller_;
-}
-
-AssistantScreenContextController*
-FakeServiceContext::assistant_screen_context_controller() {
-  NOTIMPLEMENTED();
-  return nullptr;
-}
-
-AssistantStateBase* FakeServiceContext::assistant_state() {
-  DCHECK(assistant_state_ != nullptr);
-  return assistant_state_;
-}
-
-CrasAudioHandler* FakeServiceContext::cras_audio_handler() {
-  DCHECK(cras_audio_handler_ != nullptr);
-  return cras_audio_handler_;
-}
-
-DeviceActions* FakeServiceContext::device_actions() {
-  return DeviceActions::Get();
-}
-
-scoped_refptr<base::SequencedTaskRunner>
-FakeServiceContext::main_task_runner() {
-  DCHECK(main_task_runner_ != nullptr);
-  return main_task_runner_;
-}
-
-chromeos::PowerManagerClient* FakeServiceContext::power_manager_client() {
-  DCHECK(power_manager_client_ != nullptr);
-  return power_manager_client_;
-}
-
-GaiaId FakeServiceContext::primary_account_gaia_id() {
-  return gaia_id_;
-}
-
-}  // namespace ash::assistant
diff --git a/chromeos/ash/services/assistant/test_support/fake_service_context.h b/chromeos/ash/services/assistant/test_support/fake_service_context.h
deleted file mode 100644
index b3363e2..0000000
--- a/chromeos/ash/services/assistant/test_support/fake_service_context.h
+++ /dev/null
@@ -1,68 +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.
-
-#ifndef CHROMEOS_ASH_SERVICES_ASSISTANT_TEST_SUPPORT_FAKE_SERVICE_CONTEXT_H_
-#define CHROMEOS_ASH_SERVICES_ASSISTANT_TEST_SUPPORT_FAKE_SERVICE_CONTEXT_H_
-
-#include "base/memory/raw_ptr.h"
-#include "base/task/sequenced_task_runner.h"
-#include "base/task/single_thread_task_runner.h"
-#include "chromeos/ash/services/assistant/service_context.h"
-#include "google_apis/gaia/gaia_id.h"
-
-namespace ash::assistant {
-
-// Fake implementation of the |ServiceContext| used during the unittests.
-// Every method will assert when called,
-// unless you've provided the object using one of the setter methods.
-class FakeServiceContext : public ServiceContext {
- public:
-  // Gaia ID returned by primary_account_gaia_id() (unless overridden).
-  static constexpr GaiaId::Literal kGaiaId = GaiaId::Literal("<fake-gaia-id>");
-
-  FakeServiceContext();
-  FakeServiceContext(const FakeServiceContext&) = delete;
-  FakeServiceContext& operator=(const FakeServiceContext&) = delete;
-  ~FakeServiceContext() override;
-
-  FakeServiceContext& set_assistant_alarm_timer_controller(
-      AssistantAlarmTimerController*);
-  FakeServiceContext& set_main_task_runner(
-      scoped_refptr<base::SingleThreadTaskRunner>);
-  FakeServiceContext& set_power_manager_client(chromeos::PowerManagerClient*);
-  FakeServiceContext& set_primary_account_gaia_id(const GaiaId&);
-  FakeServiceContext& set_assistant_state(AssistantStateBase*);
-  FakeServiceContext& set_assistant_notification_controller(
-      AssistantNotificationController*);
-  FakeServiceContext& set_cras_audio_handler(CrasAudioHandler*);
-
-  // ServiceContext implementation:
-  AssistantAlarmTimerController* assistant_alarm_timer_controller() override;
-  AssistantController* assistant_controller() override;
-  AssistantNotificationController* assistant_notification_controller() override;
-  AssistantScreenContextController* assistant_screen_context_controller()
-      override;
-  AssistantStateBase* assistant_state() override;
-  CrasAudioHandler* cras_audio_handler() override;
-  DeviceActions* device_actions() override;
-  scoped_refptr<base::SequencedTaskRunner> main_task_runner() override;
-  chromeos::PowerManagerClient* power_manager_client() override;
-  GaiaId primary_account_gaia_id() override;
-
- private:
-  scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
-  raw_ptr<AssistantStateBase> assistant_state_ = nullptr;
-  raw_ptr<chromeos::PowerManagerClient, DanglingUntriaged>
-      power_manager_client_ = nullptr;
-  GaiaId gaia_id_ = kGaiaId;
-  raw_ptr<AssistantAlarmTimerController> assistant_alarm_timer_controller_ =
-      nullptr;
-  raw_ptr<AssistantNotificationController> assistant_notification_controller_ =
-      nullptr;
-  raw_ptr<CrasAudioHandler> cras_audio_handler_ = nullptr;
-};
-
-}  // namespace ash::assistant
-
-#endif  // CHROMEOS_ASH_SERVICES_ASSISTANT_TEST_SUPPORT_FAKE_SERVICE_CONTEXT_H_
diff --git a/chromeos/ash/services/assistant/test_support/fake_service_controller.cc b/chromeos/ash/services/assistant/test_support/fake_service_controller.cc
deleted file mode 100644
index 8aa259f2c..0000000
--- a/chromeos/ash/services/assistant/test_support/fake_service_controller.cc
+++ /dev/null
@@ -1,143 +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.
-
-#include "chromeos/ash/services/assistant/test_support/fake_service_controller.h"
-
-#include "base/task/sequenced_task_runner.h"
-#include "google_apis/gaia/gaia_id.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace ash::assistant {
-
-// A macro which ensures we are running on the mojom thread.
-#define ENSURE_MOJOM_THREAD(method, ...)                                    \
-  if (!mojom_task_runner_->RunsTasksInCurrentSequence()) {                  \
-    mojom_task_runner_->PostTask(                                           \
-        FROM_HERE,                                                          \
-        base::BindOnce(method, weak_factory_.GetWeakPtr(), ##__VA_ARGS__)); \
-    return;                                                                 \
-  }
-
-FakeServiceController::FakeServiceController() {}
-FakeServiceController::~FakeServiceController() = default;
-
-void FakeServiceController::SetState(State new_state) {
-  // SetState() is called from our unittests, but the observers are registered
-  // on the mojom thread so we must switch threads.
-  ENSURE_MOJOM_THREAD(&FakeServiceController::SetState, new_state);
-  DCHECK_NE(state_, new_state);
-
-  state_ = new_state;
-
-  for (auto& observer : state_observers_)
-    observer->OnStateChanged(state_);
-}
-
-void FakeServiceController::Bind(
-    mojo::PendingReceiver<libassistant::mojom::ServiceController>
-        service_receiver,
-    mojo::PendingReceiver<libassistant::mojom::SettingsController>
-        settings_receiver) {
-  service_receiver_.Bind(std::move(service_receiver));
-  settings_receiver_.Bind(std::move(settings_receiver));
-}
-
-void FakeServiceController::Unbind() {
-  // All mojom objects must now be unbound, as that needs to happen on the
-  // same thread as they were bound (which is the background thread).
-  service_receiver_.reset();
-  settings_receiver_.reset();
-  state_observers_.Clear();
-}
-
-void FakeServiceController::BlockStartCalls() {
-  // This lock will be release in |UnblockStartCalls|.
-  start_mutex_.lock();
-}
-
-void FakeServiceController::UnblockStartCalls() {
-  start_mutex_.unlock();
-}
-
-std::string FakeServiceController::access_token() {
-  if (authentication_tokens_.size())
-    return authentication_tokens_[0]->access_token;
-  else
-    return kNoValue;
-}
-
-GaiaId FakeServiceController::gaia_id() {
-  if (authentication_tokens_.size()) {
-    return GaiaId(authentication_tokens_[0]->gaia_id);
-  } else {
-    return GaiaId(kNoValue);
-  }
-}
-
-void FakeServiceController::Initialize(
-    libassistant::mojom::BootupConfigPtr config,
-    mojo::PendingRemote<network::mojom::URLLoaderFactory> url_loader_factory) {
-  mojom_task_runner_ = base::SequencedTaskRunner::GetCurrentDefault();
-  libassistant_config_ = std::move(config);
-
-  authentication_tokens_ =
-      std::move(libassistant_config_->authentication_tokens);
-  dark_mode_enabled_ = libassistant_config_->dark_mode_enabled;
-}
-
-void FakeServiceController::Start() {
-  // Will block if |BlockStartCalls| was invoked.
-  std::lock_guard<std::mutex> lock(start_mutex_);
-
-  SetState(State::kStarted);
-}
-
-void FakeServiceController::Stop() {
-  // Post a delayed task to make it possible to set other state between
-  // kStopping and kStopped.
-  base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
-      FROM_HERE,
-      base::BindOnce(&FakeServiceController::SetState,
-                     weak_factory_.GetWeakPtr(), State::kStopped),
-      base::Milliseconds(1));
-}
-
-void FakeServiceController::ResetAllDataAndStop() {
-  SetState(State::kStopped);
-  has_data_been_reset_ = true;
-}
-
-void FakeServiceController::AddAndFireStateObserver(
-    mojo::PendingRemote<libassistant::mojom::StateObserver> pending_observer) {
-  mojo::Remote<libassistant::mojom::StateObserver> observer(
-      std::move(pending_observer));
-
-  observer->OnStateChanged(state_);
-
-  state_observers_.Add(std::move(observer));
-}
-
-void FakeServiceController::SetAuthenticationTokens(
-    std::vector<libassistant::mojom::AuthenticationTokenPtr> tokens) {
-  authentication_tokens_ = std::move(tokens);
-}
-
-void FakeServiceController::SetDarkModeEnabled(bool value) {
-  dark_mode_enabled_ = value;
-}
-
-void FakeServiceController::UpdateSettings(const std::string& settings,
-                                           UpdateSettingsCallback callback) {
-  // Callback must be called to satisfy the mojom contract.
-  std::move(callback).Run(std::string());
-}
-
-void FakeServiceController::GetSettings(const std::string& selector,
-                                        bool include_header,
-                                        GetSettingsCallback callback) {
-  // Callback must be called to satisfy the mojom contract.
-  std::move(callback).Run(std::string());
-}
-
-}  // namespace ash::assistant
diff --git a/chromeos/ash/services/assistant/test_support/fake_service_controller.h b/chromeos/ash/services/assistant/test_support/fake_service_controller.h
deleted file mode 100644
index dcab584..0000000
--- a/chromeos/ash/services/assistant/test_support/fake_service_controller.h
+++ /dev/null
@@ -1,136 +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.
-
-#ifndef CHROMEOS_ASH_SERVICES_ASSISTANT_TEST_SUPPORT_FAKE_SERVICE_CONTROLLER_H_
-#define CHROMEOS_ASH_SERVICES_ASSISTANT_TEST_SUPPORT_FAKE_SERVICE_CONTROLLER_H_
-
-#include <mutex>
-#include <optional>
-#include <string>
-
-#include "base/memory/weak_ptr.h"
-#include "base/task/sequenced_task_runner.h"
-#include "chromeos/ash/services/libassistant/public/mojom/service_controller.mojom.h"
-#include "chromeos/ash/services/libassistant/public/mojom/settings_controller.mojom.h"
-#include "google_apis/gaia/gaia_id.h"
-#include "mojo/public/cpp/bindings/receiver.h"
-#include "mojo/public/cpp/bindings/remote_set.h"
-
-namespace ash::assistant {
-
-// Fake implementation of the Mojom |ServiceController| and
-// |SettingsController|. This implementation will inform the registered
-// |StateObserver| instances of any state change, just like the real
-// implementation.
-class FakeServiceController : public libassistant::mojom::ServiceController,
-                              public libassistant::mojom::SettingsController {
- public:
-  // Value returned when optional fields |access_token| or |user_id| are
-  // missing. Note we use this instead of a |std::optional| because this
-  // results in a much nicer error message if the test fails. (otherwise you get
-  // a message like this:
-  //     Expected equality of these values:
-  //           "<new-user-id-wrong>"
-  //     with 32-byte object <01-00 snip 00-00>
-  static constexpr const char* kNoValue = "<no-value>";
-
-  using State = libassistant::mojom::ServiceState;
-
-  FakeServiceController();
-  FakeServiceController(FakeServiceController&) = delete;
-  FakeServiceController& operator=(FakeServiceController&) = delete;
-  ~FakeServiceController() override;
-
-  // Puts the service in the given state. Will inform all observers of the state
-  // change.
-  void SetState(State new_state);
-  State state() const { return state_; }
-
-  // Returns the Libassistant config that was passed to Initialize().
-  const libassistant::mojom::BootupConfig& libassistant_config() {
-    DCHECK(libassistant_config_);
-    return *libassistant_config_;
-  }
-
-  void Bind(mojo::PendingReceiver<libassistant::mojom::ServiceController>
-                service_receiver,
-            mojo::PendingReceiver<libassistant::mojom::SettingsController>
-                settings_receiver);
-  void Unbind();
-
-  // Call this to block any call to |Start|. The observers will not be invoked
-  // as long as the start call is blocked. Unblock these calls using
-  // |UnblockStartCalls|. This is not enabled by default, so unless you call
-  // |BlockStartCalls| any |Start| call will simply finish immediately.
-  void BlockStartCalls();
-  void UnblockStartCalls();
-
-  // Return the access-token that was passed to |SetAuthenticationTokens|, or
-  // |kNoValue| if an empty vector was passed in.
-  std::string access_token();
-  // Return the user-id that was passed to |SetAuthenticationTokens|, or
-  // |kNoValue| if an empty vector was passed in.
-  GaiaId gaia_id();
-
-  // True if ResetAllDataAndStop() was called.
-  bool has_data_been_reset() const { return has_data_been_reset_; }
-
-  std::optional<bool> dark_mode_enabled() const { return dark_mode_enabled_; }
-
- private:
-  // mojom::ServiceController implementation:
-  void Initialize(libassistant::mojom::BootupConfigPtr config,
-                  mojo::PendingRemote<network::mojom::URLLoaderFactory>
-                      url_loader_factory) override;
-  void Start() override;
-  void Stop() override;
-  void ResetAllDataAndStop() override;
-  void AddAndFireStateObserver(
-      mojo::PendingRemote<libassistant::mojom::StateObserver> pending_observer)
-      override;
-
-  // mojom::SettingsController implementation:
-  void SetAuthenticationTokens(
-      std::vector<libassistant::mojom::AuthenticationTokenPtr> tokens) override;
-  void SetListeningEnabled(bool value) override {}
-  void SetLocale(const std::string& value) override {}
-  void SetSpokenFeedbackEnabled(bool value) override {}
-  void SetDarkModeEnabled(bool value) override;
-  void UpdateSettings(const std::string& settings,
-                      UpdateSettingsCallback callback) override;
-  void GetSettings(const std::string& selector,
-                   bool include_header,
-                   GetSettingsCallback callback) override;
-  void SetHotwordEnabled(bool value) override {}
-
-  // Mutex taken in |Start| to allow the calls to block if |BlockStartCalls| was
-  // called.
-  std::mutex start_mutex_;
-
-  // Config passed to LibAssistant when it was started.
-  libassistant::mojom::BootupConfigPtr libassistant_config_;
-
-  // True if ResetAllDataAndStop() was called.
-  bool has_data_been_reset_ = false;
-
-  // Authentication tokens passed to SetAuthenticationTokens().
-  std::vector<libassistant::mojom::AuthenticationTokenPtr>
-      authentication_tokens_;
-
-  std::optional<bool> dark_mode_enabled_;
-
-  State state_ = State::kStopped;
-  mojo::Receiver<libassistant::mojom::ServiceController> service_receiver_{
-      this};
-  mojo::Receiver<libassistant::mojom::SettingsController> settings_receiver_{
-      this};
-  mojo::RemoteSet<libassistant::mojom::StateObserver> state_observers_;
-  scoped_refptr<base::SequencedTaskRunner> mojom_task_runner_;
-
-  base::WeakPtrFactory<FakeServiceController> weak_factory_{this};
-};
-
-}  // namespace ash::assistant
-
-#endif  // CHROMEOS_ASH_SERVICES_ASSISTANT_TEST_SUPPORT_FAKE_SERVICE_CONTROLLER_H_
diff --git a/chromeos/ash/services/assistant/test_support/fully_initialized_assistant_state.cc b/chromeos/ash/services/assistant/test_support/fully_initialized_assistant_state.cc
deleted file mode 100644
index 1df9168..0000000
--- a/chromeos/ash/services/assistant/test_support/fully_initialized_assistant_state.cc
+++ /dev/null
@@ -1,38 +0,0 @@
-// 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.
-
-#include "chromeos/ash/services/assistant/test_support/fully_initialized_assistant_state.h"
-
-namespace ash::assistant {
-
-FullyInitializedAssistantState::FullyInitializedAssistantState() {
-  InitializeAllValues();
-}
-
-void FullyInitializedAssistantState::SetAssistantEnabled(bool enabled) {
-  settings_enabled_ = enabled;
-
-  for (auto& observer : observers_)
-    observer.OnAssistantSettingsEnabled(settings_enabled_.value());
-}
-
-void FullyInitializedAssistantState::SetContextEnabled(bool enabled) {
-  context_enabled_ = enabled;
-}
-
-void FullyInitializedAssistantState::InitializeAllValues() {
-  settings_enabled_ = true;
-  consent_status_ = prefs::ConsentStatus::kActivityControlAccepted;
-  context_enabled_ = true;
-  hotword_enabled_ = true;
-  hotword_always_on_ = true;
-  launch_with_mic_open_ = true;
-  notification_enabled_ = true;
-  allowed_state_ = AssistantAllowedState::ALLOWED;
-  locale_ = "en_US";
-  arc_play_store_enabled_ = true;
-  locked_full_screen_enabled_ = true;
-}
-
-}  // namespace ash::assistant
diff --git a/chromeos/ash/services/assistant/test_support/fully_initialized_assistant_state.h b/chromeos/ash/services/assistant/test_support/fully_initialized_assistant_state.h
deleted file mode 100644
index 079194e..0000000
--- a/chromeos/ash/services/assistant/test_support/fully_initialized_assistant_state.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// 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.
-
-#ifndef CHROMEOS_ASH_SERVICES_ASSISTANT_TEST_SUPPORT_FULLY_INITIALIZED_ASSISTANT_STATE_H_
-#define CHROMEOS_ASH_SERVICES_ASSISTANT_TEST_SUPPORT_FULLY_INITIALIZED_ASSISTANT_STATE_H_
-
-#include "ash/public/cpp/assistant/assistant_state.h"
-
-namespace ash::assistant {
-
-// Instance of |AssistantState| where every std::optional value has a non-null
-// value. All values will be set to their equivalent of enabled.
-class FullyInitializedAssistantState : public AssistantState {
- public:
-  FullyInitializedAssistantState();
-
-  FullyInitializedAssistantState(const FullyInitializedAssistantState&) =
-      delete;
-  FullyInitializedAssistantState& operator=(
-      const FullyInitializedAssistantState&) = delete;
-
-  ~FullyInitializedAssistantState() override = default;
-
-  void SetAssistantEnabled(bool enabled);
-
-  void SetContextEnabled(bool enabled);
-
- private:
-  void InitializeAllValues();
-};
-
-}  // namespace ash::assistant
-
-#endif  // CHROMEOS_ASH_SERVICES_ASSISTANT_TEST_SUPPORT_FULLY_INITIALIZED_ASSISTANT_STATE_H_
diff --git a/chromeos/ash/services/assistant/test_support/libassistant_media_controller_mock.cc b/chromeos/ash/services/assistant/test_support/libassistant_media_controller_mock.cc
deleted file mode 100644
index ec904162..0000000
--- a/chromeos/ash/services/assistant/test_support/libassistant_media_controller_mock.cc
+++ /dev/null
@@ -1,22 +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.
-
-#include "chromeos/ash/services/assistant/test_support/libassistant_media_controller_mock.h"
-
-namespace ash::assistant {
-
-LibassistantMediaControllerMock::LibassistantMediaControllerMock() = default;
-LibassistantMediaControllerMock::~LibassistantMediaControllerMock() = default;
-
-void LibassistantMediaControllerMock::Bind(
-    mojo::PendingReceiver<libassistant::mojom::MediaController>
-        pending_receiver) {
-  receiver_.Bind(std::move(pending_receiver));
-}
-
-void LibassistantMediaControllerMock::FlushForTesting() {
-  receiver_.FlushForTesting();
-}
-
-}  // namespace ash::assistant
diff --git a/chromeos/ash/services/assistant/test_support/libassistant_media_controller_mock.h b/chromeos/ash/services/assistant/test_support/libassistant_media_controller_mock.h
deleted file mode 100644
index f311b68..0000000
--- a/chromeos/ash/services/assistant/test_support/libassistant_media_controller_mock.h
+++ /dev/null
@@ -1,42 +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.
-
-#ifndef CHROMEOS_ASH_SERVICES_ASSISTANT_TEST_SUPPORT_LIBASSISTANT_MEDIA_CONTROLLER_MOCK_H_
-#define CHROMEOS_ASH_SERVICES_ASSISTANT_TEST_SUPPORT_LIBASSISTANT_MEDIA_CONTROLLER_MOCK_H_
-
-#include "chromeos/ash/services/libassistant/public/mojom/media_controller.mojom.h"
-
-#include "mojo/public/cpp/bindings/receiver.h"
-#include "mojo/public/cpp/bindings/remote.h"
-#include "testing/gmock/include/gmock/gmock.h"
-
-namespace ash::assistant {
-
-class LibassistantMediaControllerMock
-    : public libassistant::mojom::MediaController {
- public:
-  LibassistantMediaControllerMock();
-  LibassistantMediaControllerMock(const LibassistantMediaControllerMock&) =
-      delete;
-  LibassistantMediaControllerMock& operator=(
-      const LibassistantMediaControllerMock&) = delete;
-  ~LibassistantMediaControllerMock() override;
-
-  void Bind(mojo::PendingReceiver<libassistant::mojom::MediaController>);
-  void FlushForTesting();
-
-  // libassistant::mojom::MediaController implementation:
-  MOCK_METHOD(void, ResumeInternalMediaPlayer, ());
-  MOCK_METHOD(void, PauseInternalMediaPlayer, ());
-  MOCK_METHOD(void,
-              SetExternalPlaybackState,
-              (libassistant::mojom::MediaStatePtr state));
-
- private:
-  mojo::Receiver<libassistant::mojom::MediaController> receiver_{this};
-};
-
-}  // namespace ash::assistant
-
-#endif  // CHROMEOS_ASH_SERVICES_ASSISTANT_TEST_SUPPORT_LIBASSISTANT_MEDIA_CONTROLLER_MOCK_H_
diff --git a/chromeos/ash/services/assistant/test_support/mock_assistant.cc b/chromeos/ash/services/assistant/test_support/mock_assistant.cc
deleted file mode 100644
index 81e7b6a..0000000
--- a/chromeos/ash/services/assistant/test_support/mock_assistant.cc
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chromeos/ash/services/assistant/test_support/mock_assistant.h"
-#include "chromeos/ash/services/libassistant/public/cpp/assistant_feedback.h"
-
-namespace ash::assistant {
-
-MockAssistant::MockAssistant() = default;
-
-MockAssistant::~MockAssistant() = default;
-
-}  // namespace ash::assistant
diff --git a/chromeos/ash/services/assistant/test_support/mock_assistant.h b/chromeos/ash/services/assistant/test_support/mock_assistant.h
deleted file mode 100644
index 88b7ab3..0000000
--- a/chromeos/ash/services/assistant/test_support/mock_assistant.h
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROMEOS_ASH_SERVICES_ASSISTANT_TEST_SUPPORT_MOCK_ASSISTANT_H_
-#define CHROMEOS_ASH_SERVICES_ASSISTANT_TEST_SUPPORT_MOCK_ASSISTANT_H_
-
-#include <string>
-#include <vector>
-
-#include "chromeos/ash/services/assistant/public/cpp/assistant_service.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "ui/accessibility/mojom/ax_assistant_structure.mojom.h"
-
-namespace ash::assistant {
-
-class MockAssistant : public Assistant {
- public:
-  MockAssistant();
-
-  MockAssistant(const MockAssistant&) = delete;
-  MockAssistant& operator=(const MockAssistant&) = delete;
-
-  ~MockAssistant() override;
-
-  MOCK_METHOD1(StartEditReminderInteraction, void(const std::string&));
-
-  MOCK_METHOD(void,
-              StartTextInteraction,
-              (const std::string&, AssistantQuerySource, bool));
-
-  MOCK_METHOD0(StartVoiceInteraction, void());
-
-  MOCK_METHOD1(StopActiveInteraction, void(bool));
-
-  MOCK_METHOD1(AddAssistantInteractionSubscriber,
-               void(AssistantInteractionSubscriber*));
-
-  MOCK_METHOD1(RemoveAssistantInteractionSubscriber,
-               void(AssistantInteractionSubscriber*));
-
-  MOCK_METHOD2(RetrieveNotification, void(const AssistantNotification&, int));
-
-  MOCK_METHOD1(DismissNotification, void(const AssistantNotification&));
-
-  MOCK_METHOD1(OnAccessibilityStatusChanged, void(bool));
-
-  MOCK_METHOD1(SendAssistantFeedback, void(const AssistantFeedback&));
-
-  MOCK_METHOD0(StopAlarmTimerRinging, void());
-  MOCK_METHOD1(CreateTimer, void(base::TimeDelta));
-};
-
-}  // namespace ash::assistant
-
-#endif  // CHROMEOS_ASH_SERVICES_ASSISTANT_TEST_SUPPORT_MOCK_ASSISTANT_H_
diff --git a/chromeos/ash/services/assistant/test_support/mock_assistant_interaction_subscriber.cc b/chromeos/ash/services/assistant/test_support/mock_assistant_interaction_subscriber.cc
deleted file mode 100644
index 444680b1..0000000
--- a/chromeos/ash/services/assistant/test_support/mock_assistant_interaction_subscriber.cc
+++ /dev/null
@@ -1,14 +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.
-
-#include "chromeos/ash/services/assistant/test_support/mock_assistant_interaction_subscriber.h"
-
-namespace ash::assistant {
-
-MockAssistantInteractionSubscriber::MockAssistantInteractionSubscriber() =
-    default;
-MockAssistantInteractionSubscriber::~MockAssistantInteractionSubscriber() =
-    default;
-
-}  // namespace ash::assistant
diff --git a/chromeos/ash/services/assistant/test_support/mock_assistant_interaction_subscriber.h b/chromeos/ash/services/assistant/test_support/mock_assistant_interaction_subscriber.h
deleted file mode 100644
index 0a4a4d8..0000000
--- a/chromeos/ash/services/assistant/test_support/mock_assistant_interaction_subscriber.h
+++ /dev/null
@@ -1,63 +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.
-
-#ifndef CHROMEOS_ASH_SERVICES_ASSISTANT_TEST_SUPPORT_MOCK_ASSISTANT_INTERACTION_SUBSCRIBER_H_
-#define CHROMEOS_ASH_SERVICES_ASSISTANT_TEST_SUPPORT_MOCK_ASSISTANT_INTERACTION_SUBSCRIBER_H_
-
-#include <string>
-#include <vector>
-
-#include "chromeos/ash/services/assistant/public/cpp/assistant_service.h"
-#include "testing/gmock/include/gmock/gmock.h"
-
-namespace ash::assistant {
-
-class MockAssistantInteractionSubscriber
-    : public AssistantInteractionSubscriber {
- public:
-  MockAssistantInteractionSubscriber();
-  MockAssistantInteractionSubscriber(
-      const MockAssistantInteractionSubscriber&) = delete;
-  MockAssistantInteractionSubscriber& operator=(
-      const MockAssistantInteractionSubscriber&) = delete;
-  ~MockAssistantInteractionSubscriber() override;
-
-  // AssistantInteractionSubscriber:
-  MOCK_METHOD(void,
-              OnInteractionStarted,
-              (const AssistantInteractionMetadata&),
-              (override));
-  MOCK_METHOD(void,
-              OnInteractionFinished,
-              (AssistantInteractionResolution),
-              (override));
-  MOCK_METHOD(void,
-              OnHtmlResponse,
-              (const std::string&, const std::string&),
-              (override));
-  MOCK_METHOD(void,
-              OnSuggestionsResponse,
-              (const std::vector<AssistantSuggestion>&),
-              (override));
-  MOCK_METHOD(void, OnTextResponse, (const std::string&), (override));
-  MOCK_METHOD(void, OnOpenUrlResponse, (const ::GURL&, bool), (override));
-  MOCK_METHOD(void, OnOpenAppResponse, (const AndroidAppInfo&), (override));
-  MOCK_METHOD(void, OnSpeechRecognitionStarted, (), (override));
-  MOCK_METHOD(void,
-              OnSpeechRecognitionIntermediateResult,
-              (const std::string&, const std::string&),
-              (override));
-  MOCK_METHOD(void, OnSpeechRecognitionEndOfUtterance, (), (override));
-  MOCK_METHOD(void,
-              OnSpeechRecognitionFinalResult,
-              (const std::string&),
-              (override));
-  MOCK_METHOD(void, OnSpeechLevelUpdated, (float), (override));
-  MOCK_METHOD(void, OnTtsStarted, (bool), (override));
-  MOCK_METHOD(void, OnWaitStarted, (), (override));
-};
-
-}  // namespace ash::assistant
-
-#endif  // CHROMEOS_ASH_SERVICES_ASSISTANT_TEST_SUPPORT_MOCK_ASSISTANT_INTERACTION_SUBSCRIBER_H_
diff --git a/chromeos/ash/services/assistant/test_support/scoped_device_actions.cc b/chromeos/ash/services/assistant/test_support/scoped_device_actions.cc
deleted file mode 100644
index ab8d953..0000000
--- a/chromeos/ash/services/assistant/test_support/scoped_device_actions.cc
+++ /dev/null
@@ -1,25 +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.
-
-#include "chromeos/ash/services/assistant/test_support/scoped_device_actions.h"
-
-#include <utility>
-
-namespace ash::assistant {
-
-void ScopedDeviceActions::GetScreenBrightnessLevel(
-    GetScreenBrightnessLevelCallback callback) {
-  std::move(callback).Run(/*success=*/true, current_brightness_);
-}
-
-bool ScopedDeviceActions::OpenAndroidApp(const AndroidAppInfo& app_info) {
-  return true;
-}
-
-AppStatus ScopedDeviceActions::GetAndroidAppStatus(
-    const AndroidAppInfo& app_info) {
-  return AppStatus::kAvailable;
-}
-
-}  // namespace ash::assistant
diff --git a/chromeos/ash/services/assistant/test_support/scoped_device_actions.h b/chromeos/ash/services/assistant/test_support/scoped_device_actions.h
deleted file mode 100644
index da6a1a7..0000000
--- a/chromeos/ash/services/assistant/test_support/scoped_device_actions.h
+++ /dev/null
@@ -1,46 +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.
-
-#ifndef CHROMEOS_ASH_SERVICES_ASSISTANT_TEST_SUPPORT_SCOPED_DEVICE_ACTIONS_H_
-#define CHROMEOS_ASH_SERVICES_ASSISTANT_TEST_SUPPORT_SCOPED_DEVICE_ACTIONS_H_
-
-#include <string>
-#include <vector>
-
-#include "chromeos/ash/services/assistant/public/cpp/device_actions.h"
-
-namespace ash::assistant {
-
-class ScopedDeviceActions : DeviceActions {
- public:
-  ScopedDeviceActions() = default;
-  ~ScopedDeviceActions() override = default;
-
-  // assistant::DeviceActions overrides:
-  void SetWifiEnabled(bool enabled) override {}
-  void SetBluetoothEnabled(bool enabled) override {}
-  void GetScreenBrightnessLevel(
-      GetScreenBrightnessLevelCallback callback) override;
-  void SetScreenBrightnessLevel(double level, bool gradual) override {}
-  void SetNightLightEnabled(bool enabled) override {}
-  void SetSwitchAccessEnabled(bool enabled) override {}
-  bool OpenAndroidApp(const AndroidAppInfo& app_info) override;
-  AppStatus GetAndroidAppStatus(const AndroidAppInfo& app_info) override;
-  void LaunchAndroidIntent(const std::string& intent) override {}
-  void AddAndFireAppListEventSubscriber(
-      AppListEventSubscriber* subscriber) override {}
-  void RemoveAppListEventSubscriber(
-      AppListEventSubscriber* subscriber) override {}
-
-  // Set the brightness value that will be returned by
-  // GetScreenBrightnessLevel();
-  void set_current_brightness(double value) { current_brightness_ = value; }
-
- private:
-  double current_brightness_ = 0.0;
-};
-
-}  // namespace ash::assistant
-
-#endif  // CHROMEOS_ASH_SERVICES_ASSISTANT_TEST_SUPPORT_SCOPED_DEVICE_ACTIONS_H_
diff --git a/chromeos/ash/services/assistant/timer_host.cc b/chromeos/ash/services/assistant/timer_host.cc
deleted file mode 100644
index 4e2114b..0000000
--- a/chromeos/ash/services/assistant/timer_host.cc
+++ /dev/null
@@ -1,96 +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.
-
-#include "chromeos/ash/services/assistant/timer_host.h"
-
-#include "ash/public/cpp/assistant/controller/assistant_alarm_timer_controller.h"
-#include "base/memory/raw_ref.h"
-#include "chromeos/ash/services/assistant/public/cpp/features.h"
-#include "chromeos/ash/services/assistant/service_context.h"
-#include "chromeos/ash/services/libassistant/public/cpp/assistant_timer.h"
-#include "chromeos/ash/services/libassistant/public/mojom/timer_controller.mojom.h"
-#include "mojo/public/cpp/bindings/receiver.h"
-
-namespace ash::assistant {
-
-////////////////////////////////////////////////////////////////////////////////
-// TimerDelegateImpl
-////////////////////////////////////////////////////////////////////////////////
-
-class TimerHost::TimerDelegateImpl : public libassistant::mojom::TimerDelegate {
- public:
-  explicit TimerDelegateImpl(
-      mojo::PendingReceiver<TimerDelegate> pending_receiver,
-      ServiceContext* context)
-      : receiver_(this, std::move(pending_receiver)), context_(*context) {}
-  TimerDelegateImpl(const TimerDelegateImpl&) = delete;
-  TimerDelegateImpl& operator=(const TimerDelegateImpl&) = delete;
-  ~TimerDelegateImpl() override = default;
-
- private:
-  // libassistant::mojom::TimerDelegate implementation:
-  void OnTimerStateChanged(const std::vector<AssistantTimer>& timers) override {
-    assistant_alarm_timer_controller().OnTimerStateChanged(timers);
-  }
-
-  AssistantAlarmTimerController& assistant_alarm_timer_controller() {
-    auto* result = context_->assistant_alarm_timer_controller();
-    DCHECK(result);
-    return *result;
-  }
-
-  mojo::Receiver<TimerDelegate> receiver_;
-
-  // Owned by the parent |Service|.
-  const raw_ref<ServiceContext> context_;
-};
-
-////////////////////////////////////////////////////////////////////////////////
-// TimerHost
-////////////////////////////////////////////////////////////////////////////////
-
-TimerHost::TimerHost(ServiceContext* context) : context_(*context) {
-  DCHECK(context);
-}
-
-TimerHost::~TimerHost() = default;
-
-void TimerHost::Initialize(
-    libassistant::mojom::TimerController* libassistant_controller,
-    mojo::PendingReceiver<libassistant::mojom::TimerDelegate> delegate) {
-  DCHECK(!libassistant_controller_);
-
-  timer_delegate_ =
-      std::make_unique<TimerDelegateImpl>(std::move(delegate), &*context_);
-  libassistant_controller_ = libassistant_controller;
-}
-
-void TimerHost::Stop() {
-  timer_delegate_.reset();
-  libassistant_controller_ = nullptr;
-}
-
-void TimerHost::AddTimeToTimer(const std::string& id,
-                               base::TimeDelta duration) {
-  libassistant_controller().AddTimeToTimer(id, duration);
-}
-
-void TimerHost::PauseTimer(const std::string& id) {
-  libassistant_controller().PauseTimer(id);
-}
-
-void TimerHost::RemoveTimer(const std::string& id) {
-  libassistant_controller().RemoveTimer(id);
-}
-
-void TimerHost::ResumeTimer(const std::string& id) {
-  libassistant_controller().ResumeTimer(id);
-}
-
-libassistant::mojom::TimerController& TimerHost::libassistant_controller() {
-  DCHECK(libassistant_controller_);
-  return *libassistant_controller_;
-}
-
-}  // namespace ash::assistant
diff --git a/chromeos/ash/services/assistant/timer_host.h b/chromeos/ash/services/assistant/timer_host.h
deleted file mode 100644
index dcb7d12..0000000
--- a/chromeos/ash/services/assistant/timer_host.h
+++ /dev/null
@@ -1,68 +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.
-
-#ifndef CHROMEOS_ASH_SERVICES_ASSISTANT_TIMER_HOST_H_
-#define CHROMEOS_ASH_SERVICES_ASSISTANT_TIMER_HOST_H_
-
-#include <memory>
-#include <string>
-
-#include "base/memory/raw_ptr.h"
-#include "base/memory/raw_ref.h"
-#include "base/time/time.h"
-#include "chromeos/ash/services/libassistant/public/mojom/timer_controller.mojom-forward.h"
-#include "mojo/public/cpp/bindings/pending_receiver.h"
-
-namespace ash {
-
-class AssistantAlarmTimerController;
-
-namespace assistant {
-
-class ServiceContext;
-
-// Handles all timer related interactions with Libassistant, which can broadly
-// be separated in 2 responsibilities:
-//   1) Let Libassistant know about updates to the timers (pause/add time/...).
-//   2) Let |AssistantAlarmTimerController| know when Libassistant adds or
-//      removes timers.
-class TimerHost {
- public:
-  explicit TimerHost(ServiceContext* context);
-  TimerHost(const TimerHost&) = delete;
-  TimerHost& operator=(const TimerHost&) = delete;
-  ~TimerHost();
-
-  void Initialize(
-      libassistant::mojom::TimerController* libassistant_controller,
-      mojo::PendingReceiver<libassistant::mojom::TimerDelegate> delegate);
-  void Stop();
-
-  void AddTimeToTimer(const std::string& id, base::TimeDelta duration);
-  void PauseTimer(const std::string& id);
-  void RemoveTimer(const std::string& id);
-  void ResumeTimer(const std::string& id);
-
- private:
-  class TimerDelegateImpl;
-
-  AssistantAlarmTimerController* assistant_alarm_timer_controller();
-  libassistant::mojom::TimerController& libassistant_controller();
-
-  bool IsStopped() const;
-
-  // Owned by our parent |AssistantManagerServiceImpl|.
-  raw_ptr<libassistant::mojom::TimerController, DanglingUntriaged>
-      libassistant_controller_ = nullptr;
-  std::unique_ptr<TimerDelegateImpl> timer_delegate_;
-
-  // Owned by the parent |Service| which will destroy |this| before
-  // |context_|.
-  const raw_ref<ServiceContext> context_;
-};
-
-}  // namespace assistant
-}  // namespace ash
-
-#endif  // CHROMEOS_ASH_SERVICES_ASSISTANT_TIMER_HOST_H_
diff --git a/chromeos/chromeos_export.h b/chromeos/chromeos_export.h
index 1d61005..41d2f2a2 100644
--- a/chromeos/chromeos_export.h
+++ b/chromeos/chromeos_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(CHROMEOS_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(CHROMEOS_IMPLEMENTATION) || defined(IS_CHROMEOS_SYSTEM_IMPL)
 #define CHROMEOS_EXPORT __attribute__((visibility("default")))
-#else
-#define CHROMEOS_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/chromeos/profiles/bigcore.afdo.newest.txt b/chromeos/profiles/bigcore.afdo.newest.txt
index 3c4b506d..4ab69b6 100644
--- a/chromeos/profiles/bigcore.afdo.newest.txt
+++ b/chromeos/profiles/bigcore.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-bigcore-137-7103.40-1745804339-benchmark-138.0.7155.0-r1-redacted.afdo.xz
+chromeos-chrome-amd64-bigcore-138-7137.0-1746414666-benchmark-138.0.7164.0-r1-redacted.afdo.xz
diff --git a/clank b/clank
index a85f502..11c3547 160000
--- a/clank
+++ b/clank
@@ -1 +1 @@
-Subproject commit a85f502de34463769977268999f0dbce0321efa6
+Subproject commit 11c3547fd4f0706067ad840ff190038861085741
diff --git a/components/BUILD.gn b/components/BUILD.gn
index 596e9b07..5c59002 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -809,6 +809,7 @@
       "//components/safe_browsing/content/common:file_type_policies_unittest",
       "//components/safe_browsing/content/common:unit_tests",
       "//components/safe_browsing/core/browser:safe_browsing_url_checker_unittest",
+      "//components/safe_browsing/core/common/proto_to_value:unit_tests",
     ]
   }
 
diff --git a/components/affiliations/core/browser/affiliation_utils.cc b/components/affiliations/core/browser/affiliation_utils.cc
index 1dd7d3a..efeffcf5 100644
--- a/components/affiliations/core/browser/affiliation_utils.cc
+++ b/components/affiliations/core/browser/affiliation_utils.cc
@@ -449,25 +449,6 @@
   return os << facet_uri.potentially_invalid_spec();
 }
 
-bool operator==(const FacetBrandingInfo& lhs, const FacetBrandingInfo& rhs) {
-  return std::tie(lhs.name, lhs.icon_url) == std::tie(rhs.name, rhs.icon_url);
-}
-
-bool operator!=(const FacetBrandingInfo& lhs, const FacetBrandingInfo& rhs) {
-  return !(lhs == rhs);
-}
-
-bool operator==(const Facet& lhs, const Facet& rhs) {
-  return std::tie(lhs.uri, lhs.branding_info, lhs.main_domain,
-                  lhs.change_password_url) ==
-         std::tie(rhs.uri, rhs.branding_info, rhs.main_domain,
-                  rhs.change_password_url);
-}
-
-bool operator!=(const Facet& lhs, const Facet& rhs) {
-  return !(lhs == rhs);
-}
-
 bool operator==(const GroupedFacets& lhs, const GroupedFacets& rhs) {
   if (!std::ranges::is_permutation(lhs.facets, rhs.facets)) {
     return false;
@@ -475,10 +456,6 @@
   return lhs.branding_info == rhs.branding_info;
 }
 
-bool operator!=(const GroupedFacets& lhs, const GroupedFacets& rhs) {
-  return !(lhs == rhs);
-}
-
 bool AreEquivalenceClassesEqual(const AffiliatedFacets& a,
                                 const AffiliatedFacets& b) {
   return a.size() == b.size() &&
diff --git a/components/affiliations/core/browser/affiliation_utils.h b/components/affiliations/core/browser/affiliation_utils.h
index b6c0354..38fabb2 100644
--- a/components/affiliations/core/browser/affiliation_utils.h
+++ b/components/affiliations/core/browser/affiliation_utils.h
@@ -157,6 +157,9 @@
 // The branding information for a given facet. Corresponds to the |BrandingInfo|
 // message in affiliation_api.proto.
 struct FacetBrandingInfo {
+  friend bool operator==(const FacetBrandingInfo&,
+                         const FacetBrandingInfo&) = default;
+
   // Human readable name of this facet, or empty if this information is not
   // available.
   //
@@ -189,6 +192,8 @@
   Facet& operator=(const Facet& other);
   Facet& operator=(Facet&& other);
 
+  friend bool operator==(const Facet&, const Facet&) = default;
+
   FacetURI uri;
   FacetBrandingInfo branding_info;
   GURL change_password_url;
@@ -272,12 +277,7 @@
 std::ostream& operator<<(std::ostream& os, const FacetURI& facet_uri);
 
 // Needed for testing.
-bool operator==(const FacetBrandingInfo& lhs, const FacetBrandingInfo& rhs);
-bool operator!=(const FacetBrandingInfo& lhs, const FacetBrandingInfo& rhs);
-bool operator==(const Facet& lhs, const Facet& rhs);
-bool operator!=(const Facet& lhs, const Facet& rhs);
 bool operator==(const GroupedFacets& lhs, const GroupedFacets& rhs);
-bool operator!=(const GroupedFacets& lhs, const GroupedFacets& rhs);
 
 struct FacetURIHash {
   size_t operator()(const FacetURI& facet_uri) const {
diff --git a/components/android_autofill/browser/android_autofill_features.cc b/components/android_autofill/browser/android_autofill_features.cc
index fc33231..f1afcd1 100644
--- a/components/android_autofill/browser/android_autofill_features.cc
+++ b/components/android_autofill/browser/android_autofill_features.cc
@@ -22,7 +22,8 @@
 
 const base::Feature* const kFeaturesExposedToJava[] = {
     &kAndroidAutofillDeprecateAccessibilityApi,
-    &kAutofillVirtualViewStructureAndroidInCct};
+    &kAutofillVirtualViewStructureAndroidInCct,
+    &kAndroidAutofillLazyFrameworkWrapper};
 
 }  // namespace
 
@@ -47,6 +48,15 @@
              "AutofillVirtualViewStructureAndroidPasskeyLongPress",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+// If enabled, the AutofillManagerWrapper class will not be initialized when the
+// AutofillProvider Java class is initialized. Some apps do not use Autofill at
+// all, yet they incur the latency cost of initializing the wrapper. This
+// experiment tests whether lazily initializing the wrapper will cause any
+// issues.
+BASE_FEATURE(kAndroidAutofillLazyFrameworkWrapper,
+             "AndroidAutofillLazyFrameworkWrapper",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 static jlong JNI_AndroidAutofillFeatures_GetFeature(JNIEnv* env, jint ordinal) {
   return reinterpret_cast<jlong>(kFeaturesExposedToJava[ordinal]);
 }
diff --git a/components/android_autofill/browser/android_autofill_features.h b/components/android_autofill/browser/android_autofill_features.h
index fdd3bf5..223b38e 100644
--- a/components/android_autofill/browser/android_autofill_features.h
+++ b/components/android_autofill/browser/android_autofill_features.h
@@ -15,6 +15,8 @@
 
 BASE_DECLARE_FEATURE(kAutofillVirtualViewStructureAndroidPasskeyLongPress);
 
+BASE_DECLARE_FEATURE(kAndroidAutofillLazyFrameworkWrapper);
+
 }  // namespace autofill::features
 
 #endif  // COMPONENTS_ANDROID_AUTOFILL_BROWSER_ANDROID_AUTOFILL_FEATURES_H_
diff --git a/components/android_autofill/browser/java/src/org/chromium/components/autofill/AndroidAutofillFeatures.java b/components/android_autofill/browser/java/src/org/chromium/components/autofill/AndroidAutofillFeatures.java
index ce6a5d3..179e3f4 100644
--- a/components/android_autofill/browser/java/src/org/chromium/components/autofill/AndroidAutofillFeatures.java
+++ b/components/android_autofill/browser/java/src/org/chromium/components/autofill/AndroidAutofillFeatures.java
@@ -21,14 +21,19 @@
 public class AndroidAutofillFeatures extends Features {
     public static final String ANDROID_AUTOFILL_DEPRECATE_ACCESSIBILITY_API_NAME =
             "AndroidAutofillDeprecateAccessibilityApi";
-    public static final AndroidAutofillFeatures ANDROID_AUTOFILL_DEPRECATE_ACCESSIBILITY_API =
-            new AndroidAutofillFeatures(0, ANDROID_AUTOFILL_DEPRECATE_ACCESSIBILITY_API_NAME);
+    public static final String ANDROID_AUTOFILL_LAZY_FRAMEWORK_WRAPPER_NAME =
+            "AndroidAutofillLazyFrameworkWrapper";
+
     public static final String ANDROID_AUTOFILL_VIRTUAL_VIEW_STRUCTURE_ANDROID_IN_CCT_NAME =
             "AutofillVirtualViewStructureAndroidInCct";
+    public static final AndroidAutofillFeatures ANDROID_AUTOFILL_DEPRECATE_ACCESSIBILITY_API =
+            new AndroidAutofillFeatures(0, ANDROID_AUTOFILL_DEPRECATE_ACCESSIBILITY_API_NAME);
     public static final AndroidAutofillFeatures
             ANDROID_AUTOFILL_VIRTUAL_VIEW_STRUCTURE_ANDROID_IN_CCT =
                     new AndroidAutofillFeatures(
                             1, ANDROID_AUTOFILL_VIRTUAL_VIEW_STRUCTURE_ANDROID_IN_CCT_NAME);
+    public static final AndroidAutofillFeatures ANDROID_AUTOFILL_LAZY_FRAMEWORK_WRAPPER =
+            new AndroidAutofillFeatures(2, ANDROID_AUTOFILL_LAZY_FRAMEWORK_WRAPPER_NAME);
     private final int mOrdinal;
 
     private AndroidAutofillFeatures(int ordinal, String name) {
diff --git a/components/android_autofill/browser/java/src/org/chromium/components/autofill/AutofillManagerWrapper.java b/components/android_autofill/browser/java/src/org/chromium/components/autofill/AutofillManagerWrapper.java
index 14e36fc..c71600f 100644
--- a/components/android_autofill/browser/java/src/org/chromium/components/autofill/AutofillManagerWrapper.java
+++ b/components/android_autofill/browser/java/src/org/chromium/components/autofill/AutofillManagerWrapper.java
@@ -49,7 +49,7 @@
         private WeakReference<AutofillManagerWrapper> mManager;
 
         public AutofillInputUiMonitor(AutofillManagerWrapper manager) {
-            mManager = new WeakReference<AutofillManagerWrapper>(manager);
+            mManager = new WeakReference<>(manager);
         }
 
         @Override
diff --git a/components/android_autofill/browser/java/src/org/chromium/components/autofill/AutofillProvider.java b/components/android_autofill/browser/java/src/org/chromium/components/autofill/AutofillProvider.java
index a9708f5..2cc1a4e6 100644
--- a/components/android_autofill/browser/java/src/org/chromium/components/autofill/AutofillProvider.java
+++ b/components/android_autofill/browser/java/src/org/chromium/components/autofill/AutofillProvider.java
@@ -66,7 +66,13 @@
     private static AutofillManagerWrapperFactoryForTesting sAutofillManagerFactoryForTesting;
 
     private final String mProviderName;
+
+    /**
+     * Do not use this object directly as it may not be initialized. Use the {@link
+     * #getAutofillManagerWrapper()} method instead.
+     */
     private AutofillManagerWrapper mAutofillManager;
+
     private ViewGroup mContainerView;
     private WebContents mWebContents;
 
@@ -75,7 +81,7 @@
     private AutofillProviderUMA mAutofillUMA;
     private AutofillManagerWrapper.InputUiObserver mInputUiObserver;
     private long mAutofillTriggeredTimeMillis;
-    private Context mContext;
+    private final Context mContext;
     private AutofillPopup mDatalistPopup;
     private AutofillSuggestion[] mDatalistSuggestions;
     private WebContentsAccessibility mWebContentsAccessibility;
@@ -95,36 +101,52 @@
         try (ScopedSysTraceEvent e = ScopedSysTraceEvent.scoped("AutofillProvider.constructor")) {
             if (sAutofillManagerFactoryForTesting != null) {
                 mAutofillManager = sAutofillManagerFactoryForTesting.create(context);
+                maybeInitializeUmaRecorder(context);
+                maybeInitializeInputObserver();
             } else {
-                mAutofillManager = new AutofillManagerWrapper(context);
+                if (!AndroidAutofillFeatures.ANDROID_AUTOFILL_LAZY_FRAMEWORK_WRAPPER.isEnabled()) {
+                    initializeFrameworkWrapper(context);
+                }
             }
             mContainerView = containerView;
-            mAutofillUMA =
-                    new AutofillProviderUMA(
-                            context,
-                            mAutofillManager.isAwGCurrentAutofillService(),
-                            mAutofillManager.getPackageName());
-            mInputUiObserver =
-                    new AutofillManagerWrapper.InputUiObserver() {
-                        @Override
-                        public void onInputUiShown() {
-                            // Not need to report suggestion window displayed if there is no live
-                            // autofill session.
-                            if (mRequest == null) return;
-                            mAutofillUMA.onSuggestionDisplayed(
-                                    System.currentTimeMillis() - mAutofillTriggeredTimeMillis);
-                        }
-                    };
-            mAutofillManager.addInputUiObserver(mInputUiObserver);
             mContext = context;
         }
         initializeNativeAutofillProvider(webContents);
     }
 
+    private void initializeFrameworkWrapper(Context context) {
+        mAutofillManager = new AutofillManagerWrapper(context);
+        maybeInitializeUmaRecorder(context);
+        maybeInitializeInputObserver();
+    }
+
+    private void maybeInitializeUmaRecorder(Context context) {
+        if (mAutofillUMA != null) return;
+        mAutofillUMA =
+                new AutofillProviderUMA(
+                        context,
+                        mAutofillManager.isAwGCurrentAutofillService(),
+                        mAutofillManager.getPackageName());
+    }
+
+    private void maybeInitializeInputObserver() {
+        if (mInputUiObserver != null) return;
+        mInputUiObserver =
+                () -> {
+                    // Not need to report suggestion window displayed if there is no live
+                    // autofill session.
+                    if (mRequest == null) return;
+                    mAutofillUMA.onSuggestionDisplayed(
+                            System.currentTimeMillis() - mAutofillTriggeredTimeMillis);
+                };
+
+        mAutofillManager.addInputUiObserver(mInputUiObserver);
+    }
+
     public void destroy() {
         mAutofillUMA.recordSession();
         detachFromJavaAutofillProvider();
-        mAutofillManager.destroy();
+        getAutofillManagerWrapper().destroy();
     }
 
     /**
@@ -199,16 +221,17 @@
     public boolean shouldQueryAutofillSuggestion() {
         return mRequest != null
                 && mRequest.getFocusField() != null
-                && !mAutofillManager.isAutofillInputUiShowing();
+                && !getAutofillManagerWrapper().isAutofillInputUiShowing();
     }
 
     public void queryAutofillSuggestion() {
         if (shouldQueryAutofillSuggestion()) {
             FocusField focusField = mRequest.getFocusField();
-            mAutofillManager.requestAutofill(
-                    mContainerView,
-                    mRequest.getFieldVirtualId(focusField.fieldIndex),
-                    focusField.absBound);
+            getAutofillManagerWrapper()
+                    .requestAutofill(
+                            mContainerView,
+                            mRequest.getFieldVirtualId(focusField.fieldIndex),
+                            focusField.absBound);
         }
     }
 
@@ -239,7 +262,8 @@
         mPrefillRequest = new PrefillRequest(form);
         mStructureProvidedForPrefillRequest = false;
 
-        mAutofillManager.notifyVirtualViewsReady(mContainerView, mPrefillRequest.getPrefillHints());
+        getAutofillManagerWrapper()
+                .notifyVirtualViewsReady(mContainerView, mPrefillRequest.getPrefillHints());
     }
 
     /**
@@ -269,11 +293,11 @@
         // Check focusField inside short value? Autofill Manager might have session that wasn't
         // started by AutofillProvider, we just always cancel existing session here.
         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
-            mAutofillManager.cancel();
+            getAutofillManagerWrapper().cancel();
         }
 
         transformFormFieldToContainViewCoordinates(formData);
-        mAutofillUMA.onSessionStarted(mAutofillManager.isDisabled());
+        mAutofillUMA.onSessionStarted(getAutofillManagerWrapper().isDisabled());
         mRequest =
                 new AutofillRequest(
                         formData, new FocusField((short) focus, absBound), hasServerPrediction);
@@ -287,7 +311,7 @@
         }
         mAutofillTriggeredTimeMillis = System.currentTimeMillis();
 
-        mAutofillManager.notifyNewSessionStarted(hasServerPrediction);
+        getAutofillManagerWrapper().notifyNewSessionStarted(hasServerPrediction);
     }
 
     /**
@@ -426,14 +450,16 @@
         if (!forceNotify && isDatalistField(index)) return;
         AutofillValue autofillValue = mRequest.getFieldNewValue(index);
         if (autofillValue == null) return;
-        mAutofillManager.notifyVirtualValueChanged(
-                mContainerView, mRequest.getFieldVirtualId((short) index), autofillValue);
+        getAutofillManagerWrapper()
+                .notifyVirtualValueChanged(
+                        mContainerView, mRequest.getFieldVirtualId((short) index), autofillValue);
     }
 
     private void notifyVirtualViewVisibilityChanged(int index, boolean isVisible) {
         if (isDatalistField(index)) return;
-        mAutofillManager.notifyVirtualViewVisibilityChanged(
-                mContainerView, mRequest.getFieldVirtualId((short) index), isVisible);
+        getAutofillManagerWrapper()
+                .notifyVirtualViewVisibilityChanged(
+                        mContainerView, mRequest.getFieldVirtualId((short) index), isVisible);
     }
 
     @RequiresApi(VERSION_CODES.TIRAMISU)
@@ -441,21 +467,23 @@
         // Refer to notifyVirtualValueChanged() for the reason of the datalist's special handling.
         if (isDatalistField(index)) return false;
 
-        return mAutofillManager.showAutofillDialog(
-                parent, mRequest.getFieldVirtualId((short) index));
+        return getAutofillManagerWrapper()
+                .showAutofillDialog(parent, mRequest.getFieldVirtualId((short) index));
     }
 
     private void notifyVirtualViewEntered(View parent, int index, Rect absBounds) {
         // Refer to notifyVirtualValueChanged() for the reason of the datalist's special handling.
         if (isDatalistField(index)) return;
-        mAutofillManager.notifyVirtualViewEntered(
-                parent, mRequest.getFieldVirtualId((short) index), absBounds);
+        getAutofillManagerWrapper()
+                .notifyVirtualViewEntered(
+                        parent, mRequest.getFieldVirtualId((short) index), absBounds);
     }
 
     private void notifyVirtualViewExited(View parent, int index) {
         // Refer to notifyVirtualValueChanged() for the reason of the datalist's special handling.
         if (isDatalistField(index)) return;
-        mAutofillManager.notifyVirtualViewExited(parent, mRequest.getFieldVirtualId((short) index));
+        getAutofillManagerWrapper()
+                .notifyVirtualViewExited(parent, mRequest.getFieldVirtualId((short) index));
     }
 
     /**
@@ -468,7 +496,7 @@
         // The changes could be missing, like those made by Javascript, we'd better to notify
         // AutofillManager current values. also see crbug.com/353001 and crbug.com/732856.
         forceNotifyFormValues();
-        mAutofillManager.commit(submissionSource);
+        getAutofillManagerWrapper().commit(submissionSource);
         mRequest = null;
         mAutofillUMA.onFormSubmitted(submissionSource);
     }
@@ -565,9 +593,22 @@
     }
 
     /**
+     * Returns the {@link AutofillManagerWrapper} object if initialized and creates it otherwise. Do
+     * not access the object directly as it may not be initialized.
+     *
+     * @return The wrapper object. It is guaranteed to be initialized.
+     */
+    private AutofillManagerWrapper getAutofillManagerWrapper() {
+        if (mAutofillManager == null) {
+            initializeFrameworkWrapper(mContext);
+        }
+        return mAutofillManager;
+    }
+
+    /**
      * Display the simplest popup for the datalist. This is same as WebView's datalist popup in
-     * Android pre-o. No suggestion from the autofill service will be presented, No advance
-     * features of AutofillPopup are used.
+     * Android pre-o. No suggestion from the autofill service will be presented, No advance features
+     * of AutofillPopup are used.
      */
     private void showDatalistPopup(
             String[] datalistValues, String[] datalistLabels, RectF bounds, boolean isRtl) {
@@ -693,7 +734,7 @@
     private void onServerPredictionsAvailable() {
         if (mRequest == null) return;
         mRequest.onServerPredictionsAvailable();
-        mAutofillManager.onServerPredictionsAvailable();
+        getAutofillManagerWrapper().onServerPredictionsAvailable();
         mAutofillUMA.onServerTypeAvailable(mRequest.getForm(), /* afterSessionStarted= */ true);
     }
 
@@ -782,7 +823,7 @@
 
     @CalledByNative
     public void cancelSession() {
-        mAutofillManager.cancel();
+        getAutofillManagerWrapper().cancel();
         mPrefillRequest = null;
         mRequest = null;
     }
diff --git a/components/app_restore/app_restore_data.cc b/components/app_restore/app_restore_data.cc
index 4501f30..354e537 100644
--- a/components/app_restore/app_restore_data.cc
+++ b/components/app_restore/app_restore_data.cc
@@ -522,7 +522,4 @@
          status_bar_color == other.status_bar_color;
 }
 
-bool AppRestoreData::operator!=(const AppRestoreData& other) const {
-  return !(*this == other);
-}
 }  // namespace app_restore
diff --git a/components/app_restore/app_restore_data.h b/components/app_restore/app_restore_data.h
index 1423465..17e1514 100644
--- a/components/app_restore/app_restore_data.h
+++ b/components/app_restore/app_restore_data.h
@@ -78,8 +78,6 @@
 
   bool operator==(const AppRestoreData& other) const;
 
-  bool operator!=(const AppRestoreData& other) const;
-
   // App launch parameters.
   // TODO: Replace this with a `AppLaunchInfo` object.
   std::optional<int32_t> event_flag;
diff --git a/components/autofill/android/BUILD.gn b/components/autofill/android/BUILD.gn
index 95f1b1e..4f4271a 100644
--- a/components/autofill/android/BUILD.gn
+++ b/components/autofill/android/BUILD.gn
@@ -254,6 +254,7 @@
     "java/src/org/chromium/components/autofill/AutofillProfile.java",
     "java/src/org/chromium/components/autofill/AutofillSuggestion.java",
     "java/src/org/chromium/components/autofill/DropdownKeyValue.java",
+    "java/src/org/chromium/components/autofill/LoyaltyCard.java",
     "java/src/org/chromium/components/autofill/SubKeyRequester.java",
   ]
   srcjar_deps = [
@@ -270,6 +271,7 @@
     "java/src/org/chromium/components/autofill/AutofillAddressUiComponent.java",
     "java/src/org/chromium/components/autofill/AutofillProfile.java",
     "java/src/org/chromium/components/autofill/DropdownKeyValue.java",
+    "java/src/org/chromium/components/autofill/LoyaltyCard.java",
     "java/src/org/chromium/components/autofill/SubKeyRequester.java",
   ]
 }
diff --git a/components/autofill/android/java/src/org/chromium/components/autofill/LoyaltyCard.java b/components/autofill/android/java/src/org/chromium/components/autofill/LoyaltyCard.java
new file mode 100644
index 0000000..e6a0901b
--- /dev/null
+++ b/components/autofill/android/java/src/org/chromium/components/autofill/LoyaltyCard.java
@@ -0,0 +1,66 @@
+// 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;
+
+import org.jni_zero.CalledByNative;
+import org.jni_zero.JNINamespace;
+import org.jni_zero.JniType;
+
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.url.GURL;
+
+import java.util.List;
+
+/** Represents a loyalty card coming from the the Google Wallet. */
+@JNINamespace("autofill")
+@NullMarked
+public class LoyaltyCard {
+    private String mLoyaltyCardId;
+    private String mMerchantName;
+    private String mProgramName;
+    private GURL mProgramLogo;
+    private String mLoyaltyCardNumber;
+    private List<GURL> mMerchantDomains;
+
+    @CalledByNative
+    public LoyaltyCard(
+            @JniType("std::string") String loyaltyCardId,
+            @JniType("std::string") String merchantName,
+            @JniType("std::string") String programName,
+            @JniType("GURL") GURL programLogo,
+            @JniType("std::string") String loyaltyCardNumber,
+            @JniType("std::vector<GURL>") List<GURL> merchantDomains) {
+        mLoyaltyCardId = loyaltyCardId;
+        mMerchantName = merchantName;
+        mProgramName = programName;
+        mProgramLogo = programLogo;
+        mLoyaltyCardNumber = loyaltyCardNumber;
+        mMerchantDomains = merchantDomains;
+    }
+
+    public String getLoyaltyCardId() {
+        return mLoyaltyCardId;
+    }
+
+    public String getMerchantName() {
+        return mMerchantName;
+    }
+
+    public String getProgramName() {
+        return mProgramName;
+    }
+
+    public GURL getProgramLogo() {
+        return mProgramLogo;
+    }
+
+    public String getLoyaltyCardNumber() {
+        return mLoyaltyCardNumber;
+    }
+
+    public List<GURL> getMerchantDomains() {
+        return mMerchantDomains;
+    }
+}
diff --git a/components/autofill/content/browser/content_identity_credential_delegate_unittest.cc b/components/autofill/content/browser/content_identity_credential_delegate_unittest.cc
index 4f03864..ea692a04 100644
--- a/components/autofill/content/browser/content_identity_credential_delegate_unittest.cc
+++ b/components/autofill/content/browser/content_identity_credential_delegate_unittest.cc
@@ -57,7 +57,7 @@
   scoped_refptr<content::IdentityProviderData> identity_provider_data =
       base::MakeRefCounted<content::IdentityProviderData>(
           "idp.example", metadata, client, blink::mojom::RpContext::kSignIn,
-          disclosures, false);
+          blink::mojom::Format::kSdJwt, disclosures, false);
 
   account->identity_provider = identity_provider_data;
 
@@ -243,7 +243,8 @@
       base::MakeRefCounted<content::IdentityProviderData>(
           "idp.example", metadata,
           content::ClientMetadata((GURL()), (GURL()), (GURL()), (gfx::Image())),
-          blink::mojom::RpContext::kSignIn, disclosures, false);
+          blink::mojom::RpContext::kSignIn, blink::mojom::Format::kSdJwt,
+          disclosures, false);
 
   account->identity_provider = identity_provider;
   std::vector<IdentityRequestAccountPtr> accounts = {account};
diff --git a/components/autofill/core/browser/autofill_field.h b/components/autofill/core/browser/autofill_field.h
index 1c2c403a..0990237 100644
--- a/components/autofill/core/browser/autofill_field.h
+++ b/components/autofill/core/browser/autofill_field.h
@@ -212,16 +212,17 @@
   bool ShouldSuppressSuggestionsAndFillingByDefault() const;
 
   // Returns the current value, formatted as desired for import:
-  // (1) If the user left a field unchanged, returns the empty string.
-  // (2) If the field has FormControlType::kSelect* and has a selected text,
-  //     it is FormFieldData::selected_text().
+  // (1) If the field value hasn't changed since it was seen and the field is a
+  //     non-<select>, returns the empty string.
+  // (2) If the field has FormControlType::kSelect* and has a selected option,
+  //     returns that option's human-readable text.
+  // (3) Otherwise returns value().
   //
   // The motivation behind (1) is that unchanged values usually carry little
-  // value for importing. The exception are <select> fields, which often have
-  // a correct default value, so we consider them for import even if their value
-  // didn't change.
-  // TODO: crbug.com/40137859 - Consider making an exception for also for
-  // non-<select> ADDRESS_HOME_{STATE,COUNTRY} fields.
+  // value for importing. <select> fields are exempted because their default
+  // value is often correct (e.g., in ADDRESS_HOME_COUNTRY fields).
+  // TODO(crbug.com/40137859): Consider also exempting non-<select>
+  // ADDRESS_HOME_{STATE,COUNTRY} fields.
   //
   // The motivation behind (2) is that the human-readable text of an <option> is
   // usually better suited for import than the its value. See the documentation
@@ -237,6 +238,8 @@
   // - When the field has moved to another form.
   // - When the form has been extracted without the field. For example, this
   //   could happen because the field was temporarily removed from the DOM.
+  //
+  // For the field's current value, see FormFieldData::value().
   const std::u16string& initial_value() const { return initial_value_; }
 
   // Sets the field's current value.
@@ -252,13 +255,6 @@
     return credit_card_number_offset_;
   }
 
-  void set_vote_type(AutofillUploadContents::Field::VoteType type) {
-    vote_type_ = type;
-  }
-  AutofillUploadContents::Field::VoteType vote_type() const {
-    return vote_type_;
-  }
-
   void SetPasswordRequirements(PasswordRequirementsSpec spec);
   const std::optional<PasswordRequirementsSpec>& password_requirements() const {
     return password_requirements_;
@@ -464,12 +460,6 @@
   // label when the label is divided between subsequent fields.
   std::u16string parseable_label_;
 
-  // The vote type, if the autofill type is USERNAME or any password vote.
-  // Otherwise, the field is ignored. |vote_type_| provides context as to what
-  // triggered the vote.
-  AutofillUploadContents::Field::VoteType vote_type_ =
-      AutofillUploadContents::Field::NO_INFORMATION;
-
   // A list of field log events, which record when user interacts the field
   // during autofill or editing, such as user clicks on the field, the
   // suggestion list is shown for the field, user accepts one suggestion to
diff --git a/components/autofill/core/browser/crowdsourcing/autofill_crowdsourcing_encoding.cc b/components/autofill/core/browser/crowdsourcing/autofill_crowdsourcing_encoding.cc
index 3e636fb8..9e2a736 100644
--- a/components/autofill/core/browser/crowdsourcing/autofill_crowdsourcing_encoding.cc
+++ b/components/autofill/core/browser/crowdsourcing/autofill_crowdsourcing_encoding.cc
@@ -386,8 +386,8 @@
       added_field->add_autofill_type(field_type);
     }
 
-    if (field->vote_type()) {
-      added_field->set_vote_type(field->vote_type());
+    if (field_options && field_options->vote_type) {
+      added_field->set_vote_type(field_options->vote_type);
     }
 
     if (field_options && field_options->initial_value_hash) {
diff --git a/components/autofill/core/browser/crowdsourcing/autofill_crowdsourcing_encoding.h b/components/autofill/core/browser/crowdsourcing/autofill_crowdsourcing_encoding.h
index 899f28e..16c2561 100644
--- a/components/autofill/core/browser/crowdsourcing/autofill_crowdsourcing_encoding.h
+++ b/components/autofill/core/browser/crowdsourcing/autofill_crowdsourcing_encoding.h
@@ -68,6 +68,12 @@
     // before user-interactions or automatic fillings. This field is used to
     // detect static placeholders. On non-username fields, it is not set.
     std::optional<uint32_t> initial_value_hash;
+
+    // The vote type, if the autofill type is USERNAME or any password vote.
+    // Otherwise, the field is ignored. `vote_type` provides context as to what
+    // triggered the vote.
+    AutofillUploadContents::Field::VoteType vote_type =
+        AutofillUploadContents::Field::NO_INFORMATION;
   };
 
   EncodeUploadRequestOptions();
diff --git a/components/autofill/core/browser/crowdsourcing/autofill_crowdsourcing_encoding_unittest.cc b/components/autofill/core/browser/crowdsourcing/autofill_crowdsourcing_encoding_unittest.cc
index 0afbe53..4ba2258 100644
--- a/components/autofill/core/browser/crowdsourcing/autofill_crowdsourcing_encoding_unittest.cc
+++ b/components/autofill/core/browser/crowdsourcing/autofill_crowdsourcing_encoding_unittest.cc
@@ -524,8 +524,10 @@
       field_options.generated_password_changed = true;
     }
     if (form_structure->field(i)->name() == u"username") {
-      form_structure->field(i)->set_vote_type(
-          AutofillUploadContents::Field::CREDENTIALS_REUSED);
+      auto& field_options =
+          options.fields[form_structure->field(i)->global_id()];
+      field_options.vote_type =
+          AutofillUploadContents::Field::CREDENTIALS_REUSED;
     }
   }
 
diff --git a/components/autofill/core/browser/crowdsourcing/votes_uploader.h b/components/autofill/core/browser/crowdsourcing/votes_uploader.h
index 8a1de4b..d238ac4 100644
--- a/components/autofill/core/browser/crowdsourcing/votes_uploader.h
+++ b/components/autofill/core/browser/crowdsourcing/votes_uploader.h
@@ -61,8 +61,7 @@
 //       ▼                                       │
 //   Store PendingVote, which is uploaded when   │
 //   - a submission happens in the frame;        │
-//   - the frame becomes inactive                │
-//     kAutofillVoteWhenInactive is enabled;     │
+//   - the frame becomes inactive;               │
 //   - the frame is reset;                       │
 //   - the frame is deleted;                     │
 //   - the queue becomes too large.              │
diff --git a/components/autofill/core/browser/data_manager/valuables/valuables_data_manager_test_api.h b/components/autofill/core/browser/data_manager/valuables/valuables_data_manager_test_api.h
index cadbab38..3b2516d2 100644
--- a/components/autofill/core/browser/data_manager/valuables/valuables_data_manager_test_api.h
+++ b/components/autofill/core/browser/data_manager/valuables/valuables_data_manager_test_api.h
@@ -29,6 +29,8 @@
     valuables_data_manager_->loyalty_cards_ = loyalty_cards;
   }
 
+  void ClearLoyaltyCards() { valuables_data_manager_->loyalty_cards_.clear(); }
+
   void NotifyObservers() { valuables_data_manager_->NotifyObservers(); }
 
  private:
diff --git a/components/autofill/core/browser/form_parsing/internal_resources b/components/autofill/core/browser/form_parsing/internal_resources
index 8f3290e..ee4b7f29 160000
--- a/components/autofill/core/browser/form_parsing/internal_resources
+++ b/components/autofill/core/browser/form_parsing/internal_resources
@@ -1 +1 @@
-Subproject commit 8f3290e53c07fe9527fc48952edde535c3cfa13a
+Subproject commit ee4b7f29de55d7867a004208580a994e5da35ed0
diff --git a/components/autofill/core/browser/form_structure_test_api.cc b/components/autofill/core/browser/form_structure_test_api.cc
index f838b27..812723f1 100644
--- a/components/autofill/core/browser/form_structure_test_api.cc
+++ b/components/autofill/core/browser/form_structure_test_api.cc
@@ -60,17 +60,4 @@
   SetFieldTypes(all_heuristic_types, server_types);
 }
 
-AutofillUploadContents::Field::VoteType
-FormStructureTestApi::get_username_vote_type() {
-  for (const auto& field : form_structure_->fields()) {
-    AutofillUploadContents::Field::VoteType vote_type = field->vote_type();
-    if (vote_type == AutofillUploadContents::Field::USERNAME_OVERWRITTEN ||
-        vote_type == AutofillUploadContents::Field::USERNAME_EDITED ||
-        vote_type == AutofillUploadContents::Field::CREDENTIALS_REUSED) {
-      return vote_type;
-    }
-  }
-  return AutofillUploadContents::Field::NO_INFORMATION;
-}
-
 }  // namespace autofill
diff --git a/components/autofill/core/browser/form_structure_test_api.h b/components/autofill/core/browser/form_structure_test_api.h
index d931f5c6..8cead8a3 100644
--- a/components/autofill/core/browser/form_structure_test_api.h
+++ b/components/autofill/core/browser/form_structure_test_api.h
@@ -66,11 +66,6 @@
                   /*server_types=*/overall_types);
   }
 
-  // Returns a vote type if a field contains a vote relating USERNAME correction
-  // (CREDENTIALS_REUSED, USERNAME_OVERWRITTEN, USERNAME_EDITED). If none,
-  // returns NO_INFORMATION.
-  AutofillUploadContents::Field::VoteType get_username_vote_type();
-
   void AssignSections() { autofill::AssignSections(form_structure_->fields_); }
 
   FieldCandidatesMap ParseFieldTypesWithPatterns(
diff --git a/components/autofill/core/browser/payments/payments_autofill_client.cc b/components/autofill/core/browser/payments/payments_autofill_client.cc
index b875a8c9..80134d4d 100644
--- a/components/autofill/core/browser/payments/payments_autofill_client.cc
+++ b/components/autofill/core/browser/payments/payments_autofill_client.cc
@@ -224,6 +224,12 @@
   return false;
 }
 
+bool PaymentsAutofillClient::ShowTouchToFillLoyaltyCard(
+    base::WeakPtr<TouchToFillDelegate> delegate,
+    base::span<const LoyaltyCard> loyalty_cards_to_suggest) {
+  return false;
+}
+
 void PaymentsAutofillClient::HideTouchToFillPaymentMethod() {}
 
 const PaymentsDataManager& PaymentsAutofillClient::GetPaymentsDataManager()
diff --git a/components/autofill/core/browser/payments/payments_autofill_client.h b/components/autofill/core/browser/payments/payments_autofill_client.h
index 961b12ea..caf8d47 100644
--- a/components/autofill/core/browser/payments/payments_autofill_client.h
+++ b/components/autofill/core/browser/payments/payments_autofill_client.h
@@ -536,6 +536,14 @@
   virtual bool ShowTouchToFillIban(base::WeakPtr<TouchToFillDelegate> delegate,
                                    base::span<const Iban> ibans_to_suggest);
 
+  // Shows the Touch To Fill surface for filling Wallet loyalty card
+  // information, if possible, returning `true` on success. `delegate` will be
+  // notified of events. This function is not implemented on iOS and iOS
+  // WebView, and should not be used on those platforms.
+  virtual bool ShowTouchToFillLoyaltyCard(
+      base::WeakPtr<TouchToFillDelegate> delegate,
+      base::span<const LoyaltyCard> loyalty_cards_to_suggest);
+
   // Hides the Touch To Fill surface for filling payment information if one is
   // currently shown. Should be called only if the feature is supported by the
   // platform.
diff --git a/components/autofill/core/common/autofill_test_utils.cc b/components/autofill/core/common/autofill_test_utils.cc
index 3a5ac73..e4a04bd2 100644
--- a/components/autofill/core/common/autofill_test_utils.cc
+++ b/components/autofill/core/common/autofill_test_utils.cc
@@ -314,6 +314,14 @@
   return form;
 }
 
+FormData CreateTestLoyaltyCardFormData() {
+  FormData form = ConstructFormWithNameRenderIdAndProtocol(/*is_https=*/true);
+  form.set_fields(
+      {CreateTestFormField("Your loyalty card:", "loyalty_card", /*value=*/"",
+                           FormControlType::kInputText)});
+  return form;
+}
+
 FormData CreateTestPasswordFormData() {
   std::vector<FormFieldData> fields;
   fields.push_back(
diff --git a/components/autofill/core/common/autofill_test_utils.h b/components/autofill/core/common/autofill_test_utils.h
index f1a060b..6d3e796 100644
--- a/components/autofill/core/common/autofill_test_utils.h
+++ b/components/autofill/core/common/autofill_test_utils.h
@@ -212,6 +212,10 @@
     std::string_view value = kIbanValue,
     bool is_https = true);
 
+// Populates `form_data` with data corresponding to a loyalty card form (a form
+// with a single loyalty card field).
+[[nodiscard]] FormData CreateTestLoyaltyCardFormData();
+
 // Creates a `FormData` with a username and a password field.
 [[nodiscard]] FormData CreateTestPasswordFormData();
 
diff --git a/components/autofill_strings.grdp b/components/autofill_strings.grdp
index 617f02c..6becf025 100644
--- a/components/autofill_strings.grdp
+++ b/components/autofill_strings.grdp
@@ -92,6 +92,9 @@
   <message name="IDS_AUTOFILL_IBAN_GENERIC" desc="Generic IBAN name." formatter_data="android_java">
     Iban
   </message>
+  <message name="IDS_AUTOFILL_LOYALTY_CARD_GENERIC" desc="Generic loyalty card name." formatter_data="android_java">
+    Loyalty card
+  </message>
   <message name="IDS_AUTOFILL_ADDRESS_SUMMARY_SEPARATOR" desc="The separator character used in the summary of an address." formatter_data="android_java">
     , '''
   </message>
diff --git a/components/autofill_strings_grdp/IDS_AUTOFILL_LOYALTY_CARD_GENERIC.png.sha1 b/components/autofill_strings_grdp/IDS_AUTOFILL_LOYALTY_CARD_GENERIC.png.sha1
new file mode 100644
index 0000000..c1a924af
--- /dev/null
+++ b/components/autofill_strings_grdp/IDS_AUTOFILL_LOYALTY_CARD_GENERIC.png.sha1
@@ -0,0 +1 @@
+2171f8e701521399a583884eb34ad3c3e07e9d60
\ No newline at end of file
diff --git a/components/browser_ui/strings/android/browser_ui_strings.grd b/components/browser_ui/strings/android/browser_ui_strings.grd
index fdb35cbe..07f6a97 100644
--- a/components/browser_ui/strings/android/browser_ui_strings.grd
+++ b/components/browser_ui/strings/android/browser_ui_strings.grd
@@ -372,9 +372,6 @@
       <message name="IDS_CONCAT_TWO_STRINGS_WITH_COMMA" desc="This string concatenates two other strings. In the English language, this particular concatenation is done via a comma and a whitespace in between the other strings. For example: '1 compromised password, 2 weak passwords'">
         <ph name="TYPE_1">%1$s<ex>2 compromised passwords</ex></ph>, <ph name="TYPE_2">%2$s<ex>7 weak passwords</ex></ph>
       </message>
-      <message name="IDS_CONCAT_TWO_STRINGS_WITH_PERIODS" desc="This string concatenates two other strings. In the English language, this particular concatenation is done via a period and a whitespace after the first string, and a period after the second string. For example: '1 compromised password. 2 weak passwords.'">
-        <ph name="TYPE_1">%1$s<ex>2 compromised passwords</ex></ph>. <ph name="TYPE_2">%2$s<ex>7 weak passwords</ex></ph>.
-      </message>
 
       <!-- Certificate viewer -->
       <message name="IDS_CERTTITLE" desc="Dialog box title for viewing security certificates. [CHAR_LIMIT=32]">
diff --git a/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_CONCAT_TWO_STRINGS_WITH_PERIODS.png.sha1 b/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_CONCAT_TWO_STRINGS_WITH_PERIODS.png.sha1
deleted file mode 100644
index 88d45b3e..0000000
--- a/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_CONCAT_TWO_STRINGS_WITH_PERIODS.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-7c4759e9ff383f9089f965694cc1964fe8145715
\ No newline at end of file
diff --git a/components/browsing_data/content/browsing_data_quota_helper_impl.cc b/components/browsing_data/content/browsing_data_quota_helper_impl.cc
index 9a986491..97ab955 100644
--- a/components/browsing_data/content/browsing_data_quota_helper_impl.cc
+++ b/components/browsing_data/content/browsing_data_quota_helper_impl.cc
@@ -21,7 +21,6 @@
 #include "third_party/blink/public/mojom/quota/quota_types.mojom.h"
 #include "url/origin.h"
 
-using blink::mojom::StorageType;
 using content::BrowserContext;
 using content::BrowserThread;
 
diff --git a/components/browsing_data/content/browsing_data_quota_helper_impl.h b/components/browsing_data/content/browsing_data_quota_helper_impl.h
index 5bf3f2c..94ce616 100644
--- a/components/browsing_data/content/browsing_data_quota_helper_impl.h
+++ b/components/browsing_data/content/browsing_data_quota_helper_impl.h
@@ -42,8 +42,6 @@
       delete;
 
  private:
-  using PendingHosts =
-      std::set<std::pair<std::string, blink::mojom::StorageType>>;
   using QuotaInfoMap = std::map<blink::StorageKey, QuotaInfo>;
 
   ~BrowsingDataQuotaHelperImpl() override;
diff --git a/components/captive_portal/core/captive_portal_export.h b/components/captive_portal/core/captive_portal_export.h
index 87c84845..0c5613e4 100644
--- a/components/captive_portal/core/captive_portal_export.h
+++ b/components/captive_portal/core/captive_portal_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(CAPTIVE_PORTAL_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(CAPTIVE_PORTAL_IMPLEMENTATION)
 #define CAPTIVE_PORTAL_EXPORT __attribute__((visibility("default")))
-#else
-#define CAPTIVE_PORTAL_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/components/capture_mode/capture_mode_export.h b/components/capture_mode/capture_mode_export.h
index 7b689692..e13f918 100644
--- a/components/capture_mode/capture_mode_export.h
+++ b/components/capture_mode/capture_mode_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(CAPTURE_MODE_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(CAPTURE_MODE_IMPLEMENTATION)
 #define CAPTURE_MODE_EXPORT __attribute__((visibility("default")))
-#else
-#define CAPTURE_MODE_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/components/cast/cast_component_export.h b/components/cast/cast_component_export.h
index 5c65030..decebae8 100644
--- a/components/cast/cast_component_export.h
+++ b/components/cast/cast_component_export.h
@@ -5,10 +5,9 @@
 #ifndef COMPONENTS_CAST_CAST_COMPONENT_EXPORT_H_
 #define COMPONENTS_CAST_CAST_COMPONENT_EXPORT_H_
 
-#if defined(COMPONENT_BUILD) && defined(CAST_COMPONENT_IMPLEMENTATION)
+#if defined(COMPONENT_BUILD)
 #define CAST_COMPONENT_EXPORT __attribute__((visibility("default")))
-#else  // !defined(COMPONENT_BUILD) ||
-       // !defined(CAST_COMPONENT_EXPORT)
+#else  // !defined(COMPONENT_BUILD)
 #define CAST_COMPONENT_EXPORT
 #endif
 
diff --git a/components/cbor/cbor_export.h b/components/cbor/cbor_export.h
index a04d88c3..410d555 100644
--- a/components/cbor/cbor_export.h
+++ b/components/cbor/cbor_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(CBOR_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(CBOR_IMPLEMENTATION)
 #define CBOR_EXPORT __attribute__((visibility("default")))
-#else
-#define CBOR_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/components/collaboration/internal/android/collaboration_service_android.cc b/components/collaboration/internal/android/collaboration_service_android.cc
index 600c984..2514ade1 100644
--- a/components/collaboration/internal/android/collaboration_service_android.cc
+++ b/components/collaboration/internal/android/collaboration_service_android.cc
@@ -99,6 +99,21 @@
       static_cast<CollaborationServiceShareOrManageEntryPoint>(entry));
 }
 
+void CollaborationServiceAndroid::StartLeaveOrDeleteFlow(
+    JNIEnv* env,
+    jlong delegateNativePtr,
+    const JavaParamRef<jstring>& j_sync_group_id,
+    jint entry) {
+  std::string sync_group_id_str =
+      base::android::ConvertJavaStringToUTF8(env, j_sync_group_id);
+  tab_groups::EitherGroupID either_id =
+      base::Uuid::ParseLowercase(sync_group_id_str);
+
+  collaboration_service_->StartLeaveOrDeleteFlow(
+      conversion::GetDelegateUniquePtrFromJava(delegateNativePtr), either_id,
+      static_cast<CollaborationServiceLeaveOrDeleteEntryPoint>(entry));
+}
+
 ScopedJavaLocalRef<jobject> CollaborationServiceAndroid::GetServiceStatus(
     JNIEnv* env) {
   ServiceStatus status = collaboration_service_->GetServiceStatus();
diff --git a/components/collaboration/internal/android/collaboration_service_android.h b/components/collaboration/internal/android/collaboration_service_android.h
index 8338cbb..685131c4 100644
--- a/components/collaboration/internal/android/collaboration_service_android.h
+++ b/components/collaboration/internal/android/collaboration_service_android.h
@@ -32,6 +32,11 @@
       jlong delegate,
       const base::android::JavaParamRef<jstring>& j_sync_group_id,
       jint entry);
+  void StartLeaveOrDeleteFlow(
+      JNIEnv* env,
+      jlong delegate,
+      const base::android::JavaParamRef<jstring>& j_sync_group_id,
+      jint entry);
   base::android::ScopedJavaLocalRef<jobject> GetServiceStatus(JNIEnv* env);
   jint GetCurrentUserRoleForGroup(
       JNIEnv* env,
diff --git a/components/collaboration/internal/android/java/src/org/chromium/components/collaboration/CollaborationServiceImpl.java b/components/collaboration/internal/android/java/src/org/chromium/components/collaboration/CollaborationServiceImpl.java
index 8fda275..ff97c4eb 100644
--- a/components/collaboration/internal/android/java/src/org/chromium/components/collaboration/CollaborationServiceImpl.java
+++ b/components/collaboration/internal/android/java/src/org/chromium/components/collaboration/CollaborationServiceImpl.java
@@ -55,6 +55,15 @@
     }
 
     @Override
+    public void startLeaveOrDeleteFlow(
+            CollaborationControllerDelegate delegate,
+            String syncId,
+            @CollaborationServiceLeaveOrDeleteEntryPoint int entry) {
+        CollaborationServiceImplJni.get()
+                .startLeaveOrDeleteFlow(mNativePtr, delegate.getNativePtr(), syncId, entry);
+    }
+
+    @Override
     public ServiceStatus getServiceStatus() {
         return CollaborationServiceImplJni.get().getServiceStatus(mNativePtr);
     }
@@ -116,6 +125,12 @@
                 String syncId,
                 int entry);
 
+        void startLeaveOrDeleteFlow(
+                long nativeCollaborationServiceAndroid,
+                long delegateNativePtr,
+                String syncId,
+                int entry);
+
         ServiceStatus getServiceStatus(long nativeCollaborationServiceAndroid);
 
         int getCurrentUserRoleForGroup(
diff --git a/components/collaboration/public/android/java/src/org/chromium/components/collaboration/CollaborationService.java b/components/collaboration/public/android/java/src/org/chromium/components/collaboration/CollaborationService.java
index 8085f4c..6befab7 100644
--- a/components/collaboration/public/android/java/src/org/chromium/components/collaboration/CollaborationService.java
+++ b/components/collaboration/public/android/java/src/org/chromium/components/collaboration/CollaborationService.java
@@ -53,12 +53,25 @@
      *
      * @param delegate The delegate to perform action on the Android UI.
      * @param either_id The ID to identify a tab group.
+     * @param entry The entry point of the flow.
      */
     void startShareOrManageFlow(
             CollaborationControllerDelegate delegate,
             String syncId,
             @CollaborationServiceShareOrManageEntryPoint int entry);
 
+    /**
+     * Starts a new collaboration leave or delete flow.
+     *
+     * @param delegate The delegate to perform action on the Android UI.
+     * @param either_id The ID to identify a tab group.
+     * @param entry The entry point of the flow.
+     */
+    void startLeaveOrDeleteFlow(
+            CollaborationControllerDelegate delegate,
+            String syncId,
+            @CollaborationServiceLeaveOrDeleteEntryPoint int entry);
+
     /** Returns the current {@link ServiceStatus} of the service. */
     ServiceStatus getServiceStatus();
 
diff --git a/components/crash/core/common/crash_export.h b/components/crash/core/common/crash_export.h
index 7496a5af..f13dc9c 100644
--- a/components/crash/core/common/crash_export.h
+++ b/components/crash/core/common/crash_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(CRASH_CORE_COMMON_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(CRASH_CORE_COMMON_IMPLEMENTATION)
 #define CRASH_EXPORT __attribute__((visibility("default")))
-#else
-#define CRASH_EXPORT
-#endif  // defined(CRASH_CORE_COMMON_IMPLEMENTATION)
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/components/credential_management/android/java/src/org/chromium/components/credential_management/ThirdPartyCredentialManagerBridge.java b/components/credential_management/android/java/src/org/chromium/components/credential_management/ThirdPartyCredentialManagerBridge.java
index 8fa4d27..fb5b6157 100644
--- a/components/credential_management/android/java/src/org/chromium/components/credential_management/ThirdPartyCredentialManagerBridge.java
+++ b/components/credential_management/android/java/src/org/chromium/components/credential_management/ThirdPartyCredentialManagerBridge.java
@@ -27,6 +27,8 @@
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
 
+import java.util.Collections;
+
 /** A bridge for interacting with Credential Manager. */
 @JNINamespace("credential_management")
 @NullMarked
@@ -42,13 +44,18 @@
     }
 
     @CalledByNative
-    void get(String origin, Callback<PasswordCredentialResponse> callback) {
+    void get(
+            boolean isAutoSelectAllowed,
+            String origin,
+            Callback<PasswordCredentialResponse> callback) {
         Context context = ContextUtils.getApplicationContext();
         CredentialManager credentialManager =
                 sCredentialManagerForTesting == null
                         ? CredentialManager.create(context)
                         : sCredentialManagerForTesting;
-        GetPasswordOption passwordOption = new GetPasswordOption();
+        GetPasswordOption passwordOption =
+                new GetPasswordOption(
+                        Collections.emptySet(), isAutoSelectAllowed, Collections.emptySet());
         GetCredentialRequest getPasswordRequest =
                 new GetCredentialRequest.Builder()
                         .addCredentialOption(passwordOption)
diff --git a/components/credential_management/android/java/src/org/chromium/components/credential_management/ThirdPartyCredentialManagerBridgeTest.java b/components/credential_management/android/java/src/org/chromium/components/credential_management/ThirdPartyCredentialManagerBridgeTest.java
index 7bf2de1..8a985d1 100644
--- a/components/credential_management/android/java/src/org/chromium/components/credential_management/ThirdPartyCredentialManagerBridgeTest.java
+++ b/components/credential_management/android/java/src/org/chromium/components/credential_management/ThirdPartyCredentialManagerBridgeTest.java
@@ -76,7 +76,7 @@
                         any(Executor.class),
                         any(CredentialManagerCallback.class));
 
-        mBridge.get(ORIGIN, mCredentialResponseCallback);
+        mBridge.get(true, ORIGIN, mCredentialResponseCallback);
 
         verify(mCredentialManager)
                 .getCredentialAsync(
@@ -103,7 +103,7 @@
                         any(Executor.class),
                         any(CredentialManagerCallback.class));
 
-        mBridge.get(ORIGIN, mCredentialResponseCallback);
+        mBridge.get(false, ORIGIN, mCredentialResponseCallback);
 
         verify(mCredentialManager)
                 .getCredentialAsync(
diff --git a/components/credential_management/android/third_party_credential_manager_bridge.cc b/components/credential_management/android/third_party_credential_manager_bridge.cc
index f9dc7db..0970b396 100644
--- a/components/credential_management/android/third_party_credential_manager_bridge.cc
+++ b/components/credential_management/android/third_party_credential_manager_bridge.cc
@@ -66,12 +66,14 @@
         jni_zero::AttachCurrentThread()));
   }
 
-  void Get(const std::string& origin,
+  void Get(bool is_auto_select_allowed,
+           const std::string& origin,
            base::OnceCallback<void(PasswordCredentialResponse)>
                completion_callback) override {
     JNIEnv* env = jni_zero::AttachCurrentThread();
     Java_ThirdPartyCredentialManagerBridge_get(
-        env, java_bridge_, base::android::ConvertUTF8ToJavaString(env, origin),
+        env, java_bridge_, is_auto_select_allowed,
+        base::android::ConvertUTF8ToJavaString(env, origin),
         base::android::ToJniCallback(env, std::move(completion_callback)));
   }
 
@@ -108,12 +110,13 @@
   jni_delegate_->CreateBridge();
 }
 
-void ThirdPartyCredentialManagerBridge::Get(const std::string& origin,
+void ThirdPartyCredentialManagerBridge::Get(bool is_auto_select_allowed,
+                                            const std::string& origin,
                                             GetCallback completion_callback) {
   base::OnceCallback<void(PasswordCredentialResponse)> on_complete =
       base::BindOnce(&OnPasswordCredentialReceived, origin,
                      std::move(completion_callback));
-  jni_delegate_->Get(origin, std::move(on_complete));
+  jni_delegate_->Get(is_auto_select_allowed, origin, std::move(on_complete));
 }
 
 void ThirdPartyCredentialManagerBridge::Store(
diff --git a/components/credential_management/android/third_party_credential_manager_bridge.h b/components/credential_management/android/third_party_credential_manager_bridge.h
index 587a4a7..781b9660 100644
--- a/components/credential_management/android/third_party_credential_manager_bridge.h
+++ b/components/credential_management/android/third_party_credential_manager_bridge.h
@@ -25,7 +25,8 @@
  public:
   virtual ~CredentialManagerBridge() = default;
 
-  virtual void Get(const std::string& origin,
+  virtual void Get(bool is_auto_select_allowed,
+                   const std::string& origin,
                    GetCallback completion_callback) = 0;
 
   virtual void Store(const std::u16string& username,
@@ -51,7 +52,8 @@
     // Gets a credential from the Android Credential Manager.
     // The `completion_callback` should always be invoked on completion, passing
     // the PasswordCredentialResponse.
-    virtual void Get(const std::string& origin,
+    virtual void Get(bool is_auto_select_allowed,
+                     const std::string& origin,
                      base::OnceCallback<void(PasswordCredentialResponse)>
                          completion_callback) = 0;
 
@@ -78,7 +80,9 @@
 
   void Create();
 
-  void Get(const std::string& origin, GetCallback completion_callback) override;
+  void Get(bool is_auto_select_allowed,
+           const std::string& origin,
+           GetCallback completion_callback) override;
 
   void Store(const std::u16string& username,
              const std::u16string& password,
diff --git a/components/credential_management/android/third_party_credential_manager_bridge_unittest.cc b/components/credential_management/android/third_party_credential_manager_bridge_unittest.cc
index c9e30dfa0..3cb8d7083 100644
--- a/components/credential_management/android/third_party_credential_manager_bridge_unittest.cc
+++ b/components/credential_management/android/third_party_credential_manager_bridge_unittest.cc
@@ -42,7 +42,8 @@
 
   void CreateBridge() override {}
 
-  void Get(const std::string& origin,
+  void Get(bool is_auto_select_allowed,
+           const std::string& origin,
            base::OnceCallback<void(PasswordCredentialResponse)>
                completion_callback) override {
     if (simulate_errors_) {
@@ -113,7 +114,8 @@
       mock_callback,
       Run(password_manager::CredentialManagerError::SUCCESS, testing::_))
       .WillOnce(testing::Invoke([&]() { run_loop.Quit(); }));
-  bridge()->Get(kTestOrigin, mock_callback.Get());
+  bridge()->Get(/*is_auto_select_allowed=*/false, kTestOrigin,
+                mock_callback.Get());
   run_loop.Run();
 }
 
@@ -128,7 +130,8 @@
       mock_callback,
       Run(password_manager::CredentialManagerError::UNKNOWN, testing::_))
       .WillOnce(testing::Invoke([&]() { run_loop.Quit(); }));
-  bridge()->Get(kTestOrigin, mock_callback.Get());
+  bridge()->Get(/*is_auto_select_allowed=*/true, kTestOrigin,
+                mock_callback.Get());
   run_loop.Run();
 }
 
@@ -183,7 +186,8 @@
       Run(password_manager::CredentialManagerError::SUCCESS, testing::_))
       .WillOnce(testing::Invoke([&]() { run_loop_get.Quit(); }));
 
-  bridge()->Get(kTestOrigin, mock_get_callback.Get());
+  bridge()->Get(/*is_auto_select_allowed=*/true, kTestOrigin,
+                mock_get_callback.Get());
   run_loop_get.Run();
 }
 
diff --git a/components/credential_management/android/third_party_credential_manager_impl.cc b/components/credential_management/android/third_party_credential_manager_impl.cc
index ddd8915..d86dd8e2 100644
--- a/components/credential_management/android/third_party_credential_manager_impl.cc
+++ b/components/credential_management/android/third_party_credential_manager_impl.cc
@@ -40,13 +40,59 @@
   NOTIMPLEMENTED();
 }
 
+// This method decides if credential picker should be shown.
+
+// Credential mediation can be silent, optional, conditional or
+// required.
+
+// Silent mediation should not show a credential picker even if there
+// are multiple credentials available and return null.
+// Silent mediation can't be implemented here, because the Android API
+// doesn't support it. We'd have to know the amount of available credentials
+// already before calling get.
+
+// By default, the GetCredentialRequest will have optional
+// mediation: if there's more than one matching credential, the system
+// will show the credential picker UI to the user.
+
+// Required mediation will show the credential picker, no matter the amount
+// of choices.
+
+// Conditional mediation allows the user to pick a credential from the
+// picker or avoid selecting a credential without any user-visible error
+// condition. That type of mediotion is also not supported in the Android
+// API.
+
+bool ShouldAllowAutoSelect(
+    password_manager::CredentialMediationRequirement mediation) {
+  switch (mediation) {
+    case password_manager::CredentialMediationRequirement::kOptional:
+      return true;
+    case password_manager::CredentialMediationRequirement::kRequired:
+      return false;
+    case password_manager::CredentialMediationRequirement::kSilent:
+    case password_manager::CredentialMediationRequirement::kConditional:
+      NOTIMPLEMENTED();
+  }
+  return false;
+}
+
 void ThirdPartyCredentialManagerImpl::Get(
     password_manager::CredentialMediationRequirement mediation,
     bool include_passwords,
     const std::vector<GURL>& federations,
     GetCallback callback) {
+  if (mediation == password_manager::CredentialMediationRequirement::kSilent ||
+      mediation ==
+          password_manager::CredentialMediationRequirement::kConditional) {
+    std::move(callback).Run(password_manager::CredentialManagerError::UNKNOWN,
+                            std::nullopt);
+    return;
+  }
+
   // TODO(crbug.com/404199116): Pass all the parameters to the bridge.
-  bridge_->Get(render_frame_host().GetLastCommittedOrigin().Serialize(),
+  bridge_->Get(ShouldAllowAutoSelect(mediation),
+               render_frame_host().GetLastCommittedOrigin().Serialize(),
                std::move(callback));
 }
 
diff --git a/components/credential_management/android/third_party_credential_manager_impl_unittest.cc b/components/credential_management/android/third_party_credential_manager_impl_unittest.cc
index 06d198f..55eadbf 100644
--- a/components/credential_management/android/third_party_credential_manager_impl_unittest.cc
+++ b/components/credential_management/android/third_party_credential_manager_impl_unittest.cc
@@ -32,7 +32,10 @@
   MockThirdPartyCredentialManagerBridge() = default;
   ~MockThirdPartyCredentialManagerBridge() override = default;
 
-  MOCK_METHOD(void, Get, (const std::string&, GetCallback), (override));
+  MOCK_METHOD(void,
+              Get,
+              (bool is_auto_select_allowed, const std::string&, GetCallback),
+              (override));
   MOCK_METHOD(void,
               Store,
               (const std::u16string&,
@@ -84,16 +87,58 @@
   credential_manager()->Store(info, StoreCallback());
 }
 
-TEST_F(ThirdPartyCredentialManagerImplTest, TestGet) {
+TEST_F(ThirdPartyCredentialManagerImplTest, TestGetWithOptionalMediation) {
   content::WebContentsTester::For(web_contents())
       ->NavigateAndCommit(GURL(kTestOrigin));
 
-  EXPECT_CALL(*mock_bridge(), Get(kTestOrigin, _));
+  EXPECT_CALL(*mock_bridge(), Get(/*mediation=*/true, kTestOrigin, _));
 
   credential_manager()->Get(
-      /*mediation=*/password_manager::CredentialMediationRequirement::kSilent,
+      /*mediation=*/password_manager::CredentialMediationRequirement::kOptional,
       /*include_passwords=*/true,
       /*federations=*/std::vector<GURL>(), GetCallback());
 }
 
+TEST_F(ThirdPartyCredentialManagerImplTest, TestGetWithRequiredMediation) {
+  content::WebContentsTester::For(web_contents())
+      ->NavigateAndCommit(GURL(kTestOrigin));
+
+  EXPECT_CALL(*mock_bridge(), Get(/*mediation=*/false, kTestOrigin, _));
+
+  credential_manager()->Get(
+      /*mediation=*/password_manager::CredentialMediationRequirement::kRequired,
+      /*include_passwords=*/true,
+      /*federations=*/std::vector<GURL>(), GetCallback());
+}
+
+TEST_F(ThirdPartyCredentialManagerImplTest, TestGetWithSilentMediation) {
+  base::MockCallback<GetCallback> mock_get_callback;
+  content::WebContentsTester::For(web_contents())
+      ->NavigateAndCommit(GURL(kTestOrigin));
+
+  EXPECT_CALL(mock_get_callback,
+              Run(password_manager::CredentialManagerError::UNKNOWN,
+                  testing::Eq(std::nullopt)));
+
+  credential_manager()->Get(
+      /*mediation=*/password_manager::CredentialMediationRequirement::kSilent,
+      /*include_passwords=*/true,
+      /*federations=*/std::vector<GURL>(), mock_get_callback.Get());
+}
+
+TEST_F(ThirdPartyCredentialManagerImplTest, TestGetWithConditionalMediation) {
+  base::MockCallback<GetCallback> mock_get_callback;
+  content::WebContentsTester::For(web_contents())
+      ->NavigateAndCommit(GURL(kTestOrigin));
+
+  EXPECT_CALL(mock_get_callback,
+              Run(password_manager::CredentialManagerError::UNKNOWN,
+                  testing::Eq(std::nullopt)));
+
+  credential_manager()->Get(
+      /*mediation=*/password_manager::CredentialMediationRequirement::
+          kConditional,
+      /*include_passwords=*/true,
+      /*federations=*/std::vector<GURL>(), mock_get_callback.Get());
+}
 }  // namespace credential_management
diff --git a/components/cronet/gn2bp/gen_android_bp.py b/components/cronet/gn2bp/gen_android_bp.py
index bb39bcdb..5265f93 100755
--- a/components/cronet/gn2bp/gen_android_bp.py
+++ b/components/cronet/gn2bp/gen_android_bp.py
@@ -50,8 +50,6 @@
 additional_args = None
 # Name of the java default module for non-test java modules defined in Android.extras.bp
 java_framework_defaults_module = 'MODIFIED_BY_MAIN_AFTER_PARSING_ARGS_IF_YOU_SEE_THIS_SOMETHING_BROKE_'
-# Name of the java default module for test java modules defined in Android.extras.bp
-java_framework_test_defaults_module = 'MODIFIED_BY_MAIN_AFTER_PARSING_ARGS_IF_YOU_SEE_THIS_SOMETHING_BROKE_'
 # Location of the project in the Android source tree.
 tree_path = 'MODIFIED_BY_MAIN_AFTER_PARSING_ARGS_IF_YOU_SEE_THIS_SOMETHING_BROKE_'
 
@@ -74,9 +72,6 @@
   global java_framework_defaults_module
   java_framework_defaults_module = f'{MODULE_PREFIX}java_framework_defaults'
 
-  global java_framework_test_defaults_module
-  java_framework_test_defaults_module = f'{MODULE_PREFIX}java_framework_test_defaults'
-
   global tree_path
   tree_path = f'external/cronet/{IMPORT_CHANNEL}'
 
@@ -2209,14 +2204,12 @@
   return merged_module
 
 
-def create_java_module(bp_module_name, target, is_test_target, blueprint):
+def create_java_module(bp_module_name, target, blueprint):
 
   def add_java_library_properties(module):
     module.min_sdk_version = _MIN_SDK_VERSION
     module.apex_available = [tethering_apex]
     module.defaults.add(java_framework_defaults_module)
-    if is_test_target:
-      module.defaults.add(java_framework_test_defaults_module)
     module.build_file_path = target.build_file_path
 
   # As hinted in `parse_gn_desc()`, Java GN targets are... complicated.
@@ -2651,8 +2644,7 @@
     # leaf node.
     return ()
   elif target.type == 'java_library':
-    modules = (create_java_module(bp_module_name, target, is_test_target,
-                                  blueprint), )
+    modules = (create_java_module(bp_module_name, target, blueprint), )
   else:
     # Note we don't have to handle `group` targets because parse_gn_desc() never
     # returns any; it just recurses through them and bubbles their dependencies
diff --git a/components/cronet/gn2bp/templates/Android.extras.bp.template b/components/cronet/gn2bp/templates/Android.extras.bp.template
index 0cb202b..39a4e4d 100644
--- a/components/cronet/gn2bp/templates/Android.extras.bp.template
+++ b/components/cronet/gn2bp/templates/Android.extras.bp.template
@@ -161,6 +161,7 @@
     sdk_version: "module_current",
     libs: [
          "framework-connectivity-pre-jarjar-without-cronet",
+         "framework-connectivity.stubs.module_lib",
          "framework-connectivity-t.stubs.module_lib",
          "framework-location.stubs.module_lib",
          "framework-mediaprovider.stubs.module_lib",
@@ -173,18 +174,6 @@
     ],
 }
 
-java_defaults {
-    name: "${GN2BP_MODULE_PREFIX}java_framework_test_defaults",
-    sdk_version: "module_current",
-    libs: [
-         // Some tests depend on http client api (e.g. cronet_all_java__testing)
-         "framework-connectivity-pre-jarjar",
-    ],
-    visibility: [
-        "//external/cronet:__subpackages__",
-    ],
-}
-
 // rust_bindgen modules do not have an `include_dirs` attribute. Due to that,
 // C++ headers generated from rust_bindgen can't reference other headers
 // through paths relative to the repository root. This is what Chromium does
diff --git a/components/data_sharing/internal/group_data_model.cc b/components/data_sharing/internal/group_data_model.cc
index 90e55842..c32746659 100644
--- a/components/data_sharing/internal/group_data_model.cc
+++ b/components/data_sharing/internal/group_data_model.cc
@@ -384,13 +384,13 @@
 void GroupDataModel::NotifyObserversAboutChangedMembers(
     const GroupData& old_group_data,
     const GroupData& new_group_data) {
-  std::vector<GaiaId> old_members_gaia_ids;
+  std::set<GaiaId> old_members_gaia_ids;
   for (const auto& member : old_group_data.members) {
-    old_members_gaia_ids.push_back(member.gaia_id);
+    old_members_gaia_ids.insert(member.gaia_id);
   }
-  std::vector<GaiaId> new_members_gaia_ids;
+  std::set<GaiaId> new_members_gaia_ids;
   for (const auto& member : new_group_data.members) {
-    new_members_gaia_ids.push_back(member.gaia_id);
+    new_members_gaia_ids.insert(member.gaia_id);
   }
 
   std::vector<GaiaId> added_members_gaia_ids;
diff --git a/components/data_sharing/internal/group_data_model_unittest.cc b/components/data_sharing/internal/group_data_model_unittest.cc
index 4c9f6954..0ed1f18 100644
--- a/components/data_sharing/internal/group_data_model_unittest.cc
+++ b/components/data_sharing/internal/group_data_model_unittest.cc
@@ -476,6 +476,35 @@
   WaitForGroupUpdated(group_id);
 }
 
+TEST_F(GroupDataModelTest,
+       ShouldNotifyAboutGroupChanges_MultipleMembersAddedRemoved) {
+  WaitForModelLoaded();
+
+  const GroupId group_id = MimicGroupAddedServerSide("group");
+  WaitForGroupAdded(group_id);
+
+  // Test that OnMemberAdded() is called when a member is added.
+  const GaiaId member_gaia_id1("gaia_id99");
+  EXPECT_CALL(model_observer(),
+              OnMemberAdded(group_id, member_gaia_id1, NotNullTime()));
+  MimicMemberAddedServerSide(group_id, member_gaia_id1);
+  WaitForGroupUpdated(group_id);
+  testing::Mock::VerifyAndClearExpectations(&model_observer());
+
+  const GaiaId member_gaia_id2("gaia_id2");
+  EXPECT_CALL(model_observer(),
+              OnMemberAdded(group_id, member_gaia_id2, NotNullTime()));
+  MimicMemberAddedServerSide(group_id, member_gaia_id2);
+  WaitForGroupUpdated(group_id);
+  testing::Mock::VerifyAndClearExpectations(&model_observer());
+
+  // Test that OnMemberRemoved() is called when a member is removed.
+  EXPECT_CALL(model_observer(),
+              OnMemberRemoved(group_id, member_gaia_id1, NotNullTime()));
+  MimicMemberRemovedServerSide(group_id, member_gaia_id1);
+  WaitForGroupUpdated(group_id);
+}
+
 TEST_F(GroupDataModelTest, ShouldNotifyOnSyncBridgeUpdateTypeChanged) {
   EXPECT_CALL(model_observer(), OnSyncBridgeUpdateTypeChanged(
                                     Eq(SyncBridgeUpdateType::kDisableSync)))
diff --git a/components/device_event_log/device_event_log_export.h b/components/device_event_log/device_event_log_export.h
index 4c4cb52..6ca238a 100644
--- a/components/device_event_log/device_event_log_export.h
+++ b/components/device_event_log/device_event_log_export.h
@@ -16,19 +16,11 @@
 #endif  // defined(DEVICE_EVENT_LOG_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-
-#if defined(DEVICE_EVENT_LOG_IMPLEMENTATION)
 #define DEVICE_EVENT_LOG_EXPORT __attribute__((visibility("default")))
-#else
-#define DEVICE_EVENT_LOG_EXPORT
-#endif  // defined(DEVICE_EVENT_LOG_IMPLEMENTATION)
-
 #endif  // defined(WIN32)
 
 #else  // defined(COMPONENT_BUILD)
-
 #define DEVICE_EVENT_LOG_EXPORT
-
 #endif
 
 #endif  // COMPONENTS_DEVICE_EVENT_LOG_DEVICE_EVENT_LOG_EXPORT_H_
diff --git a/components/discardable_memory/common/discardable_memory_export.h b/components/discardable_memory/common/discardable_memory_export.h
index 857815c..6304070 100644
--- a/components/discardable_memory/common/discardable_memory_export.h
+++ b/components/discardable_memory/common/discardable_memory_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(DISCARDABLE_MEMORY_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(DISCARDABLE_MEMORY_IMPLEMENTATION)
 #define DISCARDABLE_MEMORY_EXPORT __attribute__((visibility("default")))
-#else
-#define DISCARDABLE_MEMORY_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/components/domain_reliability/domain_reliability_export.h b/components/domain_reliability/domain_reliability_export.h
index 169d9b66..87b98d6 100644
--- a/components/domain_reliability/domain_reliability_export.h
+++ b/components/domain_reliability/domain_reliability_export.h
@@ -16,11 +16,7 @@
 
 #else  // defined(WIN32)
 
-#if defined(DOMAIN_RELIABILITY_IMPLEMENTATION)
 #define DOMAIN_RELIABILITY_EXPORT __attribute__((visibility("default")))
-#else
-#define DOMAIN_RELIABILITY_EXPORT
-#endif
 
 #endif  // defined(WIN32)
 #else  // defined(COMPONENT_BUILD)
diff --git a/components/download/public/common/download_export.h b/components/download/public/common/download_export.h
index abc049fc..4be8087 100644
--- a/components/download/public/common/download_export.h
+++ b/components/download/public/common/download_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(COMPONENTS_DOWNLOAD_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(COMPONENTS_DOWNLOAD_IMPLEMENTATION)
 #define COMPONENTS_DOWNLOAD_EXPORT __attribute__((visibility("default")))
-#else
-#define COMPONENTS_DOWNLOAD_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/components/drive/drive_export.h b/components/drive/drive_export.h
index 5c8d3c0..5d1fa02 100644
--- a/components/drive/drive_export.h
+++ b/components/drive/drive_export.h
@@ -17,13 +17,8 @@
 #endif  // defined(COMPONENTS_DRIVE_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(COMPONENTS_DRIVE_IMPLEMENTATION)
 #define COMPONENTS_DRIVE_EXPORT __attribute__((visibility("default")))
 #define COMPONENTS_DRIVE_EXPORT_PRIVATE __attribute__((visibility("default")))
-#else
-#define COMPONENTS_DRIVE_EXPORT
-#define COMPONENTS_DRIVE_EXPORT_PRIVATE
-#endif
 #endif
 
 #else  /// defined(COMPONENT_BUILD)
diff --git a/components/enterprise/common/proto/BUILD.gn b/components/enterprise/common/proto/BUILD.gn
index 98060e8a..d1144a66 100644
--- a/components/enterprise/common/proto/BUILD.gn
+++ b/components/enterprise/common/proto/BUILD.gn
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import(
+    "//components/safe_browsing/core/common/proto_to_value/proto_to_value.gni")
 import("//third_party/libprotobuf-mutator/fuzzable_proto_library.gni")
 import("//third_party/protobuf/proto_library.gni")
 
@@ -19,6 +21,15 @@
   deps = [ "//components/safe_browsing/core/common/proto:csd_proto" ]
 }
 
+proto_to_value("connectors_proto_to_value") {
+  sources = [ "connectors.proto" ]
+
+  deps = [
+    "//components/safe_browsing/core/common/proto:csd_proto",
+    "//components/safe_browsing/core/common/proto:csd_proto_to_value",
+  ]
+}
+
 proto_library("dlp_policy_event_proto") {
   sources = [ "synced/dlp_policy_event.proto" ]
 }
diff --git a/components/error_page/content/browser/net_error_auto_reloader_browsertest.cc b/components/error_page/content/browser/net_error_auto_reloader_browsertest.cc
index eec3b07..819a3b2 100644
--- a/components/error_page/content/browser/net_error_auto_reloader_browsertest.cc
+++ b/components/error_page/content/browser/net_error_auto_reloader_browsertest.cc
@@ -11,6 +11,7 @@
 #include "base/memory/raw_ptr.h"
 #include "base/test/bind.h"
 #include "content/public/browser/navigation_controller.h"
+#include "content/public/browser/navigation_throttle_registry.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/common/content_features.h"
@@ -43,8 +44,9 @@
         interceptor_(base::BindLambdaForTesting(
             [this,
              error](content::URLLoaderInterceptor::RequestParams* params) {
-              if (params->url_request.url != url_)
+              if (params->url_request.url != url_) {
                 return false;
+              }
               network::URLLoaderCompletionStatus status;
               status.error_code = error;
               params->client->OnComplete(status);
@@ -133,15 +135,17 @@
   // content::WebContentsObserver:
   void DidFinishNavigation(content::NavigationHandle* handle) override {
     DCHECK(throttle_);
-    if (handle == throttle_->navigation_handle())
+    if (handle == throttle_->navigation_handle()) {
       finish_wait_loop_.Quit();
+    }
   }
 
  private:
   std::unique_ptr<content::NavigationThrottle> MaybeCreateThrottle(
       content::NavigationHandle* handle) {
-    if (throttle_)
+    if (throttle_) {
       return nullptr;
+    }
 
     auto throttle = std::make_unique<DeferringThrottle>(
         handle, defer_wait_loop_.QuitClosure());
@@ -170,15 +174,10 @@
 
     content::ShellContentBrowserClient::Get()
         ->set_create_throttles_for_navigation_callback(base::BindRepeating(
-            [](content::NavigationHandle* handle)
-                -> std::vector<std::unique_ptr<content::NavigationThrottle>> {
-              std::vector<std::unique_ptr<content::NavigationThrottle>>
-                  throttles;
-              auto throttle =
-                  NetErrorAutoReloader::MaybeCreateThrottleFor(handle);
-              if (throttle)
-                throttles.push_back(std::move(throttle));
-              return throttles;
+            [](content::NavigationThrottleRegistry& registry) -> void {
+              registry.MaybeAddThrottle(
+                  NetErrorAutoReloader::MaybeCreateThrottleFor(
+                      &registry.GetNavigationHandle()));
             }));
   }
 
@@ -193,8 +192,9 @@
   std::optional<base::TimeDelta> GetCurrentAutoReloadDelay() {
     const std::optional<base::OneShotTimer>& timer =
         GetAutoReloader()->next_reload_timer_for_testing();
-    if (!timer)
+    if (!timer) {
       return std::nullopt;
+    }
     return timer->GetCurrentDelay();
   }
 
@@ -239,8 +239,9 @@
         error_page::NetErrorAutoReloader::FromWebContents(wc);
     std::optional<base::OneShotTimer>& timer =
         reloader->next_reload_timer_for_testing();
-    if (timer && timer->IsRunning())
+    if (timer && timer->IsRunning()) {
       timer->FireNow();
+    }
   }
 
   static void SimulateNetworkGoingOnline(content::WebContents* wc) {
diff --git a/components/exo/BUILD.gn b/components/exo/BUILD.gn
index c2f8dae..80b05c4 100644
--- a/components/exo/BUILD.gn
+++ b/components/exo/BUILD.gn
@@ -239,7 +239,6 @@
     "//base",
     "//base/test:test_support",
     "//chromeos/ui/base",
-    "//components/exo/wayland:test_controller_stub",
     "//components/exo/wayland:ui_controls_protocol_stub",
     "//components/viz/service",
     "//gpu",
diff --git a/components/exo/surface.h b/components/exo/surface.h
index 284c2d81..76a2b2b 100644
--- a/components/exo/surface.h
+++ b/components/exo/surface.h
@@ -514,7 +514,6 @@
     ~State();
 
     bool operator==(const State& other) const;
-    bool operator!=(const State& other) const { return !(*this == other); }
 
     cc::Region opaque_region;
     std::optional<cc::Region> input_region;
diff --git a/components/exo/wayland/BUILD.gn b/components/exo/wayland/BUILD.gn
index c95d9ffb..e7a7bdc 100644
--- a/components/exo/wayland/BUILD.gn
+++ b/components/exo/wayland/BUILD.gn
@@ -118,7 +118,6 @@
     "zcr_stylus.h",
     "zcr_stylus_tools.cc",
     "zcr_stylus_tools.h",
-    "zcr_test_controller.h",
     "zcr_touchpad_haptics.cc",
     "zcr_touchpad_haptics.h",
     "zcr_ui_controls.h",
@@ -229,26 +228,6 @@
   }
 }
 
-static_library("test_controller") {
-  testonly = true
-  sources = [ "zcr_test_controller.cc" ]
-  deps = [
-    ":wayland",
-    "//base/test:test_support",
-    "//third_party/wayland:wayland_server",
-    "//third_party/wayland-protocols:test_controller_protocol",
-    "//ui/events",
-  ]
-}
-
-static_library("test_controller_stub") {
-  testonly = false
-  sources = [
-    "zcr_test_controller.h",
-    "zcr_test_controller_stub.cc",
-  ]
-}
-
 static_library("ui_controls_protocol") {
   testonly = true
   defines = [ "UI_CONTROLS_PROTOCOL_IMPLEMENTATION" ]
diff --git a/components/exo/wayland/server.cc b/components/exo/wayland/server.cc
index d682617d..cdeb838 100644
--- a/components/exo/wayland/server.cc
+++ b/components/exo/wayland/server.cc
@@ -81,7 +81,6 @@
 #include "components/exo/wayland/zcr_remote_shell_v2.h"
 #include "components/exo/wayland/zcr_stylus.h"
 #include "components/exo/wayland/zcr_stylus_tools.h"
-#include "components/exo/wayland/zcr_test_controller.h"
 #include "components/exo/wayland/zcr_touchpad_haptics.h"
 #include "components/exo/wayland/zcr_ui_controls.h"
 #include "components/exo/wayland/zcr_vsync_feedback.h"
@@ -350,7 +349,6 @@
                    /*version=*/1, display_, bind_zwp_idle_inhibit_manager);
 
   ui_controls_holder_ = std::make_unique<UiControls>(this);
-  test_controller_ = std::make_unique<TestController>(this);
 
   zcr_keyboard_extension_data_ =
       std::make_unique<WaylandKeyboardExtension>(serial_tracker_.get());
diff --git a/components/exo/wayland/server.h b/components/exo/wayland/server.h
index 89f9f50..e46e5f2 100644
--- a/components/exo/wayland/server.h
+++ b/components/exo/wayland/server.h
@@ -28,7 +28,6 @@
 
 class ClientTracker;
 class SerialTracker;
-class TestController;
 class UiControls;
 struct WaylandDataDeviceManager;
 struct WaylandKeyboardExtension;
@@ -140,7 +139,6 @@
   std::unique_ptr<UiControls> ui_controls_holder_;
   std::unique_ptr<ClientTracker> client_tracker_;
   std::unique_ptr<WaylandProtocolLogger> wayland_protocol_logger_;
-  std::unique_ptr<TestController> test_controller_;
 };
 
 }  // namespace wayland
diff --git a/components/exo/wayland/zcr_test_controller.cc b/components/exo/wayland/zcr_test_controller.cc
deleted file mode 100644
index 6276df76..0000000
--- a/components/exo/wayland/zcr_test_controller.cc
+++ /dev/null
@@ -1,77 +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/exo/wayland/zcr_test_controller.h"
-
-#include <stdint.h>
-#include <test-controller-unstable-v1-server-protocol.h>
-#include <wayland-server-core.h>
-
-#include <memory>
-
-#include "base/check.h"
-#include "base/test/simple_test_tick_clock.h"
-#include "base/time/time.h"
-#include "components/exo/wayland/server.h"
-#include "components/exo/wayland/server_util.h"
-#include "ui/events/base_event_utils.h"
-
-namespace exo::wayland {
-
-struct TestController::State {
-  std::unique_ptr<base::SimpleTestTickClock> clock;
-};
-
-namespace {
-
-void test_controller_mock_event_tick_clock_start(struct wl_client* client,
-                                                 struct wl_resource* resource) {
-  auto* state = GetUserDataAs<TestController::State>(resource);
-  CHECK(state);
-  state->clock = std::make_unique<base::SimpleTestTickClock>();
-  state->clock->SetNowTicks(base::TimeTicks::Now());
-  ui::SetEventTickClockForTesting(state->clock.get());
-}
-
-void test_controller_mock_event_tick_clock_advance(struct wl_client* client,
-                                                   struct wl_resource* resource,
-                                                   uint32_t milliseconds) {
-  auto* state = GetUserDataAs<TestController::State>(resource);
-  CHECK(state);
-  if (state->clock) {
-    state->clock->Advance(base::Milliseconds(milliseconds));
-  }
-}
-
-const struct zcr_test_controller_v1_interface test_controller_implementation = {
-    test_controller_mock_event_tick_clock_start,
-    test_controller_mock_event_tick_clock_advance};
-
-void destroy_test_controller_resource(struct wl_resource* resource) {
-  ui::SetEventTickClockForTesting(nullptr);
-}
-
-void bind_test_controller(wl_client* client,
-                          void* data,
-                          uint32_t version,
-                          uint32_t id) {
-  wl_resource* resource = wl_resource_create(
-      client, &zcr_test_controller_v1_interface, version, id);
-
-  wl_resource_set_implementation(resource, &test_controller_implementation,
-                                 data, destroy_test_controller_resource);
-}
-
-}  // namespace
-
-TestController::TestController(Server* server)
-    : state_(std::make_unique<TestController::State>()) {
-  wl_global_create(server->GetWaylandDisplay(),
-                   &zcr_test_controller_v1_interface,
-                   /*version=*/1, state_.get(), bind_test_controller);
-}
-
-TestController::~TestController() = default;
-
-}  // namespace exo::wayland
diff --git a/components/exo/wayland/zcr_test_controller.h b/components/exo/wayland/zcr_test_controller.h
deleted file mode 100644
index 108291e..0000000
--- a/components/exo/wayland/zcr_test_controller.h
+++ /dev/null
@@ -1,30 +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_EXO_WAYLAND_ZCR_TEST_CONTROLLER_H_
-#define COMPONENTS_EXO_WAYLAND_ZCR_TEST_CONTROLLER_H_
-
-#include <memory>
-
-namespace exo::wayland {
-
-class Server;
-
-class TestController {
- public:
-  TestController(Server* server);
-  TestController(const TestController&) = delete;
-  TestController& operator=(const TestController&) = delete;
-  ~TestController();
-  // State is declared here but not defined because its definition references
-  // symbols in test-only dependencies.
-  struct State;
-
- private:
-  std::unique_ptr<State> state_;
-};
-
-}  // namespace exo::wayland
-
-#endif  // COMPONENTS_EXO_WAYLAND_ZCR_TEST_CONTROLLER_H_
diff --git a/components/exo/wayland/zcr_test_controller_stub.cc b/components/exo/wayland/zcr_test_controller_stub.cc
deleted file mode 100644
index ba84d84..0000000
--- a/components/exo/wayland/zcr_test_controller_stub.cc
+++ /dev/null
@@ -1,17 +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/exo/wayland/zcr_test_controller.h"
-
-namespace exo::wayland {
-
-class Server;
-
-TestController::TestController(Server* server) {}
-
-TestController::~TestController() = default;
-
-struct TestController::State {};
-
-}  // namespace exo::wayland
diff --git a/components/external_intents/android/external_intents_features.cc b/components/external_intents/android/external_intents_features.cc
index 6c81905..0fd49eca 100644
--- a/components/external_intents/android/external_intents_features.cc
+++ b/components/external_intents/android/external_intents_features.cc
@@ -26,7 +26,7 @@
 // Array of features exposed through the Java ExternalIntentsFeatures API.
 const base::Feature* const kFeaturesExposedToJava[] = {
     &kExternalNavigationDebugLogs, &kBlockFrameRenavigations,
-    &kBlockIntentsToSelf};
+    &kBlockIntentsToSelf, &kNavigationCaptureRefactorAndroid};
 
 }  // namespace
 
@@ -44,6 +44,10 @@
              "BlockIntentsToSelf",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
+BASE_FEATURE(kNavigationCaptureRefactorAndroid,
+             "NavigationCaptureRefactorAndroid",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 static jlong JNI_ExternalIntentsFeatures_GetFeature(JNIEnv* env, jint ordinal) {
   return reinterpret_cast<jlong>(kFeaturesExposedToJava[ordinal]);
 }
diff --git a/components/external_intents/android/external_intents_features.h b/components/external_intents/android/external_intents_features.h
index 1c7535d..8eb1243 100644
--- a/components/external_intents/android/external_intents_features.h
+++ b/components/external_intents/android/external_intents_features.h
@@ -12,6 +12,7 @@
 BASE_DECLARE_FEATURE(kExternalNavigationDebugLogs);
 BASE_DECLARE_FEATURE(kBlockFrameRenavigations);
 BASE_DECLARE_FEATURE(kBlockIntentsToSelf);
+BASE_DECLARE_FEATURE(kNavigationCaptureRefactorAndroid);
 
 }  // namespace external_intents
 
diff --git a/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalIntentsFeatures.java b/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalIntentsFeatures.java
index e2e4578..c3f5b39 100644
--- a/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalIntentsFeatures.java
+++ b/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalIntentsFeatures.java
@@ -23,6 +23,8 @@
     public static final String EXTERNAL_NAVIGATION_DEBUG_LOGS_NAME = "ExternalNavigationDebugLogs";
     public static final String BLOCK_FRAME_RENAVIGATIONS_NAME = "BlockFrameRenavigations3";
     public static final String BLOCK_INTENTS_TO_SELF_NAME = "BlockIntentsToSelf";
+    public static final String NAVIGATION_CAPTURE_REFACTOR_ANDROID_NAME =
+            "NavigationCaptureRefactorAndroid";
 
     public static final ExternalIntentsFeatures EXTERNAL_NAVIGATION_DEBUG_LOGS =
             new ExternalIntentsFeatures(0, EXTERNAL_NAVIGATION_DEBUG_LOGS_NAME);
@@ -33,6 +35,9 @@
     public static final ExternalIntentsFeatures BLOCK_INTENTS_TO_SELF =
             new ExternalIntentsFeatures(2, BLOCK_INTENTS_TO_SELF_NAME);
 
+    public static final ExternalIntentsFeatures NAVIGATION_CAPTURE_REFACTOR_ANDROID =
+            new ExternalIntentsFeatures(3, NAVIGATION_CAPTURE_REFACTOR_ANDROID_NAME);
+
     private final int mOrdinal;
 
     private ExternalIntentsFeatures(int ordinal, String name) {
diff --git a/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationHandler.java b/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationHandler.java
index 7567dd7a..2d78a72 100644
--- a/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationHandler.java
+++ b/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationHandler.java
@@ -1546,6 +1546,11 @@
             Intent targetIntent,
             GURL browserFallbackUrl,
             MutableBoolean canLaunchExternalFallbackResult) {
+
+        if (debug() && ExternalIntentsFeatures.NAVIGATION_CAPTURE_REFACTOR_ANDROID.isEnabled()) {
+            Log.i(TAG, "Navigation Capture refactor feature enabled");
+        }
+
         sanitizeQueryIntentActivitiesIntent(targetIntent);
 
         // Any subsequent navigations should cancel the existing dialog.
diff --git a/components/gcm_driver/common/gcm_driver_export.h b/components/gcm_driver/common/gcm_driver_export.h
index aa47838..9c767a3 100644
--- a/components/gcm_driver/common/gcm_driver_export.h
+++ b/components/gcm_driver/common/gcm_driver_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(GCM_DRIVER_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(GCM_DRIVER_IMPLEMENTATION)
 #define GCM_DRIVER_EXPORT __attribute__((visibility("default")))
-#else
-#define GCM_DRIVER_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/components/gwp_asan/client/export.h b/components/gwp_asan/client/export.h
index 99043e7..052dba5 100644
--- a/components/gwp_asan/client/export.h
+++ b/components/gwp_asan/client/export.h
@@ -15,11 +15,7 @@
 #endif  // defined(GWP_ASAN_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(GWP_ASAN_IMPLEMENTATION)
 #define GWP_ASAN_EXPORT __attribute__((visibility("default")))
-#else
-#define GWP_ASAN_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/components/image_fetcher/core/request_metadata.cc b/components/image_fetcher/core/request_metadata.cc
index 4fbfca2c..4ea9846 100644
--- a/components/image_fetcher/core/request_metadata.cc
+++ b/components/image_fetcher/core/request_metadata.cc
@@ -9,14 +9,4 @@
 RequestMetadata::RequestMetadata()
     : http_response_code(RESPONSE_CODE_INVALID) {}
 
-bool operator==(const RequestMetadata& lhs, const RequestMetadata& rhs) {
-  return lhs.mime_type == rhs.mime_type &&
-         lhs.http_response_code == rhs.http_response_code &&
-         lhs.content_location_header == rhs.content_location_header;
-}
-
-bool operator!=(const RequestMetadata& lhs, const RequestMetadata& rhs) {
-  return !(lhs == rhs);
-}
-
 }  // namespace image_fetcher
diff --git a/components/image_fetcher/core/request_metadata.h b/components/image_fetcher/core/request_metadata.h
index 7731941..4965461 100644
--- a/components/image_fetcher/core/request_metadata.h
+++ b/components/image_fetcher/core/request_metadata.h
@@ -17,14 +17,14 @@
 
   RequestMetadata();
 
+  friend bool operator==(const RequestMetadata&,
+                         const RequestMetadata&) = default;
+
   std::string mime_type;
   int http_response_code;
   std::string content_location_header;
 };
 
-bool operator==(const RequestMetadata& lhs, const RequestMetadata& rhs);
-bool operator!=(const RequestMetadata& lhs, const RequestMetadata& rhs);
-
 }  // namespace image_fetcher
 
 #endif  // COMPONENTS_IMAGE_FETCHER_CORE_REQUEST_METADATA_H_
diff --git a/components/ip_protection/common/ip_protection_probabilistic_reveal_token_crypter.cc b/components/ip_protection/common/ip_protection_probabilistic_reveal_token_crypter.cc
index 50dfa9a..0f28790 100644
--- a/components/ip_protection/common/ip_protection_probabilistic_reveal_token_crypter.cc
+++ b/components/ip_protection/common/ip_protection_probabilistic_reveal_token_crypter.cc
@@ -12,6 +12,7 @@
 
 #include "base/memory/ptr_util.h"
 #include "base/sequence_checker.h"
+#include "base/types/expected.h"
 #include "components/ip_protection/common/ip_protection_data_types.h"
 #include "components/ip_protection/common/ip_protection_telemetry.h"
 #include "third_party/abseil-cpp/absl/status/statusor.h"
@@ -33,55 +34,80 @@
 
 // `group` should outlive returned encrypter. Used by static creator.
 // In anonymous namespace to prevent misuse.
-absl::StatusOr<std::unique_ptr<ElGamalEncrypter>> CreateEncrypter(
+base::expected<std::unique_ptr<ElGamalEncrypter>, absl::Status> CreateEncrypter(
     ECGroup const* group,
     const std::string& serialized_public_key) {
-  PJC_ASSIGN_OR_RETURN(ECPoint g, group->GetFixedGenerator());
-  PJC_ASSIGN_OR_RETURN(ECPoint y, group->CreateECPoint(serialized_public_key));
+  absl::StatusOr<ECPoint> maybe_g = group->GetFixedGenerator();
+  if (!maybe_g.ok()) {
+    return base::unexpected(maybe_g.status());
+  }
+
+  absl::StatusOr<ECPoint> maybe_y = group->CreateECPoint(serialized_public_key);
+  if (!maybe_y.ok()) {
+    return base::unexpected(maybe_y.status());
+  }
   return std::make_unique<ElGamalEncrypter>(
-      group,
-      std::make_unique<PublicKey>(PublicKey{std::move(g), std::move(y)}));
+      group, std::make_unique<PublicKey>(PublicKey{
+                 std::move(maybe_g).value(), std::move(maybe_y).value()}));
 }
 
 // `group` should outlive returned ciphertexts. Used by static creator.
 // In anonymous namespace to prevent misuse.
-absl::StatusOr<std::vector<Ciphertext>> CreateCiphertext(
+base::expected<std::vector<Ciphertext>, absl::Status> CreateCiphertext(
     ECGroup const* group,
     const std::vector<ProbabilisticRevealToken>& tokens) {
   std::vector<Ciphertext> ciphertext;
   ciphertext.reserve(tokens.size());
   for (const auto& t : tokens) {
-    PJC_ASSIGN_OR_RETURN(ECPoint u, group->CreateECPoint(t.u));
-    PJC_ASSIGN_OR_RETURN(ECPoint e, group->CreateECPoint(t.e));
-    ciphertext.emplace_back(std::move(u), std::move(e));
+    absl::StatusOr<ECPoint> maybe_u = group->CreateECPoint(t.u);
+    if (!maybe_u.ok()) {
+      return base::unexpected(maybe_u.status());
+    }
+    absl::StatusOr<ECPoint> maybe_e = group->CreateECPoint(t.e);
+    if (!maybe_e.ok()) {
+      return base::unexpected(maybe_e.status());
+    }
+    ciphertext.emplace_back(std::move(maybe_u).value(),
+                            std::move(maybe_e).value());
   }
   return ciphertext;
 }
 
 }  // namespace
 
-absl::StatusOr<std::unique_ptr<IpProtectionProbabilisticRevealTokenCrypter>>
+base::expected<std::unique_ptr<IpProtectionProbabilisticRevealTokenCrypter>,
+               absl::Status>
 IpProtectionProbabilisticRevealTokenCrypter::Create(
     const std::string& serialized_public_key,
     const std::vector<ProbabilisticRevealToken>& tokens) {
   auto context = std::make_unique<Context>();
-  std::unique_ptr<ECGroup> group;
-  {
-    PJC_ASSIGN_OR_RETURN(ECGroup local_group,
-                         ECGroup::Create(NID_X9_62_prime256v1, context.get()));
-    group = std::make_unique<ECGroup>(std::move(local_group));
+  absl::StatusOr<ECGroup> local_group =
+      ECGroup::Create(NID_X9_62_prime256v1, context.get());
+  if (!local_group.ok()) {
+    return base::unexpected(local_group.status());
   }
-  PJC_ASSIGN_OR_RETURN(std::unique_ptr<ElGamalEncrypter> encrypter,
-                       CreateEncrypter(group.get(), serialized_public_key));
-  PJC_ASSIGN_OR_RETURN(std::vector<Ciphertext> ciphertext,
-                       CreateCiphertext(group.get(), tokens));
+  std::unique_ptr<ECGroup> group =
+      std::make_unique<ECGroup>(std::move(local_group).value());
+  base::expected<std::unique_ptr<ElGamalEncrypter>, absl::Status>
+      maybe_encrypter = CreateEncrypter(group.get(), serialized_public_key);
+  if (!maybe_encrypter.has_value()) {
+    return base::unexpected(maybe_encrypter.error());
+  }
+
+  base::expected<std::vector<Ciphertext>, absl::Status> maybe_ciphertext =
+      CreateCiphertext(group.get(), tokens);
+  if (!maybe_ciphertext.has_value()) {
+    return base::unexpected(maybe_ciphertext.error());
+  }
+
   // Can not use `make_unique` since constructor is private.
   // Can not use `return std::unique_ptr`, git cl upload
   // returns pre-submit error and recommends using base::WrapUnique.
   return base::WrapUnique<IpProtectionProbabilisticRevealTokenCrypter>(
       new IpProtectionProbabilisticRevealTokenCrypter(
-          std::move(context), std::move(group), std::move(encrypter),
-          std::move(ciphertext)));
+          std::move(context), std::move(group),
+          std::move(maybe_encrypter).value(),
+          std::move(maybe_ciphertext).value()));
 }
 
 IpProtectionProbabilisticRevealTokenCrypter::
@@ -105,13 +131,22 @@
     const std::string& serialized_public_key,
     const std::vector<ProbabilisticRevealToken>& tokens) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  PJC_ASSIGN_OR_RETURN(std::unique_ptr<ElGamalEncrypter> encrypter,
-                       CreateEncrypter(group_.get(), serialized_public_key));
-  PJC_ASSIGN_OR_RETURN(std::vector<Ciphertext> ciphertext,
-                       CreateCiphertext(group_.get(), tokens));
+  base::expected<std::unique_ptr<ElGamalEncrypter>, absl::Status>
+      maybe_encrypter = CreateEncrypter(group_.get(), serialized_public_key);
+  if (!maybe_encrypter.has_value()) {
+    return maybe_encrypter.error();
+  }
+
+  base::expected<std::vector<Ciphertext>, absl::Status> maybe_ciphertext =
+      CreateCiphertext(group_.get(), tokens);
+  if (!maybe_ciphertext.has_value()) {
+    return maybe_ciphertext.error();
+  }
+
   // Creating encrypter and ciphertext succeeded, set members.
-  encrypter_.reset(encrypter.release());
-  ciphertext_ = std::move(ciphertext);
+  encrypter_ = std::move(maybe_encrypter).value();
+
+  ciphertext_ = std::move(maybe_ciphertext).value();
   return absl::OkStatus();
 }
 
@@ -123,21 +158,36 @@
   ciphertext_.clear();
 }
 
-absl::StatusOr<ProbabilisticRevealToken>
+base::expected<ProbabilisticRevealToken, absl::Status>
 IpProtectionProbabilisticRevealTokenCrypter::Randomize(size_t i) const {
   base::TimeTicks randomization_start_time = base::TimeTicks::Now();
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (i >= ciphertext_.size()) {
-    return absl::InvalidArgumentError("invalid index");
+    return base::unexpected(absl::InvalidArgumentError("invalid index"));
   }
-  PJC_ASSIGN_OR_RETURN(Ciphertext randomized_ciphertext,
-                       encrypter_->ReRandomize(ciphertext_[i]));
+  absl::StatusOr<Ciphertext> maybe_randomized_ciphertext =
+      encrypter_->ReRandomize(ciphertext_[i]);
+  if (!maybe_randomized_ciphertext.ok()) {
+    return base::unexpected(maybe_randomized_ciphertext.status());
+  }
+  const auto& randomized_ciphertext = maybe_randomized_ciphertext.value();
+
   ProbabilisticRevealToken randomized_token;
   randomized_token.version = 1;
-  PJC_ASSIGN_OR_RETURN(randomized_token.u,
-                       randomized_ciphertext.u.ToBytesCompressed());
-  PJC_ASSIGN_OR_RETURN(randomized_token.e,
-                       randomized_ciphertext.e.ToBytesCompressed());
+  absl::StatusOr<std::string> maybe_serialized_u =
+      randomized_ciphertext.u.ToBytesCompressed();
+  if (!maybe_serialized_u.ok()) {
+    return base::unexpected(maybe_serialized_u.status());
+  }
+  randomized_token.u = std::move(maybe_serialized_u).value();
+
+  absl::StatusOr<std::string> maybe_serialized_e =
+      randomized_ciphertext.e.ToBytesCompressed();
+  if (!maybe_serialized_e.ok()) {
+    return base::unexpected(maybe_serialized_e.status());
+  }
+  randomized_token.e = std::move(maybe_serialized_e).value();
+
   Telemetry().ProbabilisticRevealTokenRandomizationTime(
       base::TimeTicks::Now() - randomization_start_time);
   return randomized_token;
diff --git a/components/ip_protection/common/ip_protection_probabilistic_reveal_token_crypter.h b/components/ip_protection/common/ip_protection_probabilistic_reveal_token_crypter.h
index 7b8e467..ea8bcf4 100644
--- a/components/ip_protection/common/ip_protection_probabilistic_reveal_token_crypter.h
+++ b/components/ip_protection/common/ip_protection_probabilistic_reveal_token_crypter.h
@@ -12,9 +12,9 @@
 
 #include "base/sequence_checker.h"
 #include "base/thread_annotations.h"
+#include "base/types/expected.h"
 #include "components/ip_protection/common/ip_protection_data_types.h"
 #include "third_party/abseil-cpp/absl/status/status.h"
-#include "third_party/abseil-cpp/absl/status/statusor.h"
 #include "third_party/private-join-and-compute/src/crypto/context.h"
 #include "third_party/private-join-and-compute/src/crypto/ec_group.h"
 #include "third_party/private-join-and-compute/src/crypto/elgamal.h"
@@ -31,8 +31,9 @@
 class IpProtectionProbabilisticRevealTokenCrypter {
  public:
   // Returns a unique pointer to crypter if successful.
-  static absl::StatusOr<
-      std::unique_ptr<IpProtectionProbabilisticRevealTokenCrypter>>
+  static base::expected<
+      std::unique_ptr<IpProtectionProbabilisticRevealTokenCrypter>,
+      absl::Status>
   Create(const std::string& serialized_public_key,
          const std::vector<ProbabilisticRevealToken>& tokens);
   ~IpProtectionProbabilisticRevealTokenCrypter();
@@ -51,7 +52,8 @@
   // ProbabilisticRevealToken by serializing it, if successful. This method
   // fails if there is no ciphertext for index `i` or `encrypter_.ReRandomize()`
   // fails.
-  absl::StatusOr<ProbabilisticRevealToken> Randomize(size_t i) const;
+  base::expected<ProbabilisticRevealToken, absl::Status> Randomize(
+      size_t i) const;
 
  private:
   // Private since it assumes all arguments are valid. Static `Create()`
diff --git a/components/ip_protection/common/ip_protection_probabilistic_reveal_token_crypter_fuzztests.cc b/components/ip_protection/common/ip_protection_probabilistic_reveal_token_crypter_fuzztests.cc
index e3a0d4d3f..c042d1ad 100644
--- a/components/ip_protection/common/ip_protection_probabilistic_reveal_token_crypter_fuzztests.cc
+++ b/components/ip_protection/common/ip_protection_probabilistic_reveal_token_crypter_fuzztests.cc
@@ -61,12 +61,14 @@
   ASSERT_THAT(issuer->Tokens(), testing::SizeIs(1));
   const ip_protection::ProbabilisticRevealToken& token = issuer->Tokens()[0];
 
-  absl::StatusOr<std::unique_ptr<
-      ip_protection::IpProtectionProbabilisticRevealTokenCrypter>>
+  base::expected<
+      std::unique_ptr<
+          ip_protection::IpProtectionProbabilisticRevealTokenCrypter>,
+      absl::Status>
       maybe_crypter =
           ip_protection::IpProtectionProbabilisticRevealTokenCrypter::Create(
               issuer->GetSerializedPublicKey(), {token});
-  ASSERT_TRUE(maybe_crypter.ok());
+  ASSERT_TRUE(maybe_crypter.has_value());
   auto& crypter = maybe_crypter.value();
   ASSERT_TRUE(crypter->IsTokenAvailable());
   ASSERT_EQ(crypter->NumTokens(), std::size_t(1));
diff --git a/components/ip_protection/common/ip_protection_probabilistic_reveal_token_crypter_unittest.cc b/components/ip_protection/common/ip_protection_probabilistic_reveal_token_crypter_unittest.cc
index 1a511cd5..e36ad1a 100644
--- a/components/ip_protection/common/ip_protection_probabilistic_reveal_token_crypter_unittest.cc
+++ b/components/ip_protection/common/ip_protection_probabilistic_reveal_token_crypter_unittest.cc
@@ -64,9 +64,11 @@
   };
   CreateTokens(/*private_key=*/12345, plaintexts);
 
-  auto maybe_crypter = IpProtectionProbabilisticRevealTokenCrypter::Create(
-      GetPublicKey(), GetTokens());
-  EXPECT_TRUE(maybe_crypter.ok());
+  base::expected<std::unique_ptr<IpProtectionProbabilisticRevealTokenCrypter>,
+                 absl::Status>
+      maybe_crypter = IpProtectionProbabilisticRevealTokenCrypter::Create(
+          GetPublicKey(), GetTokens());
+  EXPECT_TRUE(maybe_crypter.has_value());
   const auto& crypter = maybe_crypter.value();
   EXPECT_TRUE(crypter->IsTokenAvailable());
   EXPECT_EQ(crypter->NumTokens(), plaintexts.size());
@@ -80,9 +82,11 @@
   };
   CreateTokens(/*private_key=*/12345, plaintexts);
 
-  auto maybe_crypter =
-      IpProtectionProbabilisticRevealTokenCrypter::Create(GetPublicKey(), {});
-  EXPECT_TRUE(maybe_crypter.ok());
+  base::expected<std::unique_ptr<IpProtectionProbabilisticRevealTokenCrypter>,
+                 absl::Status>
+      maybe_crypter = IpProtectionProbabilisticRevealTokenCrypter::Create(
+          GetPublicKey(), {});
+  EXPECT_TRUE(maybe_crypter.has_value());
   const auto& crypter = maybe_crypter.value();
   EXPECT_FALSE(crypter->IsTokenAvailable());
   EXPECT_EQ(crypter->NumTokens(), std::size_t(0));
@@ -91,9 +95,11 @@
 TEST_F(IpProtectionProbabilisticRevealTokenCrypterTest,
        CreateInvalidPublicKey) {
   const auto& serialized_public_key = "invalid-public-key";
-  auto maybe_crypter = IpProtectionProbabilisticRevealTokenCrypter::Create(
-      serialized_public_key, {});
-  EXPECT_EQ(maybe_crypter.status().code(), absl::StatusCode::kInvalidArgument);
+  base::expected<std::unique_ptr<IpProtectionProbabilisticRevealTokenCrypter>,
+                 absl::Status>
+      maybe_crypter = IpProtectionProbabilisticRevealTokenCrypter::Create(
+          serialized_public_key, {});
+  EXPECT_EQ(maybe_crypter.error().code(), absl::StatusCode::kInvalidArgument);
 }
 
 TEST_F(IpProtectionProbabilisticRevealTokenCrypterTest, CreateInvalidTokenU) {
@@ -106,9 +112,11 @@
   std::vector<ProbabilisticRevealToken> tokens = GetTokens();
   tokens.back().u = "invalid-u";
 
-  auto maybe_crypter = IpProtectionProbabilisticRevealTokenCrypter::Create(
-      GetPublicKey(), tokens);
-  EXPECT_EQ(maybe_crypter.status().code(), absl::StatusCode::kInvalidArgument);
+  base::expected<std::unique_ptr<IpProtectionProbabilisticRevealTokenCrypter>,
+                 absl::Status>
+      maybe_crypter = IpProtectionProbabilisticRevealTokenCrypter::Create(
+          GetPublicKey(), tokens);
+  EXPECT_EQ(maybe_crypter.error().code(), absl::StatusCode::kInvalidArgument);
 }
 
 TEST_F(IpProtectionProbabilisticRevealTokenCrypterTest, CreateInvalidTokenE) {
@@ -121,9 +129,11 @@
   std::vector<ProbabilisticRevealToken> tokens = GetTokens();
   tokens.back().e = "invalid-e";
 
-  auto maybe_crypter = IpProtectionProbabilisticRevealTokenCrypter::Create(
-      GetPublicKey(), tokens);
-  EXPECT_EQ(maybe_crypter.status().code(), absl::StatusCode::kInvalidArgument);
+  base::expected<std::unique_ptr<IpProtectionProbabilisticRevealTokenCrypter>,
+                 absl::Status>
+      maybe_crypter = IpProtectionProbabilisticRevealTokenCrypter::Create(
+          GetPublicKey(), tokens);
+  EXPECT_EQ(maybe_crypter.error().code(), absl::StatusCode::kInvalidArgument);
 }
 
 TEST_F(IpProtectionProbabilisticRevealTokenCrypterTest,
@@ -136,9 +146,11 @@
   CreateTokens(/*private_key=*/11111, plaintexts);
 
   // Create a crypter with no tokens.
-  auto maybe_crypter =
-      IpProtectionProbabilisticRevealTokenCrypter::Create(GetPublicKey(), {});
-  EXPECT_TRUE(maybe_crypter.ok());
+  base::expected<std::unique_ptr<IpProtectionProbabilisticRevealTokenCrypter>,
+                 absl::Status>
+      maybe_crypter = IpProtectionProbabilisticRevealTokenCrypter::Create(
+          GetPublicKey(), {});
+  EXPECT_TRUE(maybe_crypter.has_value());
   auto& crypter = maybe_crypter.value();
   EXPECT_FALSE(crypter->IsTokenAvailable());
   EXPECT_EQ(crypter->NumTokens(), std::size_t(0));
@@ -161,9 +173,11 @@
       "------------Code never lies, ", "comments sometimes do.-------",
   };
   CreateTokens(/*private_key=*/11111, plaintexts);
-  auto maybe_crypter =
-      IpProtectionProbabilisticRevealTokenCrypter::Create(GetPublicKey(), {});
-  EXPECT_TRUE(maybe_crypter.ok());
+  base::expected<std::unique_ptr<IpProtectionProbabilisticRevealTokenCrypter>,
+                 absl::Status>
+      maybe_crypter = IpProtectionProbabilisticRevealTokenCrypter::Create(
+          GetPublicKey(), {});
+  EXPECT_TRUE(maybe_crypter.has_value());
   auto& crypter = maybe_crypter.value();
   EXPECT_FALSE(crypter->IsTokenAvailable());
   EXPECT_EQ(crypter->NumTokens(), std::size_t(0));
@@ -185,9 +199,11 @@
   };
   CreateTokens(/*private_key=*/42, plaintexts);
 
-  auto maybe_crypter = IpProtectionProbabilisticRevealTokenCrypter::Create(
-      GetPublicKey(), GetTokens());
-  EXPECT_TRUE(maybe_crypter.ok());
+  base::expected<std::unique_ptr<IpProtectionProbabilisticRevealTokenCrypter>,
+                 absl::Status>
+      maybe_crypter = IpProtectionProbabilisticRevealTokenCrypter::Create(
+          GetPublicKey(), GetTokens());
+  EXPECT_TRUE(maybe_crypter.has_value());
   auto& crypter = maybe_crypter.value();
   EXPECT_TRUE(crypter->IsTokenAvailable());
   EXPECT_EQ(crypter->NumTokens(), plaintexts.size());
@@ -208,9 +224,11 @@
   };
   CreateTokens(/*private_key=*/42, plaintexts);
 
-  auto maybe_crypter = IpProtectionProbabilisticRevealTokenCrypter::Create(
-      GetPublicKey(), GetTokens());
-  EXPECT_TRUE(maybe_crypter.ok());
+  base::expected<std::unique_ptr<IpProtectionProbabilisticRevealTokenCrypter>,
+                 absl::Status>
+      maybe_crypter = IpProtectionProbabilisticRevealTokenCrypter::Create(
+          GetPublicKey(), GetTokens());
+  EXPECT_TRUE(maybe_crypter.has_value());
   auto& crypter = maybe_crypter.value();
   EXPECT_TRUE(crypter->IsTokenAvailable());
   EXPECT_EQ(crypter->NumTokens(), plaintexts.size());
@@ -239,9 +257,11 @@
   };
   CreateTokens(/*private_key=*/42, plaintexts);
 
-  auto maybe_crypter = IpProtectionProbabilisticRevealTokenCrypter::Create(
-      GetPublicKey(), GetTokens());
-  EXPECT_TRUE(maybe_crypter.ok());
+  base::expected<std::unique_ptr<IpProtectionProbabilisticRevealTokenCrypter>,
+                 absl::Status>
+      maybe_crypter = IpProtectionProbabilisticRevealTokenCrypter::Create(
+          GetPublicKey(), GetTokens());
+  EXPECT_TRUE(maybe_crypter.has_value());
   auto& crypter = maybe_crypter.value();
   EXPECT_TRUE(crypter->IsTokenAvailable());
   EXPECT_EQ(crypter->NumTokens(), plaintexts.size());
@@ -268,9 +288,11 @@
       "------------Code never lies, ", "comments sometimes do.-------",
   };
   CreateTokens(/*private_key=*/7654, plaintexts);
-  auto maybe_crypter = IpProtectionProbabilisticRevealTokenCrypter::Create(
-      GetPublicKey(), GetTokens());
-  EXPECT_TRUE(maybe_crypter.ok());
+  base::expected<std::unique_ptr<IpProtectionProbabilisticRevealTokenCrypter>,
+                 absl::Status>
+      maybe_crypter = IpProtectionProbabilisticRevealTokenCrypter::Create(
+          GetPublicKey(), GetTokens());
+  EXPECT_TRUE(maybe_crypter.has_value());
   auto& crypter = maybe_crypter.value();
   EXPECT_TRUE(crypter->IsTokenAvailable());
   EXPECT_EQ(crypter->NumTokens(), plaintexts.size());
@@ -290,18 +312,20 @@
   };
   CreateTokens(/*private_key=*/2025, plaintexts);
 
-  const auto maybe_crypter =
-      IpProtectionProbabilisticRevealTokenCrypter::Create(GetPublicKey(),
-                                                          GetTokens());
-  EXPECT_TRUE(maybe_crypter.ok());
+  base::expected<std::unique_ptr<IpProtectionProbabilisticRevealTokenCrypter>,
+                 absl::Status>
+      maybe_crypter = IpProtectionProbabilisticRevealTokenCrypter::Create(
+          GetPublicKey(), GetTokens());
+  EXPECT_TRUE(maybe_crypter.has_value());
   const auto& crypter = maybe_crypter.value();
   EXPECT_TRUE(crypter->IsTokenAvailable());
   EXPECT_EQ(crypter->NumTokens(), plaintexts.size());
 
   // Decrypting randomized tokens should yield starting plaintexts.
   for (std::size_t i = 0; i < plaintexts.size(); ++i) {
-    const auto maybe_randomized_token = crypter->Randomize(i);
-    ASSERT_TRUE(maybe_randomized_token.ok());
+    const base::expected<ProbabilisticRevealToken, absl::Status>
+        maybe_randomized_token = crypter->Randomize(i);
+    ASSERT_TRUE(maybe_randomized_token.has_value());
     const auto& randomized_token = maybe_randomized_token.value();
 
     EXPECT_EQ(randomized_token.version, 1);
@@ -314,9 +338,10 @@
   }
 
   // Try to randomize a token that does not exist.
-  const auto maybe_token = crypter->Randomize(plaintexts.size());
-  ASSERT_FALSE(maybe_token.ok());
-  EXPECT_EQ(maybe_token.status().code(), absl::StatusCode::kInvalidArgument);
+  const base::expected<ProbabilisticRevealToken, absl::Status> maybe_token =
+      crypter->Randomize(plaintexts.size());
+  ASSERT_FALSE(maybe_token.has_value());
+  EXPECT_EQ(maybe_token.error().code(), absl::StatusCode::kInvalidArgument);
 }
 
 }  // namespace ip_protection
diff --git a/components/ip_protection/common/ip_protection_probabilistic_reveal_token_direct_fetcher.cc b/components/ip_protection/common/ip_protection_probabilistic_reveal_token_direct_fetcher.cc
index 4ea23701..51fe17d 100644
--- a/components/ip_protection/common/ip_protection_probabilistic_reveal_token_direct_fetcher.cc
+++ b/components/ip_protection/common/ip_protection_probabilistic_reveal_token_direct_fetcher.cc
@@ -328,9 +328,12 @@
       response.num_tokens_with_signal() > response.tokens_size()) {
     return TryGetProbabilisticRevealTokensStatus::kInvalidNumTokensWithSignal;
   }
-  if (auto crypter = IpProtectionProbabilisticRevealTokenCrypter::Create(
-          response.public_key().y(), {});
-      !crypter.ok()) {
+  if (base::expected<
+          std::unique_ptr<IpProtectionProbabilisticRevealTokenCrypter>,
+          absl::Status> crypter =
+          IpProtectionProbabilisticRevealTokenCrypter::Create(
+              response.public_key().y(), {});
+      !crypter.has_value()) {
     return TryGetProbabilisticRevealTokensStatus::kInvalidPublicKey;
   }
   if (response.epoch_id().size() != kEpochIdSize) {
diff --git a/components/ip_protection/common/ip_protection_probabilistic_reveal_token_manager.cc b/components/ip_protection/common/ip_protection_probabilistic_reveal_token_manager.cc
index 82dcae38..0bbb808 100644
--- a/components/ip_protection/common/ip_protection_probabilistic_reveal_token_manager.cc
+++ b/components/ip_protection/common/ip_protection_probabilistic_reveal_token_manager.cc
@@ -140,9 +140,11 @@
     return;
   }
   DCHECK(outcome.has_value());
-  auto maybe_crypter = IpProtectionProbabilisticRevealTokenCrypter::Create(
-      outcome.value().public_key, outcome.value().tokens);
-  if (!maybe_crypter.ok()) {
+  base::expected<std::unique_ptr<IpProtectionProbabilisticRevealTokenCrypter>,
+                 absl::Status>
+      maybe_crypter = IpProtectionProbabilisticRevealTokenCrypter::Create(
+          outcome.value().public_key, outcome.value().tokens);
+  if (!maybe_crypter.has_value()) {
     // Might happen if PRT issuer is misconfigured and public_key or tokens do
     // not belong to the group. Return without clearing existing tokens, which
     // might be still valid for current epoch. Retry in an hour.
@@ -223,9 +225,9 @@
 
     // Seeing this third party for the first time in this top level.
     // Randomize top level's token and return.
-    absl::StatusOr<ProbabilisticRevealToken> maybe_randomized_token =
-        crypter_->Randomize(token_index);
-    if (!maybe_randomized_token.ok()) {
+    base::expected<ProbabilisticRevealToken, absl::Status>
+        maybe_randomized_token = crypter_->Randomize(token_index);
+    if (!maybe_randomized_token.has_value()) {
       // Should not happen in theory, might happen with corrupted crypter.
       return std::nullopt;
     }
@@ -234,9 +236,9 @@
   }
   // Seeing first party for the first time.
   std::size_t token_selected = base::RandGenerator(crypter_->NumTokens());
-  absl::StatusOr<ProbabilisticRevealToken> maybe_randomized_token =
-      crypter_->Randomize(token_selected);
-  if (!maybe_randomized_token.ok()) {
+  base::expected<ProbabilisticRevealToken, absl::Status>
+      maybe_randomized_token = crypter_->Randomize(token_selected);
+  if (!maybe_randomized_token.has_value()) {
     // Should not happen in theory, might happen with corrupted crypter.
     return std::nullopt;
   }
diff --git a/components/keyed_service/core/keyed_service_export.h b/components/keyed_service/core/keyed_service_export.h
index 86482be..5edd1c7 100644
--- a/components/keyed_service/core/keyed_service_export.h
+++ b/components/keyed_service/core/keyed_service_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(KEYED_SERVICE_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(KEYED_SERVICE_IMPLEMENTATION)
 #define KEYED_SERVICE_EXPORT __attribute__((visibility("default")))
-#else
-#define KEYED_SERVICE_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/components/login/login_export.h b/components/login/login_export.h
index 35af257c..2203d3c 100644
--- a/components/login/login_export.h
+++ b/components/login/login_export.h
@@ -16,19 +16,11 @@
 #endif  // defined(LOGIN_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-
-#if defined(LOGIN_IMPLEMENTATION)
 #define LOGIN_EXPORT __attribute__((visibility("default")))
-#else
-#define LOGIN_EXPORT
-#endif  // defined(LOGIN_IMPLEMENTATION)
-
 #endif  // defined(WIN32)
 
 #else  // defined(COMPONENT_BUILD)
-
 #define LOGIN_EXPORT
-
 #endif
 
 #endif  // COMPONENTS_LOGIN_LOGIN_EXPORT_H_
diff --git a/components/metal_util/metal_util_export.h b/components/metal_util/metal_util_export.h
index 2973ef0..e027220 100644
--- a/components/metal_util/metal_util_export.h
+++ b/components/metal_util/metal_util_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(METAL_UTIL_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(METAL_UTIL_IMPLEMENTATION)
 #define METAL_UTIL_EXPORT __attribute__((visibility("default")))
-#else
-#define METAL_UTIL_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/components/omnibox/browser/BUILD.gn b/components/omnibox/browser/BUILD.gn
index 2c2c4594..8312adb 100644
--- a/components/omnibox/browser/BUILD.gn
+++ b/components/omnibox/browser/BUILD.gn
@@ -414,6 +414,7 @@
     deps += [
       ":jni_headers",
       "//components/browser_ui/util/android",
+      "//components/saved_tab_groups/public:conversion_utils",
       "//url",
     ]
   } else {
@@ -620,6 +621,7 @@
       "//components/cached_flags:java",
       "//components/embedder_support/android:util_java",
       "//components/omnibox/common:features_java",
+      "//components/saved_tab_groups/public:java",
       "//components/security_state/core:security_state_enums_java",
       "//third_party/android_deps:protobuf_lite_runtime_java",
       "//third_party/androidx:androidx_annotation_annotation_java",
diff --git a/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/AutocompleteMatch.java b/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/AutocompleteMatch.java
index 209faca..fa7dd21 100644
--- a/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/AutocompleteMatch.java
+++ b/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/AutocompleteMatch.java
@@ -91,6 +91,7 @@
     private final boolean mAllowedToBeDefaultMatch;
     private final String mInlineAutocompletion;
     private final String mAdditionalText;
+    private @Nullable String mTabGroupUuid;
 
     public AutocompleteMatch(
             int nativeType,
@@ -116,7 +117,8 @@
             @Nullable List<OmniboxAction> actions,
             boolean allowedToBeDefaultMatch,
             String inlineAutocompletion,
-            String additionalText) {
+            String additionalText,
+            @Nullable String tabGroupUuid) {
         if (subtypes == null) {
             subtypes = Collections.emptySet();
         }
@@ -152,6 +154,7 @@
         mAllowedToBeDefaultMatch = allowedToBeDefaultMatch;
         mInlineAutocompletion = inlineAutocompletion;
         mAdditionalText = additionalText;
+        mTabGroupUuid = tabGroupUuid;
     }
 
     @CalledByNative
@@ -182,7 +185,8 @@
             @JniType("std::vector") List<OmniboxAction> actions,
             boolean allowedToBeDefaultMatch,
             String inlineAutocompletion,
-            String additionalText) {
+            String additionalText,
+            String localTabGroupId) {
         assert contentClassificationOffsets.length == contentClassificationStyles.length;
         List<MatchClassification> contentClassifications = new ArrayList<>();
         for (int i = 0; i < contentClassificationOffsets.length; i++) {
@@ -221,7 +225,8 @@
                         actions,
                         allowedToBeDefaultMatch,
                         inlineAutocompletion,
-                        additionalText);
+                        additionalText,
+                        TextUtils.isEmpty(localTabGroupId) ? null : localTabGroupId);
         match.updateNativeObjectRef(nativeObject);
         match.setDescription(
                 description, descriptionClassificationOffsets, descriptionClassificationStyles);
@@ -450,7 +455,8 @@
                 && Arrays.equals(mPostData, suggestion.mPostData)
                 && mGroupId == suggestion.mGroupId
                 && mAnswerType == suggestion.mAnswerType
-                && answer_template_is_equal;
+                && answer_template_is_equal
+                && ObjectsCompat.equals(mTabGroupUuid, suggestion.mTabGroupUuid);
     }
 
     /**
@@ -462,6 +468,10 @@
         return mGroupId;
     }
 
+    public @Nullable String getTabGroupUuid() {
+        return mTabGroupUuid;
+    }
+
     /**
      * Retrieve the clipboard information and update this instance of AutocompleteMatch. Will
      * terminate immediately if the native counterpart of the AutocompleteMatch object does not
@@ -567,7 +577,8 @@
                 /* actions= */ null,
                 input.getAllowedToBeDefaultMatch(),
                 input.getInlineAutocompletion(),
-                input.getAdditionalText());
+                input.getAdditionalText(),
+                /* tabGroupUuid= */ null);
     }
 
     @Override
diff --git a/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/AutocompleteMatchBuilder.java b/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/AutocompleteMatchBuilder.java
index 4b8eae5..3b705c8 100644
--- a/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/AutocompleteMatchBuilder.java
+++ b/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/AutocompleteMatchBuilder.java
@@ -47,6 +47,7 @@
     private boolean mAllowedToBeDefaultMatch;
     private String mInlineAutocompletion;
     private String mAdditionalText;
+    private String mTabGroupUuid;
 
     /**
      * Create a suggestion builder for a search suggestion.
@@ -97,6 +98,7 @@
         mAllowedToBeDefaultMatch = false;
         mInlineAutocompletion = null;
         mAdditionalText = null;
+        mTabGroupUuid = null;
 
         mDisplayTextClassifications.add(
                 new AutocompleteMatch.MatchClassification(0, MatchClassificationStyle.NONE));
@@ -135,7 +137,8 @@
                 mActions,
                 mAllowedToBeDefaultMatch,
                 mInlineAutocompletion,
-                mAdditionalText);
+                mAdditionalText,
+                mTabGroupUuid);
     }
 
     /**
@@ -328,4 +331,13 @@
         mSerializedAnswerTemplate = serializedAnswerTemplate;
         return this;
     }
+
+    /**
+     * @param tabGroupUuid Matching tab group's uuid.
+     * @return Omnibox suggestion builder.
+     */
+    public AutocompleteMatchBuilder setTabGroupUuid(String tabGroupUuid) {
+        mTabGroupUuid = tabGroupUuid;
+        return this;
+    }
 }
diff --git a/components/omnibox/browser/autocomplete_match.cc b/components/omnibox/browser/autocomplete_match.cc
index c1d6e8ea..7fe68a76a 100644
--- a/components/omnibox/browser/autocomplete_match.cc
+++ b/components/omnibox/browser/autocomplete_match.cc
@@ -305,7 +305,8 @@
           match.history_embeddings_answer_header_text),
       history_embeddings_answer_header_loading(
           match.history_embeddings_answer_header_loading),
-      feedback_type(match.feedback_type) {}
+      feedback_type(match.feedback_type),
+      matching_tab_group_uuid(match.matching_tab_group_uuid) {}
 
 AutocompleteMatch::AutocompleteMatch(AutocompleteMatch&& match) noexcept {
   *this = std::move(match);
@@ -387,6 +388,7 @@
   history_embeddings_answer_header_loading =
       std::move(match.history_embeddings_answer_header_loading);
   feedback_type = std::move(match.feedback_type);
+  matching_tab_group_uuid = std::move(match.matching_tab_group_uuid);
 #if BUILDFLAG(IS_ANDROID)
   DestroyJavaObject();
   std::swap(java_match_, match.java_match_);
@@ -482,6 +484,7 @@
   history_embeddings_answer_header_loading =
       match.history_embeddings_answer_header_loading;
   feedback_type = match.feedback_type;
+  matching_tab_group_uuid = match.matching_tab_group_uuid;
 
 #if BUILDFLAG(IS_ANDROID)
   // In case the target element previously held a java object, release it.
diff --git a/components/omnibox/browser/autocomplete_match.h b/components/omnibox/browser/autocomplete_match.h
index 2b7690b4b..b0925ae 100644
--- a/components/omnibox/browser/autocomplete_match.h
+++ b/components/omnibox/browser/autocomplete_match.h
@@ -20,12 +20,14 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/strings/utf_offset_string_conversions.h"
+#include "base/uuid.h"
 #include "build/build_config.h"
 #include "components/omnibox/browser/actions/omnibox_action_concepts.h"
 #include "components/omnibox/browser/autocomplete_input.h"
 #include "components/omnibox/browser/autocomplete_match_type.h"
 #include "components/omnibox/browser/buildflags.h"
 #include "components/omnibox/browser/suggestion_answer.h"
+#include "components/saved_tab_groups/public/types.h"
 #include "components/search_engines/template_url.h"
 #include "components/url_formatter/url_formatter.h"
 #include "third_party/metrics_proto/omnibox_event.pb.h"
@@ -1020,6 +1022,9 @@
   // The user feedback on the match.
   FeedbackType feedback_type = FeedbackType::kNone;
 
+  // Stores the matching tab group uuid for this suggestion.
+  std::optional<base::Uuid> matching_tab_group_uuid = std::nullopt;
+
   // So users of AutocompleteMatch can use the same ellipsis that it uses.
   static const char16_t kEllipsis[];
 
diff --git a/components/omnibox/browser/autocomplete_match_android.cc b/components/omnibox/browser/autocomplete_match_android.cc
index 8452c2f09..5b5db68 100644
--- a/components/omnibox/browser/autocomplete_match_android.cc
+++ b/components/omnibox/browser/autocomplete_match_android.cc
@@ -11,11 +11,14 @@
 #include "base/android/jni_array.h"
 #include "base/android/jni_string.h"
 #include "base/functional/bind.h"
+#include "base/uuid.h"
 #include "components/omnibox/browser/actions/omnibox_action.h"
 #include "components/omnibox/browser/actions/omnibox_action_factory_android.h"
 #include "components/omnibox/browser/clipboard_provider.h"
 #include "components/omnibox/browser/search_suggestion_parser.h"
 #include "components/omnibox/common/omnibox_feature_configs.h"
+#include "components/saved_tab_groups/public/android/tab_group_sync_conversions_bridge.h"
+#include "components/saved_tab_groups/public/android/tab_group_sync_conversions_utils.h"
 #include "url/android/gurl_android.h"
 
 // Must come after all headers that specialize FromJniType() / ToJniType().
@@ -111,7 +114,9 @@
           has_tab_match.value_or(false), actions_list,
           allowed_to_be_default_match,
           ConvertUTF16ToJavaString(env, inline_autocompletion),
-          ConvertUTF16ToJavaString(env, additional_text)));
+          ConvertUTF16ToJavaString(env, additional_text),
+          tab_groups::UuidToJavaString(
+              env, matching_tab_group_uuid.value_or(base::Uuid()))));
 
   return ScopedJavaLocalRef<jobject>(*java_match_);
 }
diff --git a/components/omnibox/browser/tab_group_provider.cc b/components/omnibox/browser/tab_group_provider.cc
index 0543f91..9e3f0b8 100644
--- a/components/omnibox/browser/tab_group_provider.cc
+++ b/components/omnibox/browser/tab_group_provider.cc
@@ -125,7 +125,7 @@
   match.contents_class = ClassifyTermMatches(
       contents_terms, match.contents.size(), ACMatchClassification::MATCH,
       ACMatchClassification::NONE);
-  // TODO(crbug.com/410574178): Plumb tab group id through match.
+  match.matching_tab_group_uuid = group.saved_guid();
   match.suggestion_group_id = omnibox::GROUP_MOBILE_OPEN_TABS;
 
   return match;
diff --git a/components/omnibox/browser/tab_group_provider_unittest.cc b/components/omnibox/browser/tab_group_provider_unittest.cc
index 61a67fb5..3fd6845 100644
--- a/components/omnibox/browser/tab_group_provider_unittest.cc
+++ b/components/omnibox/browser/tab_group_provider_unittest.cc
@@ -73,4 +73,7 @@
                           TestSchemeClassifier());
   tab_group_provider().Start(input, /* minimal_changes= */ false);
   ASSERT_EQ(1UL, tab_group_provider().matches().size());
+  ASSERT_EQ(u"test", tab_group_provider().matches()[0].contents);
+  ASSERT_TRUE(
+      tab_group_provider().matches()[0].matching_tab_group_uuid.has_value());
 }
diff --git a/components/optimization_guide/core/model_execution/on_device_context.cc b/components/optimization_guide/core/model_execution/on_device_context.cc
index f4e4fe8c..b350bb8 100644
--- a/components/optimization_guide/core/model_execution/on_device_context.cc
+++ b/components/optimization_guide/core/model_execution/on_device_context.cc
@@ -211,7 +211,6 @@
   // rather than max_tokens for this call.
   options->max_tokens =
       opts_.token_limits.max_context_tokens - tokens_processed_;
-  options->token_offset = 0;
   mojo::PendingRemote<on_device_model::mojom::ContextClient> pending;
   clients_.Add(this, pending.InitWithNewPipeAndPassReceiver());
   session_->Append(std::move(options), std::move(pending));
diff --git a/components/optimization_guide/core/model_execution/on_device_model_service_controller_unittest.cc b/components/optimization_guide/core/model_execution/on_device_model_service_controller_unittest.cc
index 75d3d51..c4f8f14 100644
--- a/components/optimization_guide/core/model_execution/on_device_model_service_controller_unittest.cc
+++ b/components/optimization_guide/core/model_execution/on_device_model_service_controller_unittest.cc
@@ -332,7 +332,7 @@
   session->ExecuteModel(PageUrlRequest("foo"),
                         response_.GetStreamingCallback());
   ASSERT_TRUE(response_.GetFinalStatus());
-  const std::string expected_response = "Context: execute:foo off:0 max:1024\n";
+  const std::string expected_response = "Context: execute:foo max:1024\n";
   EXPECT_EQ(*response_.value(), expected_response);
   EXPECT_TRUE(*response_.provided_by_on_device());
   EXPECT_THAT(response_.partials(), ElementsAre(expected_response));
@@ -447,7 +447,7 @@
                         response_.GetStreamingCallback());
   ASSERT_TRUE(response_.GetFinalStatus());
   EXPECT_EQ(*response_.value(),
-            "Adaptation model: 1015\nContext: execute:foo off:0 max:1024\n");
+            "Adaptation model: 1015\nContext: execute:foo max:1024\n");
 
   // If we destroy all sessions and wait long enough, everything should idle out
   // and the service should get terminated.
@@ -500,11 +500,11 @@
 
   ASSERT_TRUE(compose_response.GetFinalStatus());
   EXPECT_EQ(*compose_response.value(),
-            "Adaptation model: 1015\nContext: execute:foo off:0 max:1024\n");
+            "Adaptation model: 1015\nContext: execute:foo max:1024\n");
   EXPECT_TRUE(*compose_response.provided_by_on_device());
   ASSERT_TRUE(test_response.GetFinalStatus());
   EXPECT_EQ(*test_response.value(),
-            "Adaptation model: 2024\nContext: execute:bar off:0 max:1024\n");
+            "Adaptation model: 2024\nContext: execute:bar max:1024\n");
   EXPECT_TRUE(*test_response.provided_by_on_device());
 
   session_compose.reset();
@@ -559,10 +559,10 @@
 
   ASSERT_TRUE(compose_response.GetFinalStatus());
   EXPECT_EQ(*compose_response.value(),
-            "Adaptation model: 1015\nContext: execute:foo off:0 max:1024\n");
+            "Adaptation model: 1015\nContext: execute:foo max:1024\n");
   EXPECT_TRUE(*compose_response.provided_by_on_device());
   ASSERT_TRUE(test_response.GetFinalStatus());
-  EXPECT_EQ(*test_response.value(), "Context: execute:bar off:0 max:1024\n");
+  EXPECT_EQ(*test_response.value(), "Context: execute:bar max:1024\n");
   EXPECT_TRUE(*test_response.provided_by_on_device());
 
   session_compose.reset();
@@ -687,7 +687,7 @@
                          response2.GetStreamingCallback());
   ASSERT_TRUE(response2.GetFinalStatus());
   EXPECT_EQ(*response2.value(),
-            "Base model: 2\nContext: execute:foo off:0 max:1024\n");
+            "Base model: 2\nContext: execute:foo max:1024\n");
 }
 
 TEST_F(OnDeviceModelServiceControllerTest, SessionFailsForInvalidFeature) {
@@ -1603,7 +1603,7 @@
   EXPECT_EQ(
       *resp1.error(),
       OptimizationGuideModelExecutionError::ModelExecutionError::kCancelled);
-  EXPECT_EQ(*resp2.value(), "Context: execute:bar off:0 max:1024\n");
+  EXPECT_EQ(*resp2.value(), "Context: execute:bar max:1024\n");
 }
 
 TEST_F(OnDeviceModelServiceControllerTest, WontStartSessionAfterGpuBlocked) {
@@ -1789,8 +1789,8 @@
       "OptimizationGuide.ModelExecution.OnDeviceExecuteModelResult.Compose",
       ExecuteModelResult::kUsedOnDevice, 1);
   std::string expected_response =
-      ("Context: ctx:foo off:0 max:8192\n"
-       "Context: execute:foobaz off:0 max:1024\n");
+      ("Context: ctx:foo max:8192\n"
+       "Context: execute:foobaz max:1024\n");
   EXPECT_EQ(*response_.value(), expected_response);
 }
 
@@ -1828,16 +1828,16 @@
   session2->ExecuteModel(PageUrlRequest("2"), response_.GetStreamingCallback());
   ASSERT_TRUE(response_.GetFinalStatus());
   std::string expected_response1 =
-      ("Context: ctx:bar off:0 max:8192\n"
-       "Context: execute:bar2 off:0 max:1024\n");
+      ("Context: ctx:bar max:8192\n"
+       "Context: execute:bar2 max:1024\n");
   EXPECT_EQ(*response_.value(), expected_response1);
 
   ResponseHolder response2;
   session1->ExecuteModel(PageUrlRequest("1"), response2.GetStreamingCallback());
   ASSERT_TRUE(response2.GetFinalStatus());
   std::string expected_response2 =
-      ("Context: ctx:foo off:0 max:8192\n"
-       "Context: execute:foo1 off:0 max:1024\n");
+      ("Context: ctx:foo max:8192\n"
+       "Context: execute:foo1 max:1024\n");
   EXPECT_EQ(*response2.value(), expected_response2);
 }
 
@@ -2044,8 +2044,7 @@
   session1->ExecuteModel(UserInputRequest("foo"),
                          response_.GetStreamingCallback());
   task_environment_.RunUntilIdle();
-  const std::string expected_response1 =
-      "Context: execute:foo off:0 max:1024\n";
+  const std::string expected_response1 = "Context: execute:foo max:1024\n";
   EXPECT_EQ(*response_.value(), expected_response1);
   EXPECT_THAT(response_.partials(), IsEmpty());
 
@@ -2056,20 +2055,19 @@
   session2->ExecuteModel(UserInputRequest("abarx"),
                          response2.GetStreamingCallback());
   task_environment_.RunUntilIdle();
-  const std::string expected_response2 =
-      "Context: execute:abarx off:0 max:1024\n";
+  const std::string expected_response2 = "Context: execute:abarx max:1024\n";
   EXPECT_EQ(*response2.value(), expected_response2);
   EXPECT_THAT(response2.partials(), IsEmpty());
 
   // Output contains redacted text (and  input doesn't), so redact.
-  fake_settings_.set_execute_result({"Context: abarx off:0 max:1024\n"});
+  fake_settings_.set_execute_result({"Context: abarx max:1024\n"});
   auto session3 = CreateSession();
   ASSERT_TRUE(session3);
   ResponseHolder response3;
   session3->ExecuteModel(UserInputRequest("foo"),
                          response3.GetStreamingCallback());
   task_environment_.RunUntilIdle();
-  const std::string expected_response3 = "Context: a[###]x off:0 max:1024\n";
+  const std::string expected_response3 = "Context: a[###]x max:1024\n";
   EXPECT_EQ(*response3.value(), expected_response3);
   EXPECT_THAT(response3.partials(), IsEmpty());
 }
@@ -2130,7 +2128,7 @@
   });
 
   // Force 'bar' to be returned from model.
-  fake_settings_.set_execute_result({"Context: bar off:0 max:1024\n"});
+  fake_settings_.set_execute_result({"Context: bar max:1024\n"});
 
   auto session = CreateSession();
   ASSERT_TRUE(session);
@@ -2139,7 +2137,7 @@
                         response_.GetStreamingCallback());
   task_environment_.RunUntilIdle();
   // `bar` shouldn't be rewritten as it's in the input.
-  const std::string expected_response = "Context: bar off:0 max:1024\n";
+  const std::string expected_response = "Context: bar max:1024\n";
   EXPECT_EQ(*response_.value(), expected_response);
   EXPECT_THAT(response_.partials(), IsEmpty());
 }
@@ -2156,14 +2154,13 @@
   });
 
   // Output contains redacted text (and  input doesn't), so redact.
-  fake_settings_.set_execute_result({"Context: abarx off:0 max:1024\n"});
+  fake_settings_.set_execute_result({"Context: abarx max:1024\n"});
   auto session = CreateSession();
   ASSERT_TRUE(session);
   session->ExecuteModel(UserInputRequest("foo"),
                         response_.GetStreamingCallback());
   task_environment_.RunUntilIdle();
-  const std::string expected_response =
-      "Context: a[redacted]x off:0 max:1024\n";
+  const std::string expected_response = "Context: a[redacted]x max:1024\n";
   EXPECT_EQ(*response_.value(), expected_response);
   EXPECT_THAT(response_.partials(), IsEmpty());
 }
@@ -2415,7 +2412,7 @@
   task_environment_.RunUntilIdle();
   EXPECT_TRUE(response_.value());
   const std::vector<std::string> partial_responses = {
-      "Context: execute:foo off:0 max:1024\n",
+      "Context: execute:foo max:1024\n",
   };
   EXPECT_EQ(*response_.value(), ConcatResponses(partial_responses));
   EXPECT_THAT(response_.partials(), ElementsAreArray(partial_responses));
@@ -3220,7 +3217,7 @@
                         response_.GetStreamingCallback());
   ASSERT_TRUE(response_.GetFinalStatus());
   EXPECT_EQ(*response_.value(),
-            "Fastest inference\nContext: execute:foo off:0 max:1024\n");
+            "Fastest inference\nContext: execute:foo max:1024\n");
 }
 
 TEST_F(OnDeviceModelServiceControllerTest, ImageExecutionSuccess) {
@@ -3277,9 +3274,8 @@
     session->ExecuteModel(proto::ExampleForTestingRequest(),
                           response.GetStreamingCallback());
     ASSERT_TRUE(response.GetFinalStatus());
-    EXPECT_EQ(
-        *response.value(),
-        "Context: <image> off:0 max:22\nContext: <image> off:0 max:1024\n");
+    EXPECT_EQ(*response.value(),
+              "Context: <image> max:22\nContext: <image> max:1024\n");
   }
 
   // Session without capabilities should not allow images.
@@ -3292,8 +3288,8 @@
                           response.GetStreamingCallback());
     ASSERT_TRUE(response.GetFinalStatus());
     EXPECT_EQ(*response.value(),
-              "Context: <unsupported> off:0 max:22\nContext: <unsupported> "
-              "off:0 max:1024\n");
+              "Context: <unsupported> max:22\nContext: <unsupported> "
+              "max:1024\n");
   }
 }
 
@@ -3386,7 +3382,7 @@
                               altered_response.GetStreamingCallback());
   ASSERT_TRUE(altered_response.GetFinalStatus());
   EXPECT_EQ(*altered_response.value(),
-            "Context: v1<image><audio>v2v3v4 off:0 max:22\n");
+            "Context: v1<image><audio>v2v3v4 max:22\n");
 
   // The clone that only extended should have sent input in separate chunks.
   ResponseHolder extended_response;
@@ -3394,19 +3390,19 @@
                                extended_response.GetStreamingCallback());
   ASSERT_TRUE(extended_response.GetFinalStatus());
   EXPECT_EQ(*extended_response.value(),
-            "Context: v1<image><audio> off:0 max:22\n"
-            "Context: v2 off:0 max:22\n"
-            "Context: v3 off:0 max:4\n"
-            "Context: v4 off:0 max:4\n");
+            "Context: v1<image><audio> max:22\n"
+            "Context: v2 max:22\n"
+            "Context: v3 max:4\n"
+            "Context: v4 max:4\n");
 
   // The original should have input in separate chunks.
   session->ExecuteModel(proto::ExampleForTestingRequest(),
                         response_.GetStreamingCallback());
   ASSERT_TRUE(response_.GetFinalStatus());
   EXPECT_EQ(*response_.value(),
-            "Context: v1<image><audio> off:0 max:22\n"
-            "Context: v2 off:0 max:22\n"
-            "Context: v3 off:0 max:4\n");
+            "Context: v1<image><audio> max:22\n"
+            "Context: v2 max:22\n"
+            "Context: v3 max:4\n");
 }
 
 TEST_F(OnDeviceModelServiceControllerTest, OmitEmptyInputs) {
@@ -3477,7 +3473,7 @@
   task_environment_.RunUntilIdle();
   EXPECT_TRUE(response_.value());
   const std::vector<std::string> partial_responses = {
-      "Context: execute:foo off:0 max:1024\n",
+      "Context: execute:foo max:1024\n",
   };
   EXPECT_EQ(*response_.value(), ConcatResponses(partial_responses));
   EXPECT_THAT(response_.partials(), ElementsAreArray(partial_responses));
@@ -3567,8 +3563,8 @@
     clone->ExecuteModel(PageUrlRequest("bar"), response.GetStreamingCallback());
     ASSERT_TRUE(response.GetFinalStatus());
     std::string expected_response =
-        ("Context: ctx:foo off:0 max:8192\n"
-         "Context: execute:foobar off:0 max:1024\n");
+        ("Context: ctx:foo max:8192\n"
+         "Context: execute:foobar max:1024\n");
     EXPECT_EQ(*response.value(), expected_response);
   }
 
@@ -3579,8 +3575,8 @@
                           response.GetStreamingCallback());
     ASSERT_TRUE(response.GetFinalStatus());
     std::string expected_response =
-        ("Context: ctx:foo off:0 max:8192\n"
-         "Context: execute:fooblah off:0 max:1024\n");
+        ("Context: ctx:foo max:8192\n"
+         "Context: execute:fooblah max:1024\n");
     EXPECT_EQ(*response.value(), expected_response);
   }
 }
@@ -3599,7 +3595,7 @@
     ResponseHolder response;
     clone->ExecuteModel(PageUrlRequest("bar"), response.GetStreamingCallback());
     ASSERT_TRUE(response.GetFinalStatus());
-    EXPECT_EQ(*response.value(), "Context: execute:bar off:0 max:1024\n");
+    EXPECT_EQ(*response.value(), "Context: execute:bar max:1024\n");
   }
 
   // Original session should execute with context
@@ -3609,8 +3605,8 @@
                           response.GetStreamingCallback());
     ASSERT_TRUE(response.GetFinalStatus());
     std::string expected_response =
-        ("Context: ctx:foo off:0 max:8192\n"
-         "Context: execute:fooblah off:0 max:1024\n");
+        ("Context: ctx:foo max:8192\n"
+         "Context: execute:fooblah max:1024\n");
     EXPECT_EQ(*response.value(), expected_response);
   }
 }
@@ -3628,7 +3624,7 @@
   ResponseHolder response;
   clone->ExecuteModel(PageUrlRequest("bar"), response.GetStreamingCallback());
   ASSERT_TRUE(response.GetFinalStatus());
-  EXPECT_EQ(*response.value(), "Context: execute:foobar off:0 max:1024\n");
+  EXPECT_EQ(*response.value(), "Context: execute:foobar max:1024\n");
 }
 
 TEST_F(OnDeviceModelServiceControllerTest, CloneAddContextDisconnectExecute) {
@@ -3647,8 +3643,8 @@
   clone->ExecuteModel(PageUrlRequest("bar"), response.GetStreamingCallback());
   ASSERT_TRUE(response.GetFinalStatus());
   std::string expected_response =
-      ("Context: ctx:foo off:0 max:8192\n"
-       "Context: execute:foobar off:0 max:1024\n");
+      ("Context: ctx:foo max:8192\n"
+       "Context: execute:foobar max:1024\n");
   EXPECT_EQ(*response.value(), expected_response);
 }
 
@@ -3673,7 +3669,7 @@
   ResponseHolder response;
   session->ExecuteModel(PageUrlRequest("bar"), response.GetStreamingCallback());
   ASSERT_TRUE(response.GetFinalStatus());
-  EXPECT_EQ(*response.value(), "Context: execute:bar off:0 max:1024\n");
+  EXPECT_EQ(*response.value(), "Context: execute:bar max:1024\n");
 }
 
 TEST_F(OnDeviceModelServiceControllerTest, Priority) {
@@ -3682,18 +3678,16 @@
   auto session = CreateSession();
   EXPECT_TRUE(session);
 
-  EXPECT_EQ(GetResponse(*session, "foo"),
-            "Context: execute:foo off:0 max:1024\n");
+  EXPECT_EQ(GetResponse(*session, "foo"), "Context: execute:foo max:1024\n");
 
   session->SetPriority(on_device_model::mojom::Priority::kBackground);
   EXPECT_EQ(GetResponse(*session, "foo"),
-            "Priority: background\nContext: execute:foo off:0 max:1024\n");
+            "Priority: background\nContext: execute:foo max:1024\n");
   EXPECT_EQ(GetResponse(*session, "foo"),
-            "Priority: background\nContext: execute:foo off:0 max:1024\n");
+            "Priority: background\nContext: execute:foo max:1024\n");
 
   session->SetPriority(on_device_model::mojom::Priority::kForeground);
-  EXPECT_EQ(GetResponse(*session, "foo"),
-            "Context: execute:foo off:0 max:1024\n");
+  EXPECT_EQ(GetResponse(*session, "foo"), "Context: execute:foo max:1024\n");
 }
 
 TEST_F(OnDeviceModelServiceControllerTest, PriorityClone) {
@@ -3702,18 +3696,17 @@
   auto session = CreateSession();
   EXPECT_TRUE(session);
 
-  EXPECT_EQ(GetResponse(*session, "foo"),
-            "Context: execute:foo off:0 max:1024\n");
+  EXPECT_EQ(GetResponse(*session, "foo"), "Context: execute:foo max:1024\n");
 
   session->SetPriority(on_device_model::mojom::Priority::kBackground);
   EXPECT_EQ(GetResponse(*session, "foo"),
-            "Priority: background\nContext: execute:foo off:0 max:1024\n");
+            "Priority: background\nContext: execute:foo max:1024\n");
 
   auto clone = session->Clone();
   EXPECT_EQ(GetResponse(*clone, "foo"),
-            "Priority: background\nContext: execute:foo off:0 max:1024\n");
+            "Priority: background\nContext: execute:foo max:1024\n");
   EXPECT_EQ(GetResponse(*clone, "foo"),
-            "Priority: background\nContext: execute:foo off:0 max:1024\n");
+            "Priority: background\nContext: execute:foo max:1024\n");
 }
 
 TEST_F(OnDeviceModelServiceControllerTest, SetInputCallback) {
@@ -3733,7 +3726,7 @@
                         response_.GetStreamingCallback());
   ASSERT_TRUE(response_.GetFinalStatus());
   EXPECT_EQ(response_.value(),
-            "Context: ctx:foo off:0 max:8192\nContext: execute:foobar off:0 "
+            "Context: ctx:foo max:8192\nContext: execute:foobar "
             "max:1024\n");
 }
 
@@ -3763,7 +3756,7 @@
                         response_.GetStreamingCallback());
   ASSERT_TRUE(response_.GetFinalStatus());
   EXPECT_EQ(response_.value(),
-            "Context: ctx:foo off:0 max:8192\nContext: execute:foobar off:0 "
+            "Context: ctx:foo max:8192\nContext: execute:foobar "
             "max:1024\n");
 }
 
@@ -3792,10 +3785,9 @@
   session->ExecuteModel(PageUrlRequest("foo"),
                         response_.GetStreamingCallback());
   ASSERT_TRUE(response_.GetFinalStatus());
-  EXPECT_EQ(response_.value(), "Context: execute:foo off:0 max:1024\n");
+  EXPECT_EQ(response_.value(), "Context: execute:foo max:1024\n");
   EXPECT_EQ(response_.input_token_count(), strlen("execute:foo"));
-  EXPECT_EQ(response_.output_token_count(),
-            strlen("execute:foo off:0 max:1024"));
+  EXPECT_EQ(response_.output_token_count(), strlen("execute:foo max:1024"));
 }
 
 }  // namespace optimization_guide
diff --git a/components/ownership/ownership_export.h b/components/ownership/ownership_export.h
index 6932100..c259a1ce 100644
--- a/components/ownership/ownership_export.h
+++ b/components/ownership/ownership_export.h
@@ -17,11 +17,7 @@
 
 #else  // defined(WIN32)
 
-#if defined(OWNERSHIP_IMPLEMENTATION)
 #define OWNERSHIP_EXPORT __attribute__((visibility("default")))
-#else
-#define OWNERSHIP_EXPORT
-#endif  // defined(OWNERSHIP_IMPLEMENTATION)
 
 #endif  // defined(WIN32)
 
diff --git a/components/page_load_metrics/browser/observers/use_counter/webdx_feature_maps.cc b/components/page_load_metrics/browser/observers/use_counter/webdx_feature_maps.cc
index 2627bae..80f8b6b6 100644
--- a/components/page_load_metrics/browser/observers/use_counter/webdx_feature_maps.cc
+++ b/components/page_load_metrics/browser/observers/use_counter/webdx_feature_maps.cc
@@ -537,6 +537,7 @@
           {CSSSampleId::kInteractivity, WebDXFeature::kInteractivity},
           {CSSSampleId::kReadingFlow, WebDXFeature::kReadingFlow},
           {CSSSampleId::kPrintColorAdjust, WebDXFeature::kPrintColorAdjust},
+          {CSSSampleId::kLineBreak, WebDXFeature::kLineBreak},
           // Add new features above this line.
       }};
 
diff --git a/components/page_load_metrics/browser/page_load_metrics_test_content_browser_client.cc b/components/page_load_metrics/browser/page_load_metrics_test_content_browser_client.cc
index 2ec156b..3349573 100644
--- a/components/page_load_metrics/browser/page_load_metrics_test_content_browser_client.cc
+++ b/components/page_load_metrics/browser/page_load_metrics_test_content_browser_client.cc
@@ -19,15 +19,13 @@
 PageLoadMetricsTestContentBrowserClient::
     ~PageLoadMetricsTestContentBrowserClient() = default;
 
-std::vector<std::unique_ptr<content::NavigationThrottle>>
-PageLoadMetricsTestContentBrowserClient::CreateThrottlesForNavigation(
+void PageLoadMetricsTestContentBrowserClient::CreateThrottlesForNavigation(
     content::NavigationThrottleRegistry& registry) {
   content::NavigationHandle& navigation_handle = registry.GetNavigationHandle();
   if (navigation_handle.IsInMainFrame()) {
     registry.AddThrottle(page_load_metrics::MetricsNavigationThrottle::Create(
         &navigation_handle));
   }
-  return {};
 }
 
 }  // namespace page_load_metrics
diff --git a/components/page_load_metrics/browser/page_load_metrics_test_content_browser_client.h b/components/page_load_metrics/browser/page_load_metrics_test_content_browser_client.h
index c215994..7613343b 100644
--- a/components/page_load_metrics/browser/page_load_metrics_test_content_browser_client.h
+++ b/components/page_load_metrics/browser/page_load_metrics_test_content_browser_client.h
@@ -24,8 +24,7 @@
   ~PageLoadMetricsTestContentBrowserClient() override;
 
   // content::ContentBrowserClient:
-  std::vector<std::unique_ptr<content::NavigationThrottle>>
-  CreateThrottlesForNavigation(
+  void CreateThrottlesForNavigation(
       content::NavigationThrottleRegistry& registry) override;
 };
 
diff --git a/components/password_manager/core/browser/BUILD.gn b/components/password_manager/core/browser/BUILD.gn
index 2fb34835..35ca418 100644
--- a/components/password_manager/core/browser/BUILD.gn
+++ b/components/password_manager/core/browser/BUILD.gn
@@ -73,6 +73,8 @@
     "move_password_to_account_store_helper.h",
     "old_google_credentials_cleaner.cc",
     "old_google_credentials_cleaner.h",
+    "one_time_passwords/otp_form_manager.cc",
+    "one_time_passwords/otp_form_manager.h",
     "one_time_passwords/otp_manager.cc",
     "one_time_passwords/otp_manager.h",
     "origin_credential_store.cc",
@@ -470,6 +472,7 @@
     "leak_detection_delegate_unittest.cc",
     "leak_detection_dialog_utils_unittest.cc",
     "old_google_credentials_cleaner_unittest.cc",
+    "one_time_passwords/otp_manager_unittest.cc",
     "origin_credential_store_unittest.cc",
     "passkey_credential_unittest.cc",
     "password_autofill_manager_unittest.cc",
diff --git a/components/password_manager/core/browser/browser_save_password_progress_logger.cc b/components/password_manager/core/browser/browser_save_password_progress_logger.cc
index 755e147..036d5939 100644
--- a/components/password_manager/core/browser/browser_save_password_progress_logger.cc
+++ b/components/password_manager/core/browser/browser_save_password_progress_logger.cc
@@ -350,15 +350,16 @@
       base::StrAppend(&field_info, {", VOTE: ", FieldTypeToStringView(type)});
     }
 
-    if (field->vote_type()) {
-      field_info += ", vote_type=" + VoteTypeToString(field->vote_type());
-    }
-
     if (auto it = vote_metadata.fields.find(field->global_id());
         it != vote_metadata.fields.end()) {
       const autofill::EncodeUploadRequestOptions::Field& field_metadata =
           it->second;
 
+      if (field_metadata.vote_type) {
+        field_info +=
+            ", vote_type=" + VoteTypeToString(field_metadata.vote_type);
+      }
+
       if (field_metadata.initial_value_hash.has_value()) {
         field_info += ", initial value hash=";
         field_info += NumberToString(field_metadata.initial_value_hash.value());
diff --git a/components/password_manager/core/browser/features/password_features.cc b/components/password_manager/core/browser/features/password_features.cc
index 6c9db5fd..6aa1072 100644
--- a/components/password_manager/core/browser/features/password_features.cc
+++ b/components/password_manager/core/browser/features/password_features.cc
@@ -10,6 +10,12 @@
 
 namespace password_manager::features {
 
+#if BUILDFLAG(IS_ANDROID)
+BASE_FEATURE(kAndroidSmsOtpFilling,
+             "AndroidSmsOtpFilling",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+#endif  // BUILDFLAG(IS_ANDROID)
+
 BASE_FEATURE(kAutoApproveSharedPasswordUpdatesFromSameSender,
              "AutoApproveSharedPasswordUpdatesFromSameSender",
              base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/components/password_manager/core/browser/features/password_features.h b/components/password_manager/core/browser/features/password_features.h
index 3e3f21d..58a15bd 100644
--- a/components/password_manager/core/browser/features/password_features.h
+++ b/components/password_manager/core/browser/features/password_features.h
@@ -16,6 +16,11 @@
 // All features in alphabetical order. The features should be documented
 // alongside the definition of their values in the .cc file.
 
+#if BUILDFLAG(IS_ANDROID)
+// Enables filling of OTPs received via SMS on Android.
+BASE_DECLARE_FEATURE(kAndroidSmsOtpFilling);
+#endif  // BUILDFLAG(IS_ANDROID)
+
 // When enabled, updates to shared existing passwords from the same sender are
 // auto-approved.
 BASE_DECLARE_FEATURE(kAutoApproveSharedPasswordUpdatesFromSameSender);
diff --git a/components/password_manager/core/browser/leak_detection/leak_detection_api.proto b/components/password_manager/core/browser/leak_detection/leak_detection_api.proto
index de653e1..066fa2b 100644
--- a/components/password_manager/core/browser/leak_detection/leak_detection_api.proto
+++ b/components/password_manager/core/browser/leak_detection/leak_detection_api.proto
@@ -75,6 +75,10 @@
     CHROME_IOS_SIGNED_IN_ON_DEVICE_PROACTIVE_PASSWORD_CHECKUP = 18;
     // Leak checks done by ios/web_view during sign in to a website.
     IOS_WEB_VIEW_SIGN_IN_CHECK = 19;
+
+    // Edit server proto first and then update this file:
+    // http://google3/google/internal/identity/passwords/leak/check/v1/service.proto
+    reserved 22 to max;
   }
   ClientUseCase client_use_case = 4;
 }
diff --git a/components/password_manager/core/browser/one_time_passwords/otp_form_manager.cc b/components/password_manager/core/browser/one_time_passwords/otp_form_manager.cc
new file mode 100644
index 0000000..cb71110
--- /dev/null
+++ b/components/password_manager/core/browser/one_time_passwords/otp_form_manager.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/password_manager/core/browser/one_time_passwords/otp_form_manager.h"
+
+namespace password_manager {
+
+OtpFormManager::OtpFormManager(
+    autofill::FormGlobalId form_id,
+    const std::vector<autofill::FieldGlobalId>& otp_field_ids)
+    : form_id_(form_id), otp_field_ids_(std::move(otp_field_ids)) {
+  // TODO(crbug.com/415273770): Trigger OTP fetching if needed.
+}
+
+OtpFormManager::OtpFormManager(OtpFormManager&&) = default;
+OtpFormManager& OtpFormManager::operator=(OtpFormManager&&) = default;
+
+OtpFormManager::~OtpFormManager() = default;
+
+void OtpFormManager::ProcessUpdatedPredictions(
+    const std::vector<autofill::FieldGlobalId>& otp_field_ids) {
+  otp_field_ids_ = std::move(otp_field_ids);
+
+  // TODO(crbug.com/415273770): Check if OTP source has changed.
+}
+
+}  // namespace password_manager
diff --git a/components/password_manager/core/browser/one_time_passwords/otp_form_manager.h b/components/password_manager/core/browser/one_time_passwords/otp_form_manager.h
new file mode 100644
index 0000000..c72cd80c
--- /dev/null
+++ b/components/password_manager/core/browser/one_time_passwords/otp_form_manager.h
@@ -0,0 +1,48 @@
+// 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_PASSWORD_MANAGER_CORE_BROWSER_ONE_TIME_PASSWORDS_OTP_FORM_MANAGER_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_ONE_TIME_PASSWORDS_OTP_FORM_MANAGER_H_
+
+#include <vector>
+
+#include "base/containers/flat_map.h"
+#include "components/autofill/core/browser/field_types.h"
+#include "components/autofill/core/common/unique_ids.h"
+
+namespace password_manager {
+
+// A class in charge of handling individual OTP forms, one instance per form.
+class OtpFormManager {
+ public:
+  OtpFormManager(autofill::FormGlobalId form_id,
+                 const std::vector<autofill::FieldGlobalId>& otp_field_ids);
+
+  OtpFormManager(const OtpFormManager&) = delete;
+  OtpFormManager& operator=(const OtpFormManager&) = delete;
+  OtpFormManager(OtpFormManager&&);
+  OtpFormManager& operator=(OtpFormManager&&);
+
+  ~OtpFormManager();
+
+  // Forms can change dynamically during their lifetime. Ensure the most recent
+  // data is used for form filling.
+  void ProcessUpdatedPredictions(
+      const std::vector<autofill::FieldGlobalId>& otp_field_ids);
+
+#if defined(UNIT_TEST)
+  const std::vector<autofill::FieldGlobalId>& otp_field_ids() const {
+    return otp_field_ids_;
+  }
+#endif  // defined(UNIT_TEST)
+
+ private:
+  autofill::FormGlobalId form_id_;
+
+  std::vector<autofill::FieldGlobalId> otp_field_ids_;
+};
+
+}  // namespace password_manager
+
+#endif  // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_ONE_TIME_PASSWORDS_OTP_FORM_MANAGER_H_
diff --git a/components/password_manager/core/browser/one_time_passwords/otp_manager.cc b/components/password_manager/core/browser/one_time_passwords/otp_manager.cc
index 5d3dd83..98fc8cfe 100644
--- a/components/password_manager/core/browser/one_time_passwords/otp_manager.cc
+++ b/components/password_manager/core/browser/one_time_passwords/otp_manager.cc
@@ -5,22 +5,61 @@
 #include "components/password_manager/core/browser/one_time_passwords/otp_manager.h"
 
 #include "components/autofill/core/common/form_data.h"
+#include "components/autofill/core/common/form_field_data.h"
+#include "components/password_manager/core/browser/one_time_passwords/otp_form_manager.h"
 #include "components/password_manager/core/browser/password_manager_client.h"
 
 namespace password_manager {
 
-OtpManager::OtpManager(PasswordManagerClient* client) : client_(client) {
-  DCHECK(client_);
+namespace {
+
+std::vector<autofill::FieldGlobalId> GetFillableOtpFieldIds(
+    const autofill::FormData& form,
+    const base::flat_map<autofill::FieldGlobalId, autofill::FieldType>&
+        field_predictions) {
+  std::vector<autofill::FieldGlobalId> fillable_otp_fields;
+  for (const auto& prediction : field_predictions) {
+    if (prediction.second != autofill::ONE_TIME_CODE) {
+      continue;
+    }
+    const autofill::FormFieldData* field =
+        form.FindFieldByGlobalId(prediction.first);
+    if (field->IsTextInputElement()) {
+      fillable_otp_fields.push_back(prediction.first);
+    }
+  }
+  return fillable_otp_fields;
 }
 
+}  // namespace
+
+OtpManager::OtpManager(PasswordManagerClient* client) : client_(client) {
+  CHECK(client_);
+}
+
+OtpManager::~OtpManager() = default;
+
 void OtpManager::ProcessClassificationModelPredictions(
     const autofill::FormData& form,
     const base::flat_map<autofill::FieldGlobalId, autofill::FieldType>&
         field_predictions) {
-  // TODO(415269545): Rationalize predictions and trigger OTP fetching if
-  // needed.
+  std::vector<autofill::FieldGlobalId> fillable_otp_fields(
+      GetFillableOtpFieldIds(form, field_predictions));
 
-  client_->InformPasswordChangeServiceOfOtpPresent();
+  autofill::FormGlobalId form_id(form.global_id());
+  if (fillable_otp_fields.empty()) {
+    form_managers_.erase(form_id);
+    return;
+  }
+
+  if (form_managers_.find(form_id) == form_managers_.end()) {
+    form_managers_.emplace(form_id,
+                           OtpFormManager(form_id, fillable_otp_fields));
+    client_->InformPasswordChangeServiceOfOtpPresent();
+
+  } else {
+    form_managers_.at(form_id).ProcessUpdatedPredictions(fillable_otp_fields);
+  }
 }
 
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/one_time_passwords/otp_manager.h b/components/password_manager/core/browser/one_time_passwords/otp_manager.h
index 003bbb6..f4da53f 100644
--- a/components/password_manager/core/browser/one_time_passwords/otp_manager.h
+++ b/components/password_manager/core/browser/one_time_passwords/otp_manager.h
@@ -16,6 +16,7 @@
 
 namespace password_manager {
 
+class OtpFormManager;
 class PasswordManagerClient;
 
 // A class in charge of handling one time passwords, one per tab.
@@ -23,15 +24,27 @@
  public:
   explicit OtpManager(PasswordManagerClient* client);
 
+  ~OtpManager();
+
   // Processes the classification model predictions received via Autofill.
   void ProcessClassificationModelPredictions(
       const autofill::FormData& form,
       const base::flat_map<autofill::FieldGlobalId, autofill::FieldType>&
           field_predictions);
 
+#if defined(UNIT_TEST)
+  const base::flat_map<autofill::FormGlobalId, OtpFormManager>& form_managers()
+      const {
+    return form_managers_;
+  }
+#endif  // defined(UNIT_TEST)
+
  private:
   // The client that owns this class and is guaranteed to outlive it.
   const raw_ptr<PasswordManagerClient> client_;
+
+  // Managers managing individual forms.
+  base::flat_map<autofill::FormGlobalId, OtpFormManager> form_managers_;
 };
 
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/one_time_passwords/otp_manager_unittest.cc b/components/password_manager/core/browser/one_time_passwords/otp_manager_unittest.cc
new file mode 100644
index 0000000..af3dbfe
--- /dev/null
+++ b/components/password_manager/core/browser/one_time_passwords/otp_manager_unittest.cc
@@ -0,0 +1,116 @@
+// 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/password_manager/core/browser/one_time_passwords/otp_manager.h"
+
+#include "components/autofill/core/common/autofill_test_utils.h"
+#include "components/autofill/core/common/form_data.h"
+#include "components/password_manager/core/browser/one_time_passwords/otp_form_manager.h"
+#include "components/password_manager/core/browser/stub_password_manager_client.h"
+
+namespace password_manager {
+
+namespace {
+
+using autofill::FormData;
+
+class MockPasswordManagerClient : public StubPasswordManagerClient {
+ public:
+  MockPasswordManagerClient() = default;
+
+  MOCK_METHOD(void, InformPasswordChangeServiceOfOtpPresent, (), (override));
+};
+}  // namespace
+
+class OtpManagerTest : public testing::Test {
+ public:
+  OtpManagerTest() : otp_manager_(&mock_client_) {}
+
+ protected:
+  MockPasswordManagerClient mock_client_;
+  OtpManager otp_manager_;
+
+ private:
+  autofill::test::AutofillUnitTestEnvironment autofill_environment_;
+};
+
+TEST_F(OtpManagerTest, FormManagerCreatedForOtpForm) {
+  FormData form;
+  form.set_fields({autofill::test::CreateTestFormField(
+      "some_label", "some_name", "some_value",
+      autofill::FormControlType::kInputText)});
+
+  EXPECT_CALL(mock_client_, InformPasswordChangeServiceOfOtpPresent);
+  otp_manager_.ProcessClassificationModelPredictions(
+      form, {{form.fields()[0].global_id(), autofill::ONE_TIME_CODE}});
+
+  EXPECT_TRUE(otp_manager_.form_managers().contains(form.global_id()));
+  std::vector<autofill::FieldGlobalId> expected_otp_field_ids = {
+      form.fields()[0].global_id()};
+  EXPECT_EQ(expected_otp_field_ids,
+            otp_manager_.form_managers().at(form.global_id()).otp_field_ids());
+}
+
+TEST_F(OtpManagerTest, FormManagerNotCreatedForNotFillableForm) {
+  FormData form;
+  form.set_fields({autofill::test::CreateTestFormField(
+      "some_label", "some_name", "some_value",
+      // Radio element cannot be filled with a text value, this prediction
+      // should not be taken into account.
+      autofill::FormControlType::kInputRadio)});
+
+  EXPECT_CALL(mock_client_, InformPasswordChangeServiceOfOtpPresent).Times(0);
+  otp_manager_.ProcessClassificationModelPredictions(
+      form, {{form.fields()[0].global_id(), autofill::ONE_TIME_CODE}});
+
+  EXPECT_FALSE(otp_manager_.form_managers().contains(form.global_id()));
+}
+
+TEST_F(OtpManagerTest, ManagersUpdatedWhenPredictionsChange) {
+  FormData form;
+  form.set_fields({autofill::test::CreateTestFormField(
+                       "some_label1", "some_name1", "some_value1",
+                       autofill::FormControlType::kInputText),
+                   {autofill::test::CreateTestFormField(
+                       "some_label2", "some_name2", "some_value2",
+                       autofill::FormControlType::kInputPassword)}});
+
+  EXPECT_CALL(mock_client_, InformPasswordChangeServiceOfOtpPresent);
+  otp_manager_.ProcessClassificationModelPredictions(
+      form, {{form.fields()[0].global_id(), autofill::ONE_TIME_CODE},
+             {form.fields()[1].global_id(), autofill::UNKNOWN_TYPE}});
+  EXPECT_TRUE(otp_manager_.form_managers().contains(form.global_id()));
+
+  // Simulate receiving new predictions.
+  // The client should not be notified the second time.
+  EXPECT_CALL(mock_client_, InformPasswordChangeServiceOfOtpPresent).Times(0);
+  otp_manager_.ProcessClassificationModelPredictions(
+      form, {{form.fields()[0].global_id(), autofill::UNKNOWN_TYPE},
+             {form.fields()[1].global_id(), autofill::ONE_TIME_CODE}});
+
+  EXPECT_TRUE(otp_manager_.form_managers().contains(form.global_id()));
+  // Check that the manager reflects the latest predictions.
+  std::vector<autofill::FieldGlobalId> expected_otp_field_ids = {
+      form.fields()[1].global_id()};
+  EXPECT_EQ(expected_otp_field_ids,
+            otp_manager_.form_managers().at(form.global_id()).otp_field_ids());
+}
+
+TEST_F(OtpManagerTest, FormManagerdDeletedWhenOtpFieldIsNoLongerParsedAsSuch) {
+  FormData form;
+  form.set_fields({autofill::test::CreateTestFormField(
+      "some_label", "some_name", "some_value",
+      autofill::FormControlType::kInputText)});
+
+  EXPECT_CALL(mock_client_, InformPasswordChangeServiceOfOtpPresent);
+  otp_manager_.ProcessClassificationModelPredictions(
+      form, {{form.fields()[0].global_id(), autofill::ONE_TIME_CODE}});
+  EXPECT_TRUE(otp_manager_.form_managers().contains(form.global_id()));
+
+  otp_manager_.ProcessClassificationModelPredictions(
+      form, {{form.fields()[0].global_id(), autofill::UNKNOWN_TYPE}});
+  EXPECT_TRUE(otp_manager_.form_managers().empty());
+}
+
+}  // namespace password_manager
diff --git a/components/password_manager/core/browser/password_manager.cc b/components/password_manager/core/browser/password_manager.cc
index e75602a..f3bbab1 100644
--- a/components/password_manager/core/browser/password_manager.cc
+++ b/components/password_manager/core/browser/password_manager.cc
@@ -1463,7 +1463,9 @@
   PasswordFormManager* submitted_manager = GetSubmittedManager();
 
   if (!submitted_manager || !submitted_manager->GetSubmittedForm()) {
-    logger->LogMessage(Logger::STRING_NO_SUBMITTED_MANAGER_AVAILABLE);
+    if (logger) {
+      logger->LogMessage(Logger::STRING_NO_SUBMITTED_MANAGER_AVAILABLE);
+    }
     return;
   }
 
diff --git a/components/password_manager/core/browser/ui/credential_ui_entry.cc b/components/password_manager/core/browser/ui/credential_ui_entry.cc
index 53a07f8..7f70a5b 100644
--- a/components/password_manager/core/browser/ui/credential_ui_entry.cc
+++ b/components/password_manager/core/browser/ui/credential_ui_entry.cc
@@ -318,10 +318,6 @@
   return CreateSortKey(lhs) == CreateSortKey(rhs);
 }
 
-bool operator!=(const CredentialUIEntry& lhs, const CredentialUIEntry& rhs) {
-  return !(lhs == rhs);
-}
-
 bool operator<(const CredentialUIEntry& lhs, const CredentialUIEntry& rhs) {
   return CreateSortKey(lhs) < CreateSortKey(rhs);
 }
diff --git a/components/password_manager/core/browser/ui/credential_ui_entry.h b/components/password_manager/core/browser/ui/credential_ui_entry.h
index 77fed51..0f9302b 100644
--- a/components/password_manager/core/browser/ui/credential_ui_entry.h
+++ b/components/password_manager/core/browser/ui/credential_ui_entry.h
@@ -184,7 +184,6 @@
 std::string CreateSortKey(const CredentialUIEntry& credential);
 
 bool operator==(const CredentialUIEntry& lhs, const CredentialUIEntry& rhs);
-bool operator!=(const CredentialUIEntry& lhs, const CredentialUIEntry& rhs);
 bool operator<(const CredentialUIEntry& lhs, const CredentialUIEntry& rhs);
 
 // Returns true when the credential is either leaked or phished.
diff --git a/components/password_manager/core/browser/ui/passwords_grouper.cc b/components/password_manager/core/browser/ui/passwords_grouper.cc
index c296076..17ca0479 100644
--- a/components/password_manager/core/browser/ui/passwords_grouper.cc
+++ b/components/password_manager/core/browser/ui/passwords_grouper.cc
@@ -106,10 +106,10 @@
    public:
     iterator(size_t i, const SortedPasskeysView* sorted)
         : i_(i), sorted_(sorted) {}
+
+    friend bool operator==(const iterator&, const iterator&) = default;
+
     void operator++() { i_++; }
-    bool operator!=(const iterator& other) const {
-      return i_ != other.i_ || sorted_ != other.sorted_;
-    }
     const PasskeyCredential& operator*() {
       return sorted_->passkeys_[sorted_->sorted_indexes_[i_]];
     }
diff --git a/components/password_manager/core/browser/votes_uploader.cc b/components/password_manager/core/browser/votes_uploader.cc
index fedbcfc..32c06772 100644
--- a/components/password_manager/core/browser/votes_uploader.cc
+++ b/components/password_manager/core/browser/votes_uploader.cc
@@ -134,26 +134,30 @@
 void LabelFields(const FieldTypeMap& field_types,
                  const bool field_name_collision,
                  const VoteTypeMap& vote_types,
-                 FormStructure* form_structure,
-                 FieldTypeSet* available_field_types) {
+                 FormStructure& form_structure,
+                 autofill::EncodeUploadRequestOptions& options) {
   UMA_HISTOGRAM_BOOLEAN("PasswordManager.FieldNameCollisionInVotes",
                         field_name_collision);
-  for (size_t i = 0; i < form_structure->field_count(); ++i) {
-    AutofillField* field = form_structure->field(i);
+  for (size_t i = 0; i < form_structure.field_count(); ++i) {
+    AutofillField* field = form_structure.field(i);
 
     FieldType type = autofill::UNKNOWN_TYPE;
     if (auto iter = field_types.find(field->renderer_id());
         iter != field_types.end()) {
       type = iter->second;
-      available_field_types->insert(type);
+      options.available_field_types.insert(type);
     }
 
     if (auto vote_type_iter = vote_types.find(field->renderer_id());
         vote_type_iter != vote_types.end()) {
-      field->set_vote_type(vote_type_iter->second);
+      AutofillUploadContents::Field::VoteType vote_type =
+          vote_type_iter->second;
+      options.fields[field->global_id()].vote_type = vote_type;
+      CHECK(type != autofill::USERNAME ||
+            vote_type != AutofillUploadContents::Field::NO_INFORMATION);
+    } else {
+      CHECK(type != autofill::USERNAME);
     }
-    CHECK(type != autofill::USERNAME ||
-          field->vote_type() != AutofillUploadContents::Field::NO_INFORMATION);
     FieldTypeSet types;
     types.insert(type);
     field->set_possible_types(types);
@@ -535,7 +539,7 @@
   LabelFields(
       field_types, field_name_collision,
       {{form_to_upload.username_element_renderer_id, username_vote_type}},
-      &form_structure, &options.available_field_types);
+      form_structure, options);
 
   if (password_manager_util::IsLoggingActive(client_)) {
     BrowserSavePasswordProgressLogger logger(client_->GetCurrentLogManager());
@@ -583,8 +587,8 @@
         AutofillUploadContents::Field::FIRST_USE;
   }
 
-  LabelFields(field_types, field_name_collision, vote_types, &form_structure,
-              &options.available_field_types);
+  LabelFields(field_types, field_name_collision, vote_types, form_structure,
+              options);
   SetKnownValueFlag(pending_credentials, best_matches, &form_structure);
 
   // Annotate the form with the source language of the page.
diff --git a/components/policy/core/common/BUILD.gn b/components/policy/core/common/BUILD.gn
index e7c873ae..dba951de 100644
--- a/components/policy/core/common/BUILD.gn
+++ b/components/policy/core/common/BUILD.gn
@@ -149,8 +149,6 @@
     "config_dir_policy_loader.h",
     "configuration_policy_provider.cc",
     "configuration_policy_provider.h",
-    "device_local_account_type.cc",
-    "device_local_account_type.h",
     "external_data_fetcher.cc",
     "external_data_fetcher.h",
     "external_data_manager.h",
@@ -338,7 +336,10 @@
     deps += [ "//components/policy/android:jni_headers" ]
   }
   if (is_chromeos) {
-    deps += [ "//chromeos/ash/components/system" ]
+    deps += [
+      "//chromeos/ash/components/policy/device_local_account",
+      "//chromeos/ash/components/system",
+    ]
     sources += [
       "default_chrome_apps_migrator.cc",
       "default_chrome_apps_migrator.h",
diff --git a/components/policy/core/common/DEPS b/components/policy/core/common/DEPS
index cc017e7..fb00f0d 100644
--- a/components/policy/core/common/DEPS
+++ b/components/policy/core/common/DEPS
@@ -1,5 +1,6 @@
 include_rules = [
   "+absl/types/variant.h",
+  "+chromeos/ash/components/policy/device_local_account",
   "+chromeos/ash/components/system",
   "+chromeos/crosapi",
   "+chromeos/startup",
diff --git a/components/policy/core/common/cloud/affiliation.cc b/components/policy/core/common/cloud/affiliation.cc
index 36cdb58..97edbcb3d 100644
--- a/components/policy/core/common/cloud/affiliation.cc
+++ b/components/policy/core/common/cloud/affiliation.cc
@@ -7,9 +7,12 @@
 #include "components/policy/core/common/cloud/cloud_policy_client.h"
 #include "components/policy/core/common/cloud/cloud_policy_core.h"
 #include "components/policy/core/common/cloud/cloud_policy_store.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "components/policy/proto/device_management_backend.pb.h"
 
+#if BUILDFLAG(IS_CHROMEOS)
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
+#endif
+
 namespace policy {
 
 bool IsAffiliated(const base::flat_set<std::string>& user_ids,
@@ -31,9 +34,11 @@
     return false;
   }
 
+#if BUILDFLAG(IS_CHROMEOS)
   if (IsDeviceLocalAccountUser(email)) {
     return true;
   }
+#endif
 
   return IsAffiliated(user_affiliation_ids, device_affiliation_ids);
 }
diff --git a/components/policy/core/common/features.cc b/components/policy/core/common/features.cc
index bfb2659..3d22f424 100644
--- a/components/policy/core/common/features.cc
+++ b/components/policy/core/common/features.cc
@@ -18,12 +18,4 @@
              "EnhancedSecurityEventFields",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-// Enables Kiosk session for the Helium android app.
-BASE_FEATURE(kHeliumArcvmKiosk,
-             "HeliumArcvmKiosk",
-             base::FEATURE_DISABLED_BY_DEFAULT);
-bool IsHeliumArcvmKioskEnabled() {
-  return base::FeatureList::IsEnabled(kHeliumArcvmKiosk);
-}
-
 }  // namespace policy::features
diff --git a/components/policy/core/common/features.h b/components/policy/core/common/features.h
index 94016d2..969677d2 100644
--- a/components/policy/core/common/features.h
+++ b/components/policy/core/common/features.h
@@ -23,10 +23,6 @@
 // Enables the addition of new security fields for SecOps.
 POLICY_EXPORT BASE_DECLARE_FEATURE(kEnhancedSecurityEventFields);
 
-// Enables Kiosk session for the Helium android app.
-POLICY_EXPORT BASE_DECLARE_FEATURE(kHeliumArcvmKiosk);
-POLICY_EXPORT bool IsHeliumArcvmKioskEnabled();
-
 }  // namespace policy::features
 
 #endif  // COMPONENTS_POLICY_CORE_COMMON_FEATURES_H_
diff --git a/components/policy/policy_export.h b/components/policy/policy_export.h
index a92647ff..1e52705 100644
--- a/components/policy/policy_export.h
+++ b/components/policy/policy_export.h
@@ -16,19 +16,11 @@
 #endif  // defined(POLICY_COMPONENT_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-
-#if defined(POLICY_COMPONENT_IMPLEMENTATION)
 #define POLICY_EXPORT __attribute__((visibility("default")))
-#else
-#define POLICY_EXPORT
-#endif  // defined(POLICY_COMPONENT_IMPLEMENTATION)
-
 #endif  // defined(WIN32)
 
 #else  // defined(COMPONENT_BUILD)
-
 #define POLICY_EXPORT
-
 #endif  // defined(COMPONENT_BUILD)
 
 #endif  // COMPONENTS_POLICY_POLICY_EXPORT_H_
diff --git a/components/policy/proto/policy_proto_export.h b/components/policy/proto/policy_proto_export.h
index 829356a..16dc5fe 100644
--- a/components/policy/proto/policy_proto_export.h
+++ b/components/policy/proto/policy_proto_export.h
@@ -22,27 +22,14 @@
 #endif  // defined(POLICY_PROTO_COMPILATION)
 
 #else  // defined(WIN32)
-
-#if defined(POLICY_PROTO_COMPILATION)
 #define POLICY_PROTO_EXPORT __attribute__((visibility("default")))
-#else
-#define POLICY_PROTO_EXPORT
-#endif  // defined(POLICY_PROTO_COMPILATION)
-
-#if defined(POLICY_CHROME_SETTINGS_PROTO_COMPILATION)
 #define POLICY_CHROME_SETTINGS_PROTO_EXPORT \
   __attribute__((visibility("default")))
-#else
-#define POLICY_CHROME_SETTINGS_PROTO_EXPORT
-#endif  // defined(POLICY_PROTO_COMPILATION)
-
 #endif  // defined(WIN32)
 
 #else  // defined(COMPONENT_BUILD)
-
 #define POLICY_PROTO_EXPORT
 #define POLICY_CHROME_SETTINGS_PROTO_EXPORT
-
 #endif  // defined(COMPONENT_BUILD)
 
 #endif  // COMPONENTS_POLICY_PROTO_POLICY_PROTO_EXPORT_H_
diff --git a/components/prefs/prefs_export.h b/components/prefs/prefs_export.h
index 8e5ee4f0..2086ffc 100644
--- a/components/prefs/prefs_export.h
+++ b/components/prefs/prefs_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(COMPONENTS_PREFS_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(COMPONENTS_PREFS_IMPLEMENTATION)
 #define COMPONENTS_PREFS_EXPORT __attribute__((visibility("default")))
-#else
-#define COMPONENTS_PREFS_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/components/proxy_config/proxy_config_export.h b/components/proxy_config/proxy_config_export.h
index 375f6d08..0f82367e 100644
--- a/components/proxy_config/proxy_config_export.h
+++ b/components/proxy_config/proxy_config_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(PROXY_CONFIG_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(PROXY_CONFIG_IMPLEMENTATION)
 #define PROXY_CONFIG_EXPORT __attribute__((visibility("default")))
-#else
-#define PROXY_CONFIG_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/components/quirks/quirks_export.h b/components/quirks/quirks_export.h
index ff1a2e1..ba724142 100644
--- a/components/quirks/quirks_export.h
+++ b/components/quirks/quirks_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(QUIRKS_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(QUIRKS_IMPLEMENTATION)
 #define QUIRKS_EXPORT __attribute__((visibility("default")))
-#else
-#define QUIRKS_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/components/reading_list/core/reading_list_local_data_batch_uploader.cc b/components/reading_list/core/reading_list_local_data_batch_uploader.cc
index 7031c5e..fde1e3dd 100644
--- a/components/reading_list/core/reading_list_local_data_batch_uploader.cc
+++ b/components/reading_list/core/reading_list_local_data_batch_uploader.cc
@@ -6,13 +6,18 @@
 
 #include <memory>
 #include <utility>
+#include <variant>
 #include <vector>
 
 #include "base/containers/flat_set.h"
+#include "base/feature_list.h"
 #include "base/functional/callback.h"
 #include "components/reading_list/core/dual_reading_list_model.h"
+#include "components/sync/base/data_type.h"
+#include "components/sync/base/features.h"
 #include "components/sync/service/local_data_description.h"
-#include "url/gurl.h"
+
+class GURL;
 
 namespace reading_list {
 
@@ -29,8 +34,19 @@
 
   base::flat_set<GURL> keys =
       dual_reading_list_model_->GetKeysThatNeedUploadToSyncServer();
-  std::move(callback).Run(
-      syncer::LocalDataDescription(std::vector(keys.begin(), keys.end())));
+  syncer::LocalDataDescription local_data_description =
+      syncer::LocalDataDescription(std::vector(keys.begin(), keys.end()));
+
+  if (base::FeatureList::IsEnabled(
+          syncer::kSyncReadingListBatchUploadSelectedItems)) {
+    local_data_description.type = syncer::DataType::READING_LIST;
+    for (const GURL& key : keys) {
+      local_data_description.local_data_models.push_back(
+          DataItemModelFromURL(key));
+    }
+  }
+
+  std::move(callback).Run(local_data_description);
 }
 
 void ReadingListLocalDataBatchUploader::TriggerLocalDataMigration() {
@@ -46,4 +62,13 @@
   return dual_reading_list_model_ && dual_reading_list_model_->loaded();
 }
 
+syncer::LocalDataItemModel
+ReadingListLocalDataBatchUploader::DataItemModelFromURL(const GURL& url) const {
+  syncer::LocalDataItemModel item;
+  item.id = url;
+  item.icon = syncer::LocalDataItemModel::PageUrlIcon(url);
+  item.title = dual_reading_list_model_->GetEntryByURL(url)->Title();
+  return item;
+}
+
 }  // namespace reading_list
diff --git a/components/reading_list/core/reading_list_local_data_batch_uploader.h b/components/reading_list/core/reading_list_local_data_batch_uploader.h
index 15566b0f..14c04f4 100644
--- a/components/reading_list/core/reading_list_local_data_batch_uploader.h
+++ b/components/reading_list/core/reading_list_local_data_batch_uploader.h
@@ -8,6 +8,8 @@
 #include "base/memory/raw_ptr.h"
 #include "components/sync/service/data_type_local_data_batch_uploader.h"
 
+class GURL;
+
 namespace reading_list {
 
 class DualReadingListModel;
@@ -34,6 +36,8 @@
 
  private:
   bool CanUpload() const;
+  // Returns the `LocalDataItemModel` corresponding to the given `url`.
+  syncer::LocalDataItemModel DataItemModelFromURL(const GURL& url) const;
 
   const raw_ptr<DualReadingListModel> dual_reading_list_model_;
 };
diff --git a/components/reading_list/core/reading_list_local_data_batch_uploader_unittest.cc b/components/reading_list/core/reading_list_local_data_batch_uploader_unittest.cc
index 85bb5f21..a3fec011 100644
--- a/components/reading_list/core/reading_list_local_data_batch_uploader_unittest.cc
+++ b/components/reading_list/core/reading_list_local_data_batch_uploader_unittest.cc
@@ -9,11 +9,14 @@
 
 #include "base/memory/ptr_util.h"
 #include "base/test/mock_callback.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/simple_test_clock.h"
 #include "base/test/test_future.h"
 #include "components/reading_list/core/dual_reading_list_model.h"
 #include "components/reading_list/core/fake_reading_list_model_storage.h"
 #include "components/reading_list/core/reading_list_model_impl.h"
+#include "components/sync/base/data_type.h"
+#include "components/sync/base/features.h"
 #include "components/sync/service/local_data_description.h"
 #include "components/sync/test/mock_data_type_local_change_processor.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -25,6 +28,7 @@
 using ::testing::ElementsAre;
 using ::testing::IsEmpty;
 using ::testing::Return;
+using ::testing::VariantWith;
 
 class ReadingListLocalDataBatchUploaderTest : public ::testing::Test {
  public:
@@ -72,6 +76,8 @@
 
   uploader.GetLocalDataDescription(description.GetCallback());
 
+  EXPECT_EQ(description.Get().type, syncer::DataType::UNSPECIFIED);
+  EXPECT_THAT(description.Get().local_data_models, IsEmpty());
   EXPECT_EQ(description.Get().item_count, 0u);
   EXPECT_EQ(description.Get().domain_count, 0u);
   EXPECT_THAT(description.Get().domains, IsEmpty());
@@ -84,6 +90,8 @@
 
   uploader.GetLocalDataDescription(description.GetCallback());
 
+  EXPECT_EQ(description.Get().type, syncer::DataType::UNSPECIFIED);
+  EXPECT_THAT(description.Get().local_data_models, IsEmpty());
   EXPECT_EQ(description.Get().item_count, 0u);
   EXPECT_EQ(description.Get().domain_count, 0u);
   EXPECT_THAT(description.Get().domains, IsEmpty());
@@ -103,6 +111,42 @@
 
   uploader.GetLocalDataDescription(description.GetCallback());
 
+  EXPECT_EQ(description.Get().type, syncer::DataType::READING_LIST);
+  auto local_data_models = description.Get().local_data_models;
+  EXPECT_EQ(local_data_models.size(), 1u);
+  auto item = local_data_models.back();
+  EXPECT_EQ(std::get<GURL>(item.id), GURL("https://local.com"));
+  EXPECT_EQ(item.title, "local");
+  EXPECT_THAT(item.subtitle, IsEmpty());
+  EXPECT_THAT(item.icon, VariantWith<syncer::LocalDataItemModel::PageUrlIcon>(
+                             GURL("https://local.com/")));
+
+  EXPECT_EQ(description.Get().item_count, 1u);
+  EXPECT_EQ(description.Get().domain_count, 1u);
+  EXPECT_THAT(description.Get().domains, ElementsAre("local.com"));
+}
+
+TEST_F(ReadingListLocalDataBatchUploaderTest,
+       LocalDescriptionEmptyItemsWhenFeatureDisabled) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndDisableFeature(
+      syncer::kSyncReadingListBatchUploadSelectedItems);
+
+  LoadModel();
+  dual_reading_list_model()->GetLocalOrSyncableModel()->AddOrReplaceEntry(
+      GURL("https://local.com"), "local", reading_list::ADDED_VIA_CURRENT_APP,
+      /*estimated_read_time=*/base::TimeDelta());
+  dual_reading_list_model()->GetAccountModelIfSyncing()->AddOrReplaceEntry(
+      GURL("https://account.com"), "account",
+      reading_list::ADDED_VIA_CURRENT_APP,
+      /*estimated_read_time=*/base::TimeDelta());
+  ReadingListLocalDataBatchUploader uploader(dual_reading_list_model());
+  base::test::TestFuture<syncer::LocalDataDescription> description;
+
+  uploader.GetLocalDataDescription(description.GetCallback());
+
+  EXPECT_EQ(description.Get().type, syncer::DataType::UNSPECIFIED);
+  EXPECT_THAT(description.Get().local_data_models, IsEmpty());
   EXPECT_EQ(description.Get().item_count, 1u);
   EXPECT_EQ(description.Get().domain_count, 1u);
   EXPECT_THAT(description.Get().domains, ElementsAre("local.com"));
diff --git a/components/remote_cocoa/app_shim/remote_cocoa_app_shim_export.h b/components/remote_cocoa/app_shim/remote_cocoa_app_shim_export.h
index bf4039af..6bbaa9b 100644
--- a/components/remote_cocoa/app_shim/remote_cocoa_app_shim_export.h
+++ b/components/remote_cocoa/app_shim/remote_cocoa_app_shim_export.h
@@ -9,11 +9,7 @@
 // RemoteCocoa app shim module can be exported to consumers.
 
 #if defined(COMPONENT_BUILD)
-#if defined(REMOTE_COCOA_APP_SHIM_IMPLEMENTATION)
 #define REMOTE_COCOA_APP_SHIM_EXPORT __attribute__((visibility("default")))
-#else
-#define REMOTE_COCOA_APP_SHIM_EXPORT
-#endif
 
 #else  // defined(COMPONENT_BUILD)
 #define REMOTE_COCOA_APP_SHIM_EXPORT
diff --git a/components/remote_cocoa/browser/remote_cocoa_browser_export.h b/components/remote_cocoa/browser/remote_cocoa_browser_export.h
index 95efd0c7..3759f737 100644
--- a/components/remote_cocoa/browser/remote_cocoa_browser_export.h
+++ b/components/remote_cocoa/browser/remote_cocoa_browser_export.h
@@ -9,11 +9,7 @@
 // RemoteCocoa browser  module can be exported to consumers.
 
 #if defined(COMPONENT_BUILD)
-#if defined(REMOTE_COCOA_BROWSER_IMPLEMENTATION)
 #define REMOTE_COCOA_BROWSER_EXPORT __attribute__((visibility("default")))
-#else
-#define REMOTE_COCOA_BROWSER_EXPORT
-#endif
 
 #else  // defined(COMPONENT_BUILD)
 #define REMOTE_COCOA_BROWSER_EXPORT
diff --git a/components/safe_browsing/content/browser/web_ui/BUILD.gn b/components/safe_browsing/content/browser/web_ui/BUILD.gn
index ee290266..ddc4da5 100644
--- a/components/safe_browsing/content/browser/web_ui/BUILD.gn
+++ b/components/safe_browsing/content/browser/web_ui/BUILD.gn
@@ -14,6 +14,7 @@
 
   deps = [
     "//base",
+    "//components/enterprise/common/proto:connectors_proto_to_value",
     "//components/enterprise/common/proto:connectors_proto",
     "//components/password_manager/core/browser:hash_password_manager",
     "//components/resources:components_resources_grit",
@@ -31,6 +32,7 @@
     "//components/safe_browsing/core/common/proto:csd_proto",
     "//components/safe_browsing/core/common/proto:csd_proto_to_value",
     "//components/safe_browsing/core/common/proto:realtimeapi_proto",
+    "//components/safe_browsing/core/common/proto:realtimeapi_proto_to_value",
     "//components/safe_browsing/core/common/proto:safebrowsingv5_proto",
     "//components/safe_browsing/core/common/proto:safebrowsingv5_proto_to_value",
     "//components/strings:components_strings_grit",
diff --git a/components/safe_browsing/content/browser/web_ui/DEPS b/components/safe_browsing/content/browser/web_ui/DEPS
index 0e3849f..a2e080da 100644
--- a/components/safe_browsing/content/browser/web_ui/DEPS
+++ b/components/safe_browsing/content/browser/web_ui/DEPS
@@ -1,5 +1,6 @@
 include_rules = [
   "+components/enterprise/common/proto/connectors.pb.h",
+  "+components/enterprise/common/proto/connectors.to_value.h",
   "+components/grit/safe_browsing_resources.h",
   "+components/grit/safe_browsing_resources_map.h",
   "+components/password_manager/core/browser/hash_password_manager.h",
diff --git a/components/safe_browsing/content/browser/web_ui/safe_browsing_ui.cc b/components/safe_browsing/content/browser/web_ui/safe_browsing_ui.cc
index 0f37736..4938581 100644
--- a/components/safe_browsing/content/browser/web_ui/safe_browsing_ui.cc
+++ b/components/safe_browsing/content/browser/web_ui/safe_browsing_ui.cc
@@ -38,6 +38,7 @@
 #include "components/safe_browsing/core/common/features.h"
 #include "components/safe_browsing/core/common/proto/csd.pb.h"
 #include "components/safe_browsing/core/common/proto/csd.to_value.h"
+#include "components/safe_browsing/core/common/proto/realtimeapi.to_value.h"
 #include "components/safe_browsing/core/common/proto/safebrowsingv5.pb.h"
 #include "components/safe_browsing/core/common/proto/safebrowsingv5.to_value.h"
 #include "components/safe_browsing/core/common/safe_browsing_prefs.h"
@@ -58,6 +59,7 @@
 
 #if BUILDFLAG(SAFE_BROWSING_DOWNLOAD_PROTECTION) && !BUILDFLAG(IS_ANDROID)
 #include "components/enterprise/common/proto/connectors.pb.h"
+#include "components/enterprise/common/proto/connectors.to_value.h"
 #endif
 
 using base::Time;
@@ -809,44 +811,6 @@
 
 #endif
 
-base::Value::Dict SerializeClientReportingMetadata(
-    const enterprise_connectors::ClientMetadata& client_metadata) {
-  base::Value::Dict client_metadata_dict;
-  if (client_metadata.has_browser()) {
-    base::Value::Dict browser_dict;
-    browser_dict.Set("browser_id", client_metadata.browser().browser_id());
-    browser_dict.Set("user_agent", client_metadata.browser().user_agent());
-    browser_dict.Set("chrome_version",
-                     client_metadata.browser().chrome_version());
-    browser_dict.Set("machine_user", client_metadata.browser().machine_user());
-    client_metadata_dict.Set("browser", std::move(browser_dict));
-  }
-  if (client_metadata.has_device()) {
-    base::Value::Dict device_dict;
-    device_dict.Set("dm_token", client_metadata.device().dm_token());
-    device_dict.Set("client_id", client_metadata.device().client_id());
-    device_dict.Set("os_version", client_metadata.device().os_version());
-    device_dict.Set("os_platform", client_metadata.device().os_platform());
-    device_dict.Set("name", client_metadata.device().name());
-    device_dict.Set("device_fqdn", client_metadata.device().device_fqdn());
-    device_dict.Set("network_name", client_metadata.device().network_name());
-    client_metadata_dict.Set("device", std::move(device_dict));
-  }
-  if (client_metadata.has_profile()) {
-    base::Value::Dict profile_dict;
-    profile_dict.Set("dm_token", client_metadata.profile().dm_token());
-    profile_dict.Set("gaia_email", client_metadata.profile().gaia_email());
-    profile_dict.Set("profile_path", client_metadata.profile().profile_path());
-    profile_dict.Set("profile_name", client_metadata.profile().profile_name());
-    profile_dict.Set("client_id", client_metadata.profile().client_id());
-    client_metadata_dict.Set("profile", std::move(profile_dict));
-  }
-  client_metadata_dict.Set(
-      "is_chrome_os_managed_guest_session",
-      client_metadata.is_chrome_os_managed_guest_session());
-  return client_metadata_dict;
-}
-
 std::string SerializeClientDownloadRequest(const ClientDownloadRequest& cdr) {
   return SerializeJson(proto_to_value::Serialize(cdr));
 }
@@ -1022,61 +986,6 @@
   return result;
 }
 
-base::Value::Dict SerializeRTThreatInfo(
-    const RTLookupResponse::ThreatInfo& threat_info) {
-  base::Value::Dict threat_info_dict;
-
-  threat_info_dict.Set(
-      "threat_type",
-      RTLookupResponse_ThreatInfo_ThreatType_Name(threat_info.threat_type()));
-
-  threat_info_dict.Set("cache_duration_sec",
-                       static_cast<double>(threat_info.cache_duration_sec()));
-
-  threat_info_dict.Set(
-      "verdict_type",
-      RTLookupResponse_ThreatInfo_VerdictType_Name(threat_info.verdict_type()));
-
-  threat_info_dict.Set(
-      "cache_expression_match_type",
-      RTLookupResponse_ThreatInfo_CacheExpressionMatchType_Name(
-          threat_info.cache_expression_match_type()));
-  threat_info_dict.Set("cache_expression_using_match_type",
-                       threat_info.cache_expression_using_match_type());
-
-  if (threat_info.has_matched_url_navigation_rule()) {
-    base::Value::Dict matched_rule;
-    matched_rule.Set("rule_id",
-                     threat_info.matched_url_navigation_rule().rule_id());
-    matched_rule.Set("rule_name",
-                     threat_info.matched_url_navigation_rule().rule_name());
-    matched_rule.Set(
-        "matched_url_category",
-        threat_info.matched_url_navigation_rule().matched_url_category());
-
-    if (threat_info.matched_url_navigation_rule().has_custom_message()) {
-      base::Value::List message_segments;
-      for (const auto& segment : threat_info.matched_url_navigation_rule()
-                                     .custom_message()
-                                     .message_segments()) {
-        base::Value::Dict segment_value;
-        if (segment.has_text()) {
-          segment_value.Set("text", segment.text());
-        }
-        if (segment.has_link()) {
-          segment_value.Set("link", segment.link());
-        }
-        message_segments.Append(std::move(segment_value));
-      }
-      matched_rule.Set("message_segments", std::move(message_segments));
-    }
-    threat_info_dict.Set("matched_url_navigation_rule",
-                         std::move(matched_rule));
-  }
-
-  return threat_info_dict;
-}
-
 #if BUILDFLAG(IS_ANDROID)
 // This serializes the internal::ReferringAppInfo struct (not to be confused
 // with the protobuf message ReferringAppInfo), which contains intermediate
@@ -1108,72 +1017,13 @@
 }
 
 std::string SerializeURTLookupPing(const URTLookupRequest& ping) {
-  base::Value::Dict request_dict;
-  const RTLookupRequest& request = ping.request;
-
-  request_dict.Set("url", request.url());
-  request_dict.Set("population",
-                   proto_to_value::Serialize(request.population()));
+  base::Value::Dict request_dict = proto_to_value::Serialize(ping.request);
   request_dict.Set("scoped_oauth_token", ping.token);
-  request_dict.Set("dm_token", request.dm_token());
-  request_dict.Set("profile_dm_token", request.profile_dm_token());
-  request_dict.Set("browser_dm_token", request.browser_dm_token());
-  request_dict.Set("email", request.email());
-  if (request.has_client_reporting_metadata()) {
-    request_dict.Set(
-        "client_reporting_metadata",
-        SerializeClientReportingMetadata(request.client_reporting_metadata()));
-  }
-
-  request_dict.Set("lookup_type",
-                   RTLookupRequest_LookupType_Name(request.lookup_type()));
-
-  request_dict.Set("version", request.version());
-
-  request_dict.Set("os", RTLookupRequest_OSType_Name(request.os_type()));
-
-  base::Value::List local_ips;
-  for (const std::string& local_ip : request.local_ips()) {
-    local_ips.Append(local_ip);
-  }
-  request_dict.Set("local_ips", std::move(local_ips));
-
-  base::Value::List referrer_chain;
-  for (const auto& referrer_chain_entry : request.referrer_chain()) {
-    referrer_chain.Append(proto_to_value::Serialize(referrer_chain_entry));
-  }
-  request_dict.Set("referrer_chain", std::move(referrer_chain));
-#if BUILDFLAG(IS_ANDROID)
-  if (request.has_referring_app_info()) {
-    request_dict.Set("referring_app_info",
-                     proto_to_value::Serialize(request.referring_app_info()));
-  }
-#endif
-
   return SerializeJson(request_dict);
 }
 
 std::string SerializeURTLookupResponse(const RTLookupResponse& response) {
-  base::Value::Dict response_dict;
-
-  base::Value::List threat_info_list;
-  for (const RTLookupResponse::ThreatInfo& threat_info :
-       response.threat_info()) {
-    threat_info_list.Append(SerializeRTThreatInfo(threat_info));
-  }
-  response_dict.Set("threat_infos", std::move(threat_info_list));
-
-  response_dict.Set(
-      "client_side_detection_type",
-      ClientSideDetectionType_Name(response.client_side_detection_type()));
-
-  base::Value::List url_categories_list;
-  for (const std::string& url_category : response.url_categories()) {
-    url_categories_list.Append(url_category);
-  }
-  response_dict.Set("url_categories", std::move(url_categories_list));
-
-  return SerializeJson(response_dict);
+  return SerializeJson(proto_to_value::Serialize(response));
 }
 
 std::string SerializeHPRTLookupPing(const HPRTLookupRequest& ping) {
@@ -1228,185 +1078,16 @@
     const std::string& upload_info,
     const std::string& upload_url,
     const enterprise_connectors::ContentAnalysisRequest& request) {
-  base::Value::Dict request_dict;
-
-  request_dict.Set(per_profile_request ? "profile_token" : "device_token",
-                   request.device_token());
-  request_dict.Set("blocking", request.blocking());
-  request_dict.Set("analysis_connector",
-                   enterprise_connectors::AnalysisConnector_Name(
-                       request.analysis_connector()));
-  request_dict.Set("reason",
-                   enterprise_connectors::ContentAnalysisRequest_Reason_Name(
-                       request.reason()));
-  base::Value::List local_ips;
-  for (const std::string& local_ip : request.local_ips()) {
-    local_ips.Append(local_ip);
-  }
-  request_dict.Set("local_ips", std::move(local_ips));
-
-  if (request.has_request_data()) {
-    base::Value::Dict request_data;
-    request_data.Set("url", request.request_data().url());
-    request_data.Set("filename", request.request_data().filename());
-    request_data.Set("digest", request.request_data().digest());
-    if (request.request_data().has_csd()) {
-      std::string csd_base64 =
-          base::Base64Encode(request.request_data().csd().SerializeAsString());
-      request_data.Set("csd", std::move(csd_base64));
-    }
-    if (request.request_data().referrer_chain_size() > 0) {
-      base::Value::List referrers;
-      for (const auto& referrer_chain_entry :
-           request.request_data().referrer_chain()) {
-        referrers.Append(proto_to_value::Serialize(referrer_chain_entry));
-      }
-      request_data.Set("referrers", std::move(referrers));
-    }
-    request_data.Set("content_type", request.request_data().content_type());
-    request_data.Set("tab_url", request.request_data().tab_url());
-    request_data.Set("source", request.request_data().source());
-    request_data.Set("destination", request.request_data().destination());
-    request_data.Set("email", request.request_data().email());
-    request_data.Set("tab_title", request.request_data().tab_title());
-    request_data.Set("content_area_account_email",
-                     request.request_data().content_area_account_email());
-
-    if (request.request_data().has_print_metadata()) {
-      base::Value::Dict print_metadata;
-      print_metadata.Set(
-          "printer_name",
-          request.request_data().print_metadata().printer_name());
-      print_metadata.Set(
-          "printer_type",
-          enterprise_connectors::ContentMetaData::PrintMetadata::
-              PrinterType_Name(
-                  request.request_data().print_metadata().printer_type()));
-      request_data.Set("print_metadata", std::move(print_metadata));
-    }
-
-    if (request.request_data().has_copied_text_source()) {
-      base::Value::Dict copied_text_source;
-      copied_text_source.Set("url",
-                             request.request_data().copied_text_source().url());
-      copied_text_source.Set(
-          "context",
-          enterprise_connectors::ContentMetaData::CopiedTextSource::
-              CopiedTextSourceType_Name(
-                  request.request_data().copied_text_source().context()));
-      request_data.Set("copied_text_source", std::move(copied_text_source));
-    }
-
-    request_dict.Set("request_data", std::move(request_data));
-  }
-
-  if (request.has_client_metadata()) {
-    base::Value::Dict metadata;
-
-    if (request.client_metadata().has_browser()) {
-      const auto& browser = request.client_metadata().browser();
-      base::Value::Dict browser_metadata;
-      browser_metadata.Set("browser_id", browser.browser_id());
-      browser_metadata.Set("user_agent", browser.user_agent());
-      browser_metadata.Set("chrome_version", browser.chrome_version());
-      browser_metadata.Set("machine_user", browser.machine_user());
-      metadata.Set("browser", std::move(browser_metadata));
-    }
-
-    if (request.client_metadata().has_device()) {
-      base::Value::Dict device_metadata;
-      const auto& device = request.client_metadata().device();
-      device_metadata.Set("dm_token", device.dm_token());
-      device_metadata.Set("client_id", device.client_id());
-      device_metadata.Set("os_version", device.os_version());
-      device_metadata.Set("os_platform", device.os_platform());
-      device_metadata.Set("name", device.name());
-      device_metadata.Set("device_fqdn", device.device_fqdn());
-      device_metadata.Set("network_name", device.network_name());
-      metadata.Set("device", std::move(device_metadata));
-    }
-
-    if (request.client_metadata().has_profile()) {
-      base::Value::Dict profile_metadata;
-      const auto& profile = request.client_metadata().profile();
-      profile_metadata.Set("dm_token", profile.dm_token());
-      profile_metadata.Set("gaia_email", profile.gaia_email());
-      profile_metadata.Set("profile_path", profile.profile_path());
-      profile_metadata.Set("profile_name", profile.profile_name());
-      profile_metadata.Set("client_id", profile.client_id());
-      metadata.Set("profile", std::move(profile_metadata));
-    }
-
-    request_dict.Set("client_metadata", std::move(metadata));
-  }
-
-  base::Value::List tags;
-  for (const std::string& tag : request.tags()) {
-    tags.Append(tag);
-  }
-  request_dict.Set("tags", std::move(tags));
-  request_dict.Set("request_token", request.request_token());
+  base::Value::Dict request_dict = proto_to_value::Serialize(request);
   request_dict.Set("access_token", access_token_truncated);
   request_dict.Set("upload_info", upload_info);
-  request_dict.Set(
-      "is_chrome_os_managed_guest_session",
-      request.client_metadata().is_chrome_os_managed_guest_session());
   request_dict.Set("upload_url", upload_url);
-
   return SerializeJson(request_dict);
 }
 
 std::string SerializeContentAnalysisResponse(
     const enterprise_connectors::ContentAnalysisResponse& response) {
-  base::Value::Dict response_dict;
-
-  response_dict.Set("token", response.request_token());
-
-  base::Value::List result_values;
-  for (const auto& result : response.results()) {
-    base::Value::Dict result_value;
-    result_value.Set(
-        "status",
-        enterprise_connectors::ContentAnalysisResponse_Result_Status_Name(
-            result.status()));
-    result_value.Set("tag", result.tag());
-
-    base::Value::List triggered_rules;
-    for (const auto& rule : result.triggered_rules()) {
-      base::Value::Dict rule_value;
-
-      rule_value.Set(
-          "action",
-          enterprise_connectors::
-              ContentAnalysisResponse_Result_TriggeredRule_Action_Name(
-                  rule.action()));
-      rule_value.Set("rule_name", rule.rule_name());
-      rule_value.Set("rule_id", rule.rule_id());
-      rule_value.Set("url_category", rule.url_category());
-
-      if (rule.has_custom_rule_message()) {
-        base::Value::List message_segments;
-        for (const auto& segment :
-             rule.custom_rule_message().message_segments()) {
-          base::Value::Dict segment_value;
-          if (segment.has_text()) {
-            segment_value.Set("text", segment.text());
-          }
-          if (segment.has_link()) {
-            segment_value.Set("link", segment.link());
-          }
-          message_segments.Append(std::move(segment_value));
-        }
-        rule_value.Set("message_segments", std::move(message_segments));
-      }
-      triggered_rules.Append(std::move(rule_value));
-    }
-    result_value.Set("triggered_rules", std::move(triggered_rules));
-    result_values.Append(std::move(result_value));
-  }
-  response_dict.Set("results", std::move(result_values));
-
-  return SerializeJson(response_dict);
+  return SerializeJson(proto_to_value::Serialize(response));
 }
 
 base::Value::Dict SerializeDeepScanDebugData(const std::string& token,
diff --git a/components/safe_browsing/core/common/proto/BUILD.gn b/components/safe_browsing/core/common/proto/BUILD.gn
index e3af61bb6..f73e0ee8 100644
--- a/components/safe_browsing/core/common/proto/BUILD.gn
+++ b/components/safe_browsing/core/common/proto/BUILD.gn
@@ -39,6 +39,16 @@
   ]
 }
 
+proto_to_value("realtimeapi_proto_to_value") {
+  sources = [ "realtimeapi.proto" ]
+  deps = [
+    ":csd_proto",
+    ":csd_proto_to_value",
+    "//components/enterprise/common/proto:connectors_proto",
+    "//components/enterprise/common/proto:connectors_proto_to_value",
+  ]
+}
+
 fuzzable_proto_library("client_model_proto") {
   proto_in_dir = "//"
   sources = [ "client_model.proto" ]
diff --git a/components/safe_browsing/core/common/proto_to_value/BUILD.gn b/components/safe_browsing/core/common/proto_to_value/BUILD.gn
index a80921e..8783f173 100644
--- a/components/safe_browsing/core/common/proto_to_value/BUILD.gn
+++ b/components/safe_browsing/core/common/proto_to_value/BUILD.gn
@@ -11,3 +11,18 @@
     ]
   }
 }
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [ "to_value_plugin_unittest.cc" ]
+
+  deps = [
+    "//base",
+    "//base/test:test_support",
+    "//components/safe_browsing/core/common/proto_to_value/test_proto:test_proto",
+    "//components/safe_browsing/core/common/proto_to_value/test_proto:test_proto_to_value",
+    "//testing/gmock",
+    "//testing/gtest",
+    "//third_party/protobuf:protobuf_lite",
+  ]
+}
diff --git a/components/safe_browsing/core/common/proto_to_value/proto_to_value.gni b/components/safe_browsing/core/common/proto_to_value/proto_to_value.gni
index 8455976..47b6c44 100644
--- a/components/safe_browsing/core/common/proto_to_value/proto_to_value.gni
+++ b/components/safe_browsing/core/common/proto_to_value/proto_to_value.gni
@@ -17,7 +17,13 @@
 template("proto_to_value") {
   proto_library("${target_name}") {
     proto_in_dir = "//"
+
     sources = invoker.sources
+
+    if (defined(invoker.deps)) {
+      deps = invoker.deps
+    }
+
     generator_plugin_label =
         "//components/safe_browsing/core/common/proto_to_value:to_value_plugin"
     generator_plugin_suffix = ".to_value"
diff --git a/components/safe_browsing/core/common/proto_to_value/test_proto/BUILD.gn b/components/safe_browsing/core/common/proto_to_value/test_proto/BUILD.gn
new file mode 100644
index 0000000..359afaa74
--- /dev/null
+++ b/components/safe_browsing/core/common/proto_to_value/test_proto/BUILD.gn
@@ -0,0 +1,22 @@
+# 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(
+    "//components/safe_browsing/core/common/proto_to_value/proto_to_value.gni")
+import("//third_party/protobuf/proto_library.gni")
+
+proto_library("test_proto") {
+  proto_in_dir = "//"
+  sources = [
+    "test_proto.proto",
+    "test_proto_dependency.proto",
+  ]
+}
+
+proto_to_value("test_proto_to_value") {
+  sources = [
+    "test_proto.proto",
+    "test_proto_dependency.proto",
+  ]
+}
diff --git a/components/safe_browsing/core/common/proto_to_value/test_proto/test_proto.proto b/components/safe_browsing/core/common/proto_to_value/test_proto/test_proto.proto
new file mode 100644
index 0000000..0a36d46
--- /dev/null
+++ b/components/safe_browsing/core/common/proto_to_value/test_proto/test_proto.proto
@@ -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.
+
+syntax = "proto3";
+
+package proto_to_value;
+
+option optimize_for = LITE_RUNTIME;
+
+import "components/safe_browsing/core/common/proto_to_value/test_proto/test_proto_dependency.proto";
+
+message TestMessage {
+  double double_field = 1;
+
+  int32 int32_field = 2;
+
+  message NestedMessage {
+    int32 int32_field = 1;
+  }
+
+  NestedMessage nested_message_field = 3;
+
+  enum TestEnum {
+    UNKNOWN = 0;
+    ENUM_A = 1;
+    ENUM_B = 2;
+  }
+
+  TestEnum enum_field = 4;
+
+  repeated int32 repeated_int32_field = 5;
+
+  string string_field = 6;
+
+  bytes bytes_field = 7;
+
+  DependencyMessage dependency_message = 8;
+}
diff --git a/components/safe_browsing/core/common/proto_to_value/test_proto/test_proto_dependency.proto b/components/safe_browsing/core/common/proto_to_value/test_proto/test_proto_dependency.proto
new file mode 100644
index 0000000..c0de017
--- /dev/null
+++ b/components/safe_browsing/core/common/proto_to_value/test_proto/test_proto_dependency.proto
@@ -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.
+
+syntax = "proto3";
+
+option optimize_for = LITE_RUNTIME;
+
+package proto_to_value;
+
+message DependencyMessage {
+  int32 int32_field = 1;
+}
diff --git a/components/safe_browsing/core/common/proto_to_value/to_value_plugin.cc b/components/safe_browsing/core/common/proto_to_value/to_value_plugin.cc
index dccf977..dd5c4bbe 100644
--- a/components/safe_browsing/core/common/proto_to_value/to_value_plugin.cc
+++ b/components/safe_browsing/core/common/proto_to_value/to_value_plugin.cc
@@ -31,13 +31,7 @@
                 const std::string& options,
                 GeneratorContext* context,
                 std::string* error) const override {
-#if BUILDFLAG(IS_WIN)
-    base::FilePath base_file_path =
-        base::FilePath(base::ASCIIToWide(file->name())).RemoveExtension();
-#else
-    base::FilePath base_file_path =
-        base::FilePath(file->name()).RemoveExtension();
-#endif
+    base::FilePath base_file_path = ToValueFilePath(file->name());
     base::FilePath h_file_path =
         base_file_path.AddExtension(FILE_PATH_LITERAL("to_value.h"));
     base::FilePath cc_file_path =
@@ -79,6 +73,14 @@
     cc_printer.Print("#include \"base/base64.h\"\n");
     cc_printer.Print("#include \"$p$\"\n\n", "p", h_file_path.AsUTF8Unsafe());
 
+    for (int i = 0; i < file->dependency_count(); i++) {
+      base::FilePath include_path =
+          ToValueFilePath(file->dependency(i)->name())
+              .AddExtension(FILE_PATH_LITERAL("to_value.h"));
+      cc_printer.Print("#include \"$p$\"\n", "p", include_path.AsUTF8Unsafe());
+    }
+    cc_printer.Print("\n");
+
     cc_printer.Print("namespace proto_to_value {\n\n");
     for (int i = 0; i < file->message_type_count(); i++) {
       if (!PrintFunctionDefinition(*file->message_type(i), &cc_printer,
@@ -93,6 +95,14 @@
   }
 
  private:
+  base::FilePath ToValueFilePath(std::string_view file_name) const {
+#if BUILDFLAG(IS_WIN)
+    return base::FilePath(base::ASCIIToWide(file_name)).RemoveExtension();
+#else
+    return base::FilePath(file_name).RemoveExtension();
+#endif
+  }
+
   std::string CppFullName(std::string_view full_name) const {
     std::string ret;
     CHECK(base::ReplaceChars(full_name, ".", "::", &ret));
@@ -150,6 +160,15 @@
         printer->Print("(message.$f$()));\n", "f", field_name);
         printer->Outdent();
         printer->Print("}\n");
+      } else if (field->type() == FieldDescriptor::Type::TYPE_STRING ||
+                 field->type() == FieldDescriptor::Type::TYPE_BYTES) {
+        printer->Print("if (!message.$f$().empty()) {\n", "f", field_name);
+        printer->Indent();
+        printer->Print("dict.Set(\"$f$\", ", "f", field_name);
+        PrintFieldToValue(*field, printer);
+        printer->Print("(message.$f$()));\n", "f", field_name);
+        printer->Outdent();
+        printer->Print("}\n");
       } else {
         printer->Print("dict.Set(\"$f$\", ", "f", field_name);
         PrintFieldToValue(*field, printer);
diff --git a/components/safe_browsing/core/common/proto_to_value/to_value_plugin_unittest.cc b/components/safe_browsing/core/common/proto_to_value/to_value_plugin_unittest.cc
new file mode 100644
index 0000000..23aaba8b
--- /dev/null
+++ b/components/safe_browsing/core/common/proto_to_value/to_value_plugin_unittest.cc
@@ -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.
+
+#include "base/test/values_test_util.h"
+#include "components/safe_browsing/core/common/proto_to_value/test_proto/test_proto.pb.h"
+#include "components/safe_browsing/core/common/proto_to_value/test_proto/test_proto.to_value.h"
+#include "components/safe_browsing/core/common/proto_to_value/test_proto/test_proto_dependency.pb.h"
+#include "components/safe_browsing/core/common/proto_to_value/test_proto/test_proto_dependency.to_value.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace proto_to_value {
+
+TEST(ProtoToValueTest, BasicField) {
+  TestMessage message;
+  message.set_double_field(1.0);
+  message.set_int32_field(2);
+  message.mutable_nested_message_field()->set_int32_field(3);
+  message.set_enum_field(TestMessage::ENUM_B);
+  message.set_string_field("abc");
+  message.set_bytes_field("\x01\x02\x03");
+
+  EXPECT_EQ(Serialize(message), base::test::ParseJson(R"!({
+    "double_field": 1.0,
+    "int32_field": 2,
+    "nested_message_field": {
+      "int32_field": 3
+    },
+    "enum_field": "ENUM_B",
+    "string_field": "abc",
+    "bytes_field": "AQID",
+})!"));
+}
+
+TEST(ProtoToValueTest, RepeatedField) {
+  TestMessage message;
+
+  // Default fields only if empty
+  EXPECT_EQ(Serialize(message), base::test::ParseJson(R"!({
+    "double_field": 0.0,
+    "int32_field": 0,
+    "enum_field": "UNKNOWN",
+})!"));
+
+  message.add_repeated_int32_field(1);
+  message.add_repeated_int32_field(2);
+  message.add_repeated_int32_field(3);
+
+  EXPECT_EQ(Serialize(message), base::test::ParseJson(R"!({
+    "double_field": 0.0,
+    "int32_field": 0,
+    "enum_field": "UNKNOWN",
+    "repeated_int32_field": [1, 2, 3],
+})!"));
+}
+
+TEST(ProtoToValueTest, DepedentFile) {
+  TestMessage message;
+  message.mutable_dependency_message()->set_int32_field(4);
+
+  EXPECT_EQ(Serialize(message), base::test::ParseJson(R"!({
+    "double_field": 0.0,
+    "int32_field": 0,
+    "enum_field": "UNKNOWN",
+    "dependency_message": {
+      "int32_field": 4,
+    },
+})!"));
+}
+
+}  // namespace proto_to_value
diff --git a/components/services/storage/dom_storage/async_dom_storage_database.cc b/components/services/storage/dom_storage/async_dom_storage_database.cc
index b8b1cef3..c173270 100644
--- a/components/services/storage/dom_storage/async_dom_storage_database.cc
+++ b/components/services/storage/dom_storage/async_dom_storage_database.cc
@@ -12,6 +12,7 @@
 #include <string>
 #include <utility>
 
+#include "base/debug/alias.h"
 #include "base/feature_list.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
@@ -81,13 +82,19 @@
                       [](std::vector<BatchDatabaseTask> tasks,
                          const DomStorageDatabase& db) {
                         leveldb::WriteBatch batch;
+                        // TODO(crbug.com/40245293): Remove this after debugging
+                        // is complete.
+                        size_t batch_task_count = tasks.size();
                         size_t iteration_count = 0;
+                        size_t current_batch_size = 0;
+                        base::debug::Alias(&batch_task_count);
+                        base::debug::Alias(&iteration_count);
+                        base::debug::Alias(&current_batch_size);
                         for (auto& task : tasks) {
                           iteration_count++;
-                          size_t current_batch_size = batch.ApproximateSize();
                           std::move(task).Run(&batch, db);
-                          size_t new_batch_size = batch.ApproximateSize();
-                          size_t growth = new_batch_size - current_batch_size;
+                          size_t growth =
+                              batch.ApproximateSize() - current_batch_size;
                           base::UmaHistogramCustomCounts(
                               "Storage.DomStorage."
                               "BatchTaskGrowthSizeBytes",
@@ -97,7 +104,7 @@
                             size_t target_batch_size =
                                 batch_size_mb * 1024 * 1024;
                             if (current_batch_size < target_batch_size &&
-                                new_batch_size >= target_batch_size) {
+                                batch.ApproximateSize() >= target_batch_size) {
                               base::UmaHistogramCounts10000(
                                   base::StringPrintf("Storage.DomStorage."
                                                      "IterationsToReach%zuMB",
diff --git a/components/session_manager/session_manager_export.h b/components/session_manager/session_manager_export.h
index ba11e2ae..406679a 100644
--- a/components/session_manager/session_manager_export.h
+++ b/components/session_manager/session_manager_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(SESSION_EXPORT)
 
 #else  // defined(WIN32)
-#if defined(SESSION_IMPLEMENTATION)
 #define SESSION_EXPORT __attribute__((visibility("default")))
-#else
-#define SESSION_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/components/sessions/core/sessions_export.h b/components/sessions/core/sessions_export.h
index 097301c0..4978bb5 100644
--- a/components/sessions/core/sessions_export.h
+++ b/components/sessions/core/sessions_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(SESSIONS_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(SESSIONS_IMPLEMENTATION)
 #define SESSIONS_EXPORT __attribute__((visibility("default")))
-#else
-#define SESSIONS_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/components/sync/base/features.cc b/components/sync/base/features.cc
index 133dbd5..e7a964b4 100644
--- a/components/sync/base/features.cc
+++ b/components/sync/base/features.cc
@@ -112,6 +112,11 @@
              base::FEATURE_DISABLED_BY_DEFAULT);
 #endif  // BUILDFLAG(IS_IOS) || BUILDFLAG(IS_ANDROID)
 
+// Enabled by default, intended as a kill switch.
+BASE_FEATURE(kSyncReadingListBatchUploadSelectedItems,
+             "SyncReadingListBatchUploadSelectedItems",
+             base::FEATURE_ENABLED_BY_DEFAULT);
+
 BASE_FEATURE(kSeparateLocalAndAccountThemes,
              "SeparateLocalAndAccountThemes",
              base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/components/sync/base/features.h b/components/sync/base/features.h
index b659e83..e623c1b 100644
--- a/components/sync/base/features.h
+++ b/components/sync/base/features.h
@@ -111,6 +111,19 @@
 BASE_DECLARE_FEATURE(kMigrateAccountPrefs);
 #endif  // BUILDFLAG(IS_IOS) || BUILDFLAG(IS_ANDROID)
 
+// If enabled, support displaying and uploading individual Reading List items in
+// the Batch Upload UI.
+//
+// Batch Upload of all items is supported regardless of this feature flag.
+//
+// On Windows/Mac/Linux: this flag only affects behavior if the
+// `syncer::kReadingListEnableSyncTransportModeUponSignIn` feature is also
+// enabled.
+//
+// On Android: this flag does not affect user-visiable behavior, but does enable
+// new code paths.
+BASE_DECLARE_FEATURE(kSyncReadingListBatchUploadSelectedItems);
+
 // If enabled, distinguishes between local and account themes.
 BASE_DECLARE_FEATURE(kSeparateLocalAndAccountThemes);
 
diff --git a/components/sync/service/local_data_description.h b/components/sync/service/local_data_description.h
index 3973f0a..878a037 100644
--- a/components/sync/service/local_data_description.h
+++ b/components/sync/service/local_data_description.h
@@ -29,6 +29,8 @@
   using DataId = std::variant<
       // BOOKMARKS.
       int64_t,  // bookmarks::BookmarkNode::id()
+      // READING_LIST.
+      GURL,
       // CONTACT_INFO, THEMES.
       std::string,
       // PASSWORDS.
diff --git a/components/test/data/autofill/heuristics-json/internal b/components/test/data/autofill/heuristics-json/internal
index dbd13cb..cdb4965 160000
--- a/components/test/data/autofill/heuristics-json/internal
+++ b/components/test/data/autofill/heuristics-json/internal
@@ -1 +1 @@
-Subproject commit dbd13cb361518dac753112204eacd1ce9e1fab84
+Subproject commit cdb496540ac02f8d98cfc04097e3e10a197f18f4
diff --git a/components/tracing/tracing_export.h b/components/tracing/tracing_export.h
index 357d8c09..51757111 100644
--- a/components/tracing/tracing_export.h
+++ b/components/tracing/tracing_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(TRACING_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(TRACING_IMPLEMENTATION)
 #define TRACING_EXPORT __attribute__((visibility("default")))
-#else
-#define TRACING_EXPORT
-#endif // defined(TRACING_IMPLEMENTATION)
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/components/ui_devtools/devtools_export.h b/components/ui_devtools/devtools_export.h
index 46dfc805..ab39350 100644
--- a/components/ui_devtools/devtools_export.h
+++ b/components/ui_devtools/devtools_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(UI_DEVTOOLS_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(UI_DEVTOOLS_IMPLEMENTATION)
 #define UI_DEVTOOLS_EXPORT __attribute__((visibility("default")))
-#else
-#define UI_DEVTOOLS_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/components/url_matcher/url_matcher_export.h b/components/url_matcher/url_matcher_export.h
index 44504582..54ff967 100644
--- a/components/url_matcher/url_matcher_export.h
+++ b/components/url_matcher/url_matcher_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(URL_MATCHER_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(URL_MATCHER_IMPLEMENTATION)
 #define URL_MATCHER_EXPORT __attribute__((visibility("default")))
-#else
-#define URL_MATCHER_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/components/user_manager/BUILD.gn b/components/user_manager/BUILD.gn
index c6fadcd7a..9c7ddf9 100644
--- a/components/user_manager/BUILD.gn
+++ b/components/user_manager/BUILD.gn
@@ -106,6 +106,7 @@
       "//chromeos/ash/components/dbus/cryptohome",
       "//chromeos/ash/components/dbus/cryptohome:cryptohome_proto",
       "//chromeos/ash/components/dbus/userdataauth",
+      "//chromeos/ash/components/policy/device_local_account",
       "//chromeos/ash/components/settings",
       "//components/policy/core/common",
       "//components/prefs",
diff --git a/components/user_manager/DEPS b/components/user_manager/DEPS
index 0bad503..553868c 100644
--- a/components/user_manager/DEPS
+++ b/components/user_manager/DEPS
@@ -1,6 +1,7 @@
 include_rules = [
   "+ash/constants",
   "+chromeos/ash/components/install_attributes",
+  "+chromeos/ash/components/policy/device_local_account",
   "+chromeos/ash/components/settings",
   "+components/account_id/account_id.h",
   "+components/pref_registry",
diff --git a/components/user_manager/test_helper.cc b/components/user_manager/test_helper.cc
index 1411c9744..f024d246 100644
--- a/components/user_manager/test_helper.cc
+++ b/components/user_manager/test_helper.cc
@@ -7,8 +7,8 @@
 #include "base/check_deref.h"
 #include "chromeos/ash/components/cryptohome/cryptohome_parameters.h"
 #include "chromeos/ash/components/dbus/userdataauth/userdataauth_client.h"
+#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "components/account_id/account_id.h"
-#include "components/policy/core/common/device_local_account_type.h"
 #include "components/prefs/scoped_user_pref_update.h"
 #include "components/user_manager/known_user.h"
 #include "components/user_manager/user_manager.h"
diff --git a/components/user_manager/user_manager_export.h b/components/user_manager/user_manager_export.h
index f336d37f..51f149a 100644
--- a/components/user_manager/user_manager_export.h
+++ b/components/user_manager/user_manager_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(USER_MANAGER_EXPORT)
 
 #else  // defined(WIN32)
-#if defined(USER_MANAGER_IMPLEMENTATION)
 #define USER_MANAGER_EXPORT __attribute__((visibility("default")))
-#else
-#define USER_MANAGER_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/components/user_prefs/user_prefs_export.h b/components/user_prefs/user_prefs_export.h
index 736594374..a0be0f6b 100644
--- a/components/user_prefs/user_prefs_export.h
+++ b/components/user_prefs/user_prefs_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(USER_PREFS_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(USER_PREFS_IMPLEMENTATION)
 #define USER_PREFS_EXPORT __attribute__((visibility("default")))
-#else
-#define USER_PREFS_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/components/variations/cros_evaluate_seed/early_boot_safe_seed.cc b/components/variations/cros_evaluate_seed/early_boot_safe_seed.cc
index c2b664c..6eb23e3 100644
--- a/components/variations/cros_evaluate_seed/early_boot_safe_seed.cc
+++ b/components/variations/cros_evaluate_seed/early_boot_safe_seed.cc
@@ -22,11 +22,9 @@
 void EarlyBootSafeSeed::SetFetchTime(const base::Time& fetch_time) {}
 
 int EarlyBootSafeSeed::GetMilestone() const {
-  return safe_seed_details_.milestone();
+  return GetCompressedSeed().milestone;
 }
 
-void EarlyBootSafeSeed::SetMilestone(int milestone) {}
-
 base::Time EarlyBootSafeSeed::GetTimeForStudyDateChecks() const {
   return base::Time::FromDeltaSinceWindowsEpoch(
       base::Milliseconds(safe_seed_details_.date()));
@@ -39,13 +37,11 @@
   return {
       .storage_format = StoredSeed::StorageFormat::kCompressedAndBase64Encoded,
       .data = safe_seed_details_.b64_compressed_data(),
-      .signature = safe_seed_details_.signature()};
+      .signature = safe_seed_details_.signature(),
+      .milestone = safe_seed_details_.milestone()};
 }
 
-void EarlyBootSafeSeed::SetCompressedSeed(
-    const std::string& safe_compressed,
-    const std::string& base64_safe_compressed,
-    const std::string& signature) {}
+void EarlyBootSafeSeed::SetCompressedSeed(ValidatedSeedInfo seed_info) {}
 
 std::string EarlyBootSafeSeed::GetLocale() const {
   return safe_seed_details_.locale();
diff --git a/components/variations/cros_evaluate_seed/early_boot_safe_seed.h b/components/variations/cros_evaluate_seed/early_boot_safe_seed.h
index e86d554..a1def44 100644
--- a/components/variations/cros_evaluate_seed/early_boot_safe_seed.h
+++ b/components/variations/cros_evaluate_seed/early_boot_safe_seed.h
@@ -39,15 +39,12 @@
   void SetFetchTime(const base::Time& fetch_time) override;
 
   int GetMilestone() const override;
-  void SetMilestone(int milestone) override;
 
   base::Time GetTimeForStudyDateChecks() const override;
   void SetTimeForStudyDateChecks(const base::Time& safe_seed_time) override;
 
   StoredSeed GetCompressedSeed() const override;
-  void SetCompressedSeed(const std::string& safe_compressed,
-                         const std::string& base64_safe_compressed,
-                         const std::string& signature) override;
+  void SetCompressedSeed(ValidatedSeedInfo seed_info) override;
 
   std::string GetLocale() const override;
   void SetLocale(const std::string& locale) override;
diff --git a/components/variations/cros_evaluate_seed/early_boot_safe_seed_unittest.cc b/components/variations/cros_evaluate_seed/early_boot_safe_seed_unittest.cc
index f2873ba..7045a25 100644
--- a/components/variations/cros_evaluate_seed/early_boot_safe_seed_unittest.cc
+++ b/components/variations/cros_evaluate_seed/early_boot_safe_seed_unittest.cc
@@ -42,10 +42,6 @@
   EXPECT_EQ(early_boot_safe_seed.GetMilestone(), 100);
 
   // Should not change.
-  early_boot_safe_seed.SetMilestone(101);
-  EXPECT_EQ(early_boot_safe_seed.GetMilestone(), 100);
-
-  // Still should not change.
   early_boot_safe_seed.ClearState();
   EXPECT_EQ(early_boot_safe_seed.GetMilestone(), 100);
 }
@@ -86,7 +82,11 @@
   EXPECT_EQ(early_boot_safe_seed.GetCompressedSeed().signature, "signature");
 
   // Should not change.
-  early_boot_safe_seed.SetCompressedSeed("data", "base64_data", "asdf");
+  early_boot_safe_seed.SetCompressedSeed(
+      ValidatedSeedInfo{.compressed_seed_data = "data",
+                        .base64_seed_data = "base64_data",
+                        .signature = "asdf",
+                        .milestone = 100});
   EXPECT_EQ(early_boot_safe_seed.GetCompressedSeed().signature, "signature");
 }
 
@@ -131,9 +131,12 @@
   EarlyBootSafeSeed early_boot_safe_seed(details);
 
   early_boot_safe_seed.SetFetchTime(base::Time::Now());
-  early_boot_safe_seed.SetMilestone(100);
   early_boot_safe_seed.SetTimeForStudyDateChecks(base::Time::Now());
-  early_boot_safe_seed.SetCompressedSeed("data", "base64_data", "signature");
+  early_boot_safe_seed.SetCompressedSeed(
+      ValidatedSeedInfo{.compressed_seed_data = "data",
+                        .base64_seed_data = "base64_data",
+                        .signature = "signature",
+                        .milestone = 100});
   early_boot_safe_seed.SetLocale("locale");
   early_boot_safe_seed.SetPermanentConsistencyCountry("us");
   early_boot_safe_seed.SetSessionConsistencyCountry("us");
diff --git a/components/variations/seed_reader_writer.cc b/components/variations/seed_reader_writer.cc
index 2c2c6ce..39dcfa2 100644
--- a/components/variations/seed_reader_writer.cc
+++ b/components/variations/seed_reader_writer.cc
@@ -87,11 +87,13 @@
 
 const SeedFieldsPrefs kRegularSeedFieldsPrefs = {
     .seed = prefs::kVariationsCompressedSeed,
-    .signature = prefs::kVariationsSeedSignature};
+    .signature = prefs::kVariationsSeedSignature,
+    .milestone = prefs::kVariationsSeedMilestone};
 
 const SeedFieldsPrefs kSafeSeedFieldsPrefs = {
     .seed = prefs::kVariationsSafeCompressedSeed,
-    .signature = prefs::kVariationsSafeSeedSignature};
+    .signature = prefs::kVariationsSafeSeedSignature,
+    .milestone = prefs::kVariationsSafeSeedMilestone};
 
 SeedReaderWriter::SeedReaderWriter(
     PrefService* local_state,
@@ -125,15 +127,12 @@
   }
 }
 
-void SeedReaderWriter::StoreValidatedSeedInfo(
-    std::string_view compressed_seed_data,
-    std::string_view base64_seed_data,
-    std::string_view signature) {
+void SeedReaderWriter::StoreValidatedSeedInfo(ValidatedSeedInfo seed_info) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (ShouldUseSeedFile()) {
-    ScheduleSeedFileWrite(compressed_seed_data, signature);
+    ScheduleSeedFileWrite(seed_info);
   } else {
-    ScheduleLocalStateWrite(base64_seed_data, signature);
+    ScheduleLocalStateWrite(seed_info);
   }
 }
 
@@ -141,10 +140,11 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // TODO(crbug.com/372009105): Remove if-statements when experiment has ended.
   if (ShouldUseSeedFile()) {
-    ScheduleSeedFileWrite(std::string_view(), std::string_view());
+    ScheduleSeedFileClear();
   } else {
     local_state_->ClearPref(fields_prefs_->seed);
     local_state_->ClearPref(fields_prefs_->signature);
+    local_state_->ClearPref(fields_prefs_->milestone);
     // Although only clients in the treatment group write seeds to dedicated
     // seed files, attempt to delete the seed file for clients with
     // Local-State-based seeds. If a client switches experiment groups or
@@ -159,13 +159,15 @@
   if (ShouldUseSeedFile()) {
     return StoredSeed{.storage_format = StoredSeed::StorageFormat::kCompressed,
                       .data = seed_info_.data,
-                      .signature = seed_info_.signature};
+                      .signature = seed_info_.signature,
+                      .milestone = seed_info_.milestone};
   } else {
     return StoredSeed{
         .storage_format =
             StoredSeed::StorageFormat::kCompressedAndBase64Encoded,
         .data = local_state_->GetString(fields_prefs_->seed),
-        .signature = local_state_->GetString(fields_prefs_->signature)};
+        .signature = local_state_->GetString(fields_prefs_->signature),
+        .milestone = local_state_->GetInteger(fields_prefs_->milestone)};
   }
 }
 
@@ -191,15 +193,15 @@
   return base::BindOnce(&DoSerialize, seed_info_.data);
 }
 
-void SeedReaderWriter::ScheduleSeedFileWrite(std::string_view seed_data,
-                                                 std::string_view signature) {
+void SeedReaderWriter::ScheduleSeedFileWrite(ValidatedSeedInfo seed_info) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // Set `seed_info_.data`, this will be used later by the background
   // serialization and can be changed multiple times before a scheduled write
   // completes, in which case the background serializer will use the
   // `seed_info_.data` set at the last call of this function.
-  seed_info_.data = seed_data;
-  seed_info_.signature = signature;
+  seed_info_.data = seed_info.compressed_seed_data;
+  seed_info_.signature = seed_info.signature;
+  seed_info_.milestone = seed_info.milestone;
   // `seed_writer_` will eventually call
   // GetSerializedDataProducerForBackgroundSequence() on *this* object to get
   // a callback that will be run asynchronously. This callback will be used to
@@ -212,11 +214,35 @@
   // TODO(crbug.com/380465790): Seed-related info that has not yet been migrated
   // to seed files must continue to be maintained in local state. Once the
   // migration is complete, stop updating local state.
-  if (signature.empty()) {
-    local_state_->ClearPref(fields_prefs_->signature);
-  } else {
-    local_state_->SetString(fields_prefs_->signature, signature);
-  }
+  local_state_->SetString(fields_prefs_->signature, seed_info_.signature);
+  local_state_->SetInteger(fields_prefs_->milestone, seed_info_.milestone);
+}
+
+void SeedReaderWriter::ScheduleSeedFileClear() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  // Set `seed_info_.data`, this will be used later by the background
+  // serialization and can be changed multiple times before a scheduled write
+  // completes, in which case the background serializer will use the
+  // `seed_info_.data` set at the last call of this function.
+  seed_info_ = {
+      .data = "",
+      .signature = "",
+      .milestone = 0,
+  };
+  // `seed_writer_` will eventually call
+  // GetSerializedDataProducerForBackgroundSequence() on *this* object to get
+  // a callback that will be run asynchronously. This callback will be used to
+  // call the DoSerialize() function which will return the seed data to write
+  // to the file. This write will also be asynchronous and on a different
+  // thread. Note that it is okay to call this while a write is already
+  // occurring in a background thread and that this will result in a new write
+  // being scheduled.
+  seed_writer_->ScheduleWriteWithBackgroundDataSerializer(this);
+  // TODO(crbug.com/380465790): Seed-related info that has not yet been migrated
+  // to seed files must continue to be maintained in local state. Once the
+  // migration is complete, stop updating local state.
+  local_state_->ClearPref(fields_prefs_->signature);
+  local_state_->ClearPref(fields_prefs_->milestone);
 }
 
 void SeedReaderWriter::DeleteSeedFile() {
@@ -237,9 +263,10 @@
 
   if (success) {
     seed_info_.data = std::move(seed_file_data);
-    // TODO(crbug.com/380465790): Read signature from the seed file once it's
-    // stored there.
+    // TODO(crbug.com/380465790): Read other SeedInfo fields from the seed file
+    // once it's stored there.
     seed_info_.signature = local_state_->GetString(fields_prefs_->signature);
+    seed_info_.milestone = local_state_->GetInteger(fields_prefs_->milestone);
   } else {
     // Export seed data from Local State to a seed file in the following cases.
     // 1. Seed file does not exist because this is the first run. For Windows,
@@ -251,8 +278,11 @@
     std::string decoded_data;
     if (base::Base64Decode(local_state_->GetString(fields_prefs_->seed),
                            &decoded_data)) {
-      ScheduleSeedFileWrite(decoded_data,
-                            local_state_->GetString(fields_prefs_->signature));
+      ScheduleSeedFileWrite(ValidatedSeedInfo{
+          .compressed_seed_data = decoded_data,
+          .signature = local_state_->GetString(fields_prefs_->signature),
+          .milestone = local_state_->GetInteger(fields_prefs_->milestone),
+      });
 
       // Record whether empty data is written to the seed file. This can happen
       // in the following cases.
@@ -280,12 +310,11 @@
   local_state_->ClearPref(fields_prefs_->seed);
 }
 
-void SeedReaderWriter::ScheduleLocalStateWrite(
-    std::string_view base64_seed_data,
-    std::string_view signature) {
+void SeedReaderWriter::ScheduleLocalStateWrite(ValidatedSeedInfo seed_info) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  local_state_->SetString(fields_prefs_->seed, base64_seed_data);
-  local_state_->SetString(fields_prefs_->signature, signature);
+  local_state_->SetString(fields_prefs_->seed, seed_info.base64_seed_data);
+  local_state_->SetString(fields_prefs_->signature, seed_info.signature);
+  local_state_->SetInteger(fields_prefs_->milestone, seed_info.milestone);
 }
 
 bool SeedReaderWriter::ShouldUseSeedFile() const {
diff --git a/components/variations/seed_reader_writer.h b/components/variations/seed_reader_writer.h
index d89308a2..73d9ed3 100644
--- a/components/variations/seed_reader_writer.h
+++ b/components/variations/seed_reader_writer.h
@@ -42,20 +42,23 @@
   StorageFormat storage_format;
   std::string_view data;
   std::string_view signature;
+  int milestone = 0;
 };
 
-// TODO(crbug.com/380465790): Represents the seed and other related info.
-// This info will be stored together in the SeedFile. Once all the seed-related
-// info is stored in the struct, change it to a proto and use it to serialize
-// and deserialize the data.
-struct SeedInfo {
-  std::string data;
-  std::string signature;
+// Groups the data from a seed and other seed-related info that is validated
+// and ready to be stored in a seed file or local state. This struct is passed
+// by value, so it must be copyable and lightweight.
+struct ValidatedSeedInfo {
+  std::string_view compressed_seed_data;
+  std::string_view base64_seed_data;
+  std::string_view signature;
+  int milestone = 0;
 };
 
 struct SeedFieldsPrefs {
   const char* seed;
   const char* signature;
+  const char* milestone;
 };
 
 COMPONENT_EXPORT(VARIATIONS)
@@ -99,9 +102,7 @@
   // clients (see ShouldUseSeedFile()) and schedules a write of
   // `base64_seed_data` to local state for all other clients. Also stores other
   // seed-related info.
-  void StoreValidatedSeedInfo(std::string_view compressed_seed_data,
-                              std::string_view base64_seed_data,
-                              std::string_view signature);
+  void StoreValidatedSeedInfo(ValidatedSeedInfo seed_info);
 
   // Clears seed data and other seed-related info by overwriting it with an
   // empty string.
@@ -115,14 +116,28 @@
   void SetTimerForTesting(base::OneShotTimer* timer_override);
 
  private:
+  // TODO(crbug.com/380465790): Represents the seed and other related info.
+  // This info will be stored together in the SeedFile. Once all the
+  // seed-related info is stored in the struct, change it to a proto and use it
+  // to serialize and deserialize the data.
+  struct SeedInfo {
+    std::string data;
+    std::string signature;
+    int milestone = 0;
+  };
+
   // Returns the serialized data to be written to disk. This is done
   // asynchronously during the write process.
   base::ImportantFileWriter::BackgroundDataProducerCallback
   GetSerializedDataProducerForBackgroundSequence() override;
 
-  // Schedules `seed_info` to be written using `seed_writer_`.
-  void ScheduleSeedFileWrite(std::string_view seed_data,
-                             std::string_view signature);
+  // Schedules `seed_info` to be written using `seed_writer_`. Fields with
+  // zero/empty values will be ignored. If you want to clear the seed file, use
+  // ScheduleSeedFileClear() instead.
+  void ScheduleSeedFileWrite(ValidatedSeedInfo seed_info);
+
+  // Schedules `seed_info` to be cleared using `seed_writer_`.
+  void ScheduleSeedFileClear();
 
   // Schedules the deletion of a seed file.
   void DeleteSeedFile();
@@ -134,9 +149,10 @@
   // in `local state_`, additionally clears it.
   void ReadSeedFile();
 
-  // Schedules a write of `base64_seed_data` to `local_state_`.
-  void ScheduleLocalStateWrite(std::string_view base64_seed_data,
-                               std::string_view signature);
+  // Schedules a write of `base64_seed_data` to `local_state_`. Fields with
+  // zero/empty values will be ignored. If you want to clear the seed file, use
+  // ScheduleSeedFileClear() instead.
+  void ScheduleLocalStateWrite(ValidatedSeedInfo seed_info);
 
   // Returns true if a seed file should be used.
   bool ShouldUseSeedFile() const;
diff --git a/components/variations/seed_reader_writer_unittest.cc b/components/variations/seed_reader_writer_unittest.cc
index 146f685..8d0aca3e 100644
--- a/components/variations/seed_reader_writer_unittest.cc
+++ b/components/variations/seed_reader_writer_unittest.cc
@@ -234,7 +234,10 @@
   const std::string base64_compressed_seed =
       base::Base64Encode(compressed_seed);
   seed_reader_writer.StoreValidatedSeedInfo(
-      compressed_seed, base64_compressed_seed, "signature");
+      ValidatedSeedInfo{.compressed_seed_data = compressed_seed,
+                        .base64_seed_data = base64_compressed_seed,
+                        .signature = "signature",
+                        .milestone = 2});
 
   // Force write.
   timer_.Fire();
@@ -387,7 +390,10 @@
   const std::string base64_compressed_seed =
       base::Base64Encode(compressed_seed);
   seed_reader_writer.StoreValidatedSeedInfo(
-      compressed_seed, base64_compressed_seed, "signature");
+      ValidatedSeedInfo{.compressed_seed_data = compressed_seed,
+                        .base64_seed_data = base64_compressed_seed,
+                        .signature = "signature",
+                        .milestone = 2});
 
   // Ensure there's no pending write.
   EXPECT_FALSE(timer_.IsRunning());
@@ -475,7 +481,10 @@
   const std::string base64_compressed_seed =
       base::Base64Encode(compressed_seed);
   seed_reader_writer.StoreValidatedSeedInfo(
-      compressed_seed, base64_compressed_seed, "signature");
+      ValidatedSeedInfo{.compressed_seed_data = compressed_seed,
+                        .base64_seed_data = base64_compressed_seed,
+                        .signature = "signature",
+                        .milestone = 2});
 
   // Ensure there's no pending write.
   EXPECT_FALSE(timer_.IsRunning());
diff --git a/components/variations/variations_safe_seed_store.h b/components/variations/variations_safe_seed_store.h
index 2dd4ae8c..f503a762 100644
--- a/components/variations/variations_safe_seed_store.h
+++ b/components/variations/variations_safe_seed_store.h
@@ -29,9 +29,8 @@
   virtual base::Time GetFetchTime() const = 0;
   virtual void SetFetchTime(const base::Time& fetch_time) = 0;
 
-  // Getter and setter for the milestone that was used for the safe seed.
+  // Getter for the milestone that was used for the safe seed.
   virtual int GetMilestone() const = 0;
-  virtual void SetMilestone(int milestone) = 0;
 
   // Getter and setter for the last server-provided safe seed date of when the
   // seed to be used was fetched. (See GetTimeForStudyDateChecks.)
@@ -40,9 +39,7 @@
 
   // Getter and setter for the compressed and base64-encoded safe seed.
   virtual StoredSeed GetCompressedSeed() const = 0;
-  virtual void SetCompressedSeed(const std::string& safe_compressed,
-                                 const std::string& base64_safe_compressed,
-                                 const std::string& signature) = 0;
+  virtual void SetCompressedSeed(ValidatedSeedInfo seed_info) = 0;
 
   // Getter and setter for the locale associated with the safe seed in the
   // underlying storage.
diff --git a/components/variations/variations_safe_seed_store_local_state.cc b/components/variations/variations_safe_seed_store_local_state.cc
index 7e2dcfb..4265e64a 100644
--- a/components/variations/variations_safe_seed_store_local_state.cc
+++ b/components/variations/variations_safe_seed_store_local_state.cc
@@ -46,11 +46,7 @@
 }
 
 int VariationsSafeSeedStoreLocalState::GetMilestone() const {
-  return local_state_->GetInteger(prefs::kVariationsSafeSeedMilestone);
-}
-
-void VariationsSafeSeedStoreLocalState::SetMilestone(int milestone) {
-  local_state_->SetInteger(prefs::kVariationsSafeSeedMilestone, milestone);
+  return seed_reader_writer_->GetSeedData().milestone;
 }
 
 base::Time VariationsSafeSeedStoreLocalState::GetTimeForStudyDateChecks()
@@ -68,11 +64,8 @@
 }
 
 void VariationsSafeSeedStoreLocalState::SetCompressedSeed(
-    const std::string& safe_compressed,
-    const std::string& base64_safe_compressed,
-    const std::string& signature) {
-  seed_reader_writer_->StoreValidatedSeedInfo(safe_compressed,
-                                              base64_safe_compressed, signature);
+    ValidatedSeedInfo seed_info) {
+  seed_reader_writer_->StoreValidatedSeedInfo(seed_info);
 }
 
 std::string VariationsSafeSeedStoreLocalState::GetLocale() const {
@@ -123,7 +116,6 @@
   local_state_->ClearPref(prefs::kVariationsSafeSeedDate);
   local_state_->ClearPref(prefs::kVariationsSafeSeedFetchTime);
   local_state_->ClearPref(prefs::kVariationsSafeSeedLocale);
-  local_state_->ClearPref(prefs::kVariationsSafeSeedMilestone);
   local_state_->ClearPref(
       prefs::kVariationsSafeSeedPermanentConsistencyCountry);
   local_state_->ClearPref(prefs::kVariationsSafeSeedSessionConsistencyCountry);
diff --git a/components/variations/variations_safe_seed_store_local_state.h b/components/variations/variations_safe_seed_store_local_state.h
index 18eb3baa..55fc94ae 100644
--- a/components/variations/variations_safe_seed_store_local_state.h
+++ b/components/variations/variations_safe_seed_store_local_state.h
@@ -50,15 +50,12 @@
   void SetFetchTime(const base::Time& fetch_time) override;
 
   int GetMilestone() const override;
-  void SetMilestone(int milestone) override;
 
   base::Time GetTimeForStudyDateChecks() const override;
   void SetTimeForStudyDateChecks(const base::Time& safe_seed_time) override;
 
   StoredSeed GetCompressedSeed() const override;
-  void SetCompressedSeed(const std::string& safe_compressed,
-                         const std::string& base64_safe_compressed,
-                         const std::string& signature) override;
+  void SetCompressedSeed(ValidatedSeedInfo seed_info) override;
 
   std::string GetLocale() const override;
   void SetLocale(const std::string& locale) override;
diff --git a/components/variations/variations_seed_store.cc b/components/variations/variations_seed_store.cc
index 3ae9f91..c283c8e 100644
--- a/components/variations/variations_seed_store.cc
+++ b/components/variations/variations_seed_store.cc
@@ -324,7 +324,7 @@
 }
 
 int VariationsSeedStore::GetLatestMilestone() const {
-  return local_state_->GetInteger(prefs::kVariationsSeedMilestone);
+  return seed_reader_writer_->GetSeedData().milestone;
 }
 
 int VariationsSeedStore::GetSafeSeedMilestone() const {
@@ -727,20 +727,24 @@
   if (!country_code.empty())
     local_state_->SetString(prefs::kVariationsCountry, country_code);
 
-  int milestone;
-  if (base::StringToInt(version_info::GetMajorVersionNumber(), &milestone))
-    local_state_->SetInteger(prefs::kVariationsSeedMilestone, milestone);
+  int milestone = version_info::GetMajorVersionNumberAsInt();
 
   // As a space optimization, store an alias to the safe seed if the contents
   // are identical.
   if (seed.MatchesStoredSeed(safe_seed_store_->GetCompressedSeed())) {
-    seed_reader_writer_->StoreValidatedSeedInfo(kIdenticalToSafeSeedSentinel,
-                                                kIdenticalToSafeSeedSentinel,
-                                                seed.base64_seed_signature);
+    seed_reader_writer_->StoreValidatedSeedInfo(ValidatedSeedInfo{
+        .compressed_seed_data = kIdenticalToSafeSeedSentinel,
+        .base64_seed_data = kIdenticalToSafeSeedSentinel,
+        .signature = seed.base64_seed_signature,
+        .milestone = milestone,
+    });
   } else {
-    seed_reader_writer_->StoreValidatedSeedInfo(seed.compressed_seed_data,
-                                                seed.base64_seed_data,
-                                                seed.base64_seed_signature);
+    seed_reader_writer_->StoreValidatedSeedInfo(ValidatedSeedInfo{
+        .compressed_seed_data = seed.compressed_seed_data,
+        .base64_seed_data = seed.base64_seed_data,
+        .signature = seed.base64_seed_signature,
+        .milestone = milestone,
+    });
   }
   UpdateSeedDateAndLogDayChange(date_fetched);
   latest_serial_number_ = seed.parsed.serial_number();
@@ -752,52 +756,45 @@
     const ClientFilterableState& client_state,
     base::Time seed_fetch_time) {
   const StoredSeed previous_safe_seed = safe_seed_store_->GetCompressedSeed();
-  // Avoid overwriting the previous safe seed with an identical copy, which
-  // would be an expensive no-op. This can happen as follows:
+  // Before updating the safe seed, update the latest seed if the latest
+  // seed's value is |kIdenticalToSafeSeedSentinel|.
   //
-  // 1. The client has safe seed A and latest seed B and is applying B.
-  // 2. The client attempts to fetch a seed, receives a 304 Not Modified
-  //    response from the variations server, and promotes B to safe seed. Note
-  //    that B is both the safe seed and the latest seed.
-  // 3. The client attempts to fetch another seed and receives another 304
-  //    response. In this case, the below condition is false and an unnecessary
-  //    write is avoided.
-  if (!seed.MatchesStoredSeed(previous_safe_seed)) {
-    // Before updating the safe seed, update the latest seed if the latest
-    // seed's value is |kIdenticalToSafeSeedSentinel|.
+  // It's theoretically possible for the client to be in the following state:
+  // 1. The client has safe seed A.
+  // 2. The client is applying seed B. In other words, seed B was the latest
+  //    seed when Chrome was started.
+  // 3. The client has just successfully fetched a new latest seed that
+  //    happens to be seed A—perhaps due to a rollback. In this case,
+  //    |kIdenticalToSafeSeedSentinel| is stored as the latest seed value to
+  //    avoid duplicating seed A in storage.
+  // 4. The client is promoting seed B to safe seed.
+  auto latest_seed = seed_reader_writer_->GetSeedData();
+  if (!seed.MatchesStoredSeed(previous_safe_seed) &&
+      latest_seed.data == kIdenticalToSafeSeedSentinel) {
+    // For the below call to StoreValidatedSeed(), there are two possibilities
+    // to consider:
     //
-    // It's theoretically possible for the client to be in the following state:
-    // 1. The client has safe seed A.
-    // 2. The client is applying seed B. In other words, seed B was the latest
-    //    seed when Chrome was started.
-    // 3. The client has just successfully fetched a new latest seed that
-    //    happens to be seed A—perhaps due to a rollback. In this case,
-    //    |kIdenticalToSafeSeedSentinel| is stored as the latest seed value to
-    //    avoid duplicating seed A in storage.
-    // 4. The client is promoting seed B to safe seed.
-    if (seed_reader_writer_->GetSeedData().data ==
-        kIdenticalToSafeSeedSentinel) {
-      // For the below call to StoreValidatedSeed(), there are two possibilities
-      // to consider:
-      //
-      // 1. The client is in the SeedFile experiment's treatment group. In this
-      //    case, StoreValidatedSeedInfo() updates the seed file and ignores the
-      //    local state seed.
-      // 2. The client is either not in the experiment or is in its control or
-      //    default group. In this case, |previous_safe_seed.data| is ignored.
-      seed_reader_writer_->StoreValidatedSeedInfo(
-          previous_safe_seed.data,
-          local_state_->GetString(prefs::kVariationsSafeCompressedSeed),
-          previous_safe_seed.signature);
-    }
-    safe_seed_store_->SetCompressedSeed(seed.compressed_seed_data,
-                                        seed.base64_seed_data,
-                                        seed.base64_seed_signature);
+    // 1. The client is in the SeedFile experiment's treatment group. In this
+    //    case, StoreValidatedSeedInfo() updates the seed file and ignores the
+    //    local state seed.
+    // 2. The client is either not in the experiment or is in its control or
+    //    default group. In this case, |previous_safe_seed.data| is ignored.
+    seed_reader_writer_->StoreValidatedSeedInfo(ValidatedSeedInfo{
+        .compressed_seed_data = previous_safe_seed.data,
+        .base64_seed_data =
+            local_state_->GetString(prefs::kVariationsSafeCompressedSeed),
+        .signature = latest_seed.signature,
+        .milestone = latest_seed.milestone});
   }
 
+  safe_seed_store_->SetCompressedSeed(
+      ValidatedSeedInfo{.compressed_seed_data = seed.compressed_seed_data,
+                        .base64_seed_data = seed.base64_seed_data,
+                        .signature = seed.base64_seed_signature,
+                        .milestone = seed_milestone});
+
   safe_seed_store_->SetTimeForStudyDateChecks(client_state.reference_date);
   safe_seed_store_->SetLocale(client_state.locale);
-  safe_seed_store_->SetMilestone(seed_milestone);
   safe_seed_store_->SetPermanentConsistencyCountry(
       client_state.permanent_consistency_country);
   safe_seed_store_->SetSessionConsistencyCountry(
@@ -806,9 +803,11 @@
   // As a space optimization, overwrite the stored latest seed data with an
   // alias to the safe seed, if they are identical.
   if (seed.MatchesStoredSeed(seed_reader_writer_->GetSeedData())) {
-    seed_reader_writer_->StoreValidatedSeedInfo(kIdenticalToSafeSeedSentinel,
-                                                kIdenticalToSafeSeedSentinel,
-                                                seed.base64_seed_signature);
+    seed_reader_writer_->StoreValidatedSeedInfo(
+        ValidatedSeedInfo{.compressed_seed_data = kIdenticalToSafeSeedSentinel,
+                          .base64_seed_data = kIdenticalToSafeSeedSentinel,
+                          .signature = latest_seed.signature,
+                          .milestone = latest_seed.milestone});
 
     // Moreover, in this case, the last fetch time for the safe seed should
     // match the latest seed's.
diff --git a/components/variations/variations_seed_store_unittest.cc b/components/variations/variations_seed_store_unittest.cc
index 1e3aca91..79e5e4a 100644
--- a/components/variations/variations_seed_store_unittest.cc
+++ b/components/variations/variations_seed_store_unittest.cc
@@ -237,8 +237,12 @@
   //  seed OR the seed-file-based seed depending on the seed file trial group to
   //  which the client belongs.
   seed_store.GetSeedReaderWriterForTesting()->StoreValidatedSeedInfo(
-      /*compressed_seed_data=*/"coffee",
-      /*base64_seed_data=*/"coffee", /*signature=*/"tea");
+      ValidatedSeedInfo{
+          .compressed_seed_data = "coffee",
+          .base64_seed_data = "coffee",
+          .signature = "tea",
+          .milestone = 1,
+      });
   prefs->SetTime(prefs::kVariationsLastFetchTime, now);
   prefs->SetTime(prefs::kVariationsSeedDate, now - delta * 1);
 
@@ -246,12 +250,15 @@
   //  seed OR the seed-file-based seed depending on the seed file trial group to
   //  which the client belongs.
   seed_store.GetSafeSeedReaderWriterForTesting()->StoreValidatedSeedInfo(
-      /*compressed_seed_data=*/"ketchup",
-      /*base64_seed_data=*/"ketchup", /*signature=*/"mustard");
+      ValidatedSeedInfo{
+          .compressed_seed_data = "ketchup",
+          .base64_seed_data = "ketchup",
+          .signature = "mustard",
+          .milestone = 90,
+      });
   prefs->SetTime(prefs::kVariationsSafeSeedDate, now - delta * 2);
   prefs->SetTime(prefs::kVariationsSafeSeedFetchTime, now - delta * 3);
   prefs->SetString(prefs::kVariationsSafeSeedLocale, "en-MX");
-  prefs->SetInteger(prefs::kVariationsSafeSeedMilestone, 90);
   prefs->SetString(prefs::kVariationsSafeSeedPermanentConsistencyCountry, "mx");
   prefs->SetString(prefs::kVariationsSafeSeedSessionConsistencyCountry, "gt");
 }
@@ -264,15 +271,16 @@
 
 void CheckRegularSeedAndSeedPrefsAreSet(const TestingPrefServiceSimple& prefs,
                                         TestVariationsSeedStore& seed_store) {
-  EXPECT_THAT(seed_store.GetSeedReaderWriterForTesting()->GetSeedData().data,
-              Not(IsEmpty()));
-  EXPECT_THAT(
-      seed_store.GetSeedReaderWriterForTesting()->GetSeedData().signature,
-      Not(IsEmpty()));
+  StoredSeed stored_seed =
+      seed_store.GetSeedReaderWriterForTesting()->GetSeedData();
+  EXPECT_THAT(stored_seed.data, Not(IsEmpty()));
+  EXPECT_THAT(stored_seed.signature, Not(IsEmpty()));
+  EXPECT_NE(stored_seed.milestone, 0);
   if (ShouldUseLocalStateSeed()) {
     EXPECT_FALSE(PrefHasDefaultValue(prefs, prefs::kVariationsCompressedSeed));
-    EXPECT_FALSE(PrefHasDefaultValue(prefs, prefs::kVariationsSeedSignature));
   }
+  EXPECT_FALSE(PrefHasDefaultValue(prefs, prefs::kVariationsSeedSignature));
+  EXPECT_FALSE(PrefHasDefaultValue(prefs, prefs::kVariationsSeedMilestone));
   EXPECT_FALSE(PrefHasDefaultValue(prefs, prefs::kVariationsLastFetchTime));
   EXPECT_FALSE(PrefHasDefaultValue(prefs, prefs::kVariationsSeedDate));
 }
@@ -280,37 +288,37 @@
 void CheckRegularSeedAndSeedPrefsAreCleared(
     const TestingPrefServiceSimple& prefs,
     TestVariationsSeedStore& seed_store) {
-  EXPECT_THAT(seed_store.GetSeedReaderWriterForTesting()->GetSeedData().data,
-              IsEmpty());
-  EXPECT_THAT(
-      seed_store.GetSeedReaderWriterForTesting()->GetSeedData().signature,
-      IsEmpty());
+  StoredSeed stored_seed =
+      seed_store.GetSeedReaderWriterForTesting()->GetSeedData();
+  EXPECT_THAT(stored_seed.data, IsEmpty());
+  EXPECT_THAT(stored_seed.signature, IsEmpty());
+  EXPECT_EQ(stored_seed.milestone, 0);
   if (ShouldUseLocalStateSeed()) {
     EXPECT_TRUE(PrefHasDefaultValue(prefs, prefs::kVariationsCompressedSeed));
-    EXPECT_TRUE(PrefHasDefaultValue(prefs, prefs::kVariationsSeedSignature));
   }
+  EXPECT_TRUE(PrefHasDefaultValue(prefs, prefs::kVariationsSeedSignature));
+  EXPECT_TRUE(PrefHasDefaultValue(prefs, prefs::kVariationsSeedMilestone));
   EXPECT_TRUE(PrefHasDefaultValue(prefs, prefs::kVariationsLastFetchTime));
   EXPECT_TRUE(PrefHasDefaultValue(prefs, prefs::kVariationsSeedDate));
 }
 
 void CheckSafeSeedAndSeedPrefsAreSet(const TestingPrefServiceSimple& prefs,
                                      TestVariationsSeedStore& seed_store) {
-  EXPECT_THAT(
-      seed_store.GetSafeSeedReaderWriterForTesting()->GetSeedData().data,
-      Not(IsEmpty()));
-  EXPECT_THAT(
-      seed_store.GetSafeSeedReaderWriterForTesting()->GetSeedData().signature,
-      Not(IsEmpty()));
+  StoredSeed stored_seed =
+      seed_store.GetSafeSeedReaderWriterForTesting()->GetSeedData();
+  EXPECT_THAT(stored_seed.data, Not(IsEmpty()));
+  EXPECT_THAT(stored_seed.signature, Not(IsEmpty()));
+  EXPECT_NE(stored_seed.milestone, 0);
   if (ShouldUseLocalStateSeed()) {
     EXPECT_FALSE(
         PrefHasDefaultValue(prefs, prefs::kVariationsSafeCompressedSeed));
-    EXPECT_FALSE(
-        PrefHasDefaultValue(prefs, prefs::kVariationsSafeSeedSignature));
   }
+  EXPECT_FALSE(PrefHasDefaultValue(prefs, prefs::kVariationsSafeSeedSignature));
+  EXPECT_FALSE(
+      PrefHasDefaultValue(prefs, prefs::kVariationsSafeSeedMilestone));
   EXPECT_FALSE(PrefHasDefaultValue(prefs, prefs::kVariationsSafeSeedDate));
   EXPECT_FALSE(PrefHasDefaultValue(prefs, prefs::kVariationsSafeSeedFetchTime));
   EXPECT_FALSE(PrefHasDefaultValue(prefs, prefs::kVariationsSafeSeedLocale));
-  EXPECT_FALSE(PrefHasDefaultValue(prefs, prefs::kVariationsSafeSeedMilestone));
   EXPECT_FALSE(PrefHasDefaultValue(
       prefs, prefs::kVariationsSafeSeedPermanentConsistencyCountry));
   EXPECT_FALSE(PrefHasDefaultValue(
@@ -319,22 +327,21 @@
 
 void CheckSafeSeedAndSeedPrefsAreCleared(const TestingPrefServiceSimple& prefs,
                                          TestVariationsSeedStore& seed_store) {
-  EXPECT_THAT(
-      seed_store.GetSafeSeedReaderWriterForTesting()->GetSeedData().data,
-      IsEmpty());
-  EXPECT_THAT(
-      seed_store.GetSafeSeedReaderWriterForTesting()->GetSeedData().signature,
-      IsEmpty());
+  StoredSeed stored_seed =
+      seed_store.GetSafeSeedReaderWriterForTesting()->GetSeedData();
+  EXPECT_THAT(stored_seed.data, IsEmpty());
+  EXPECT_THAT(stored_seed.signature, IsEmpty());
+  EXPECT_EQ(stored_seed.milestone, 0);
   if (ShouldUseLocalStateSeed()) {
     EXPECT_TRUE(
         PrefHasDefaultValue(prefs, prefs::kVariationsSafeCompressedSeed));
-    EXPECT_TRUE(
-        PrefHasDefaultValue(prefs, prefs::kVariationsSafeSeedSignature));
   }
+  EXPECT_TRUE(PrefHasDefaultValue(prefs, prefs::kVariationsSafeSeedSignature));
+  EXPECT_TRUE(
+      PrefHasDefaultValue(prefs, prefs::kVariationsSafeSeedMilestone));
   EXPECT_TRUE(PrefHasDefaultValue(prefs, prefs::kVariationsSafeSeedDate));
   EXPECT_TRUE(PrefHasDefaultValue(prefs, prefs::kVariationsSafeSeedFetchTime));
   EXPECT_TRUE(PrefHasDefaultValue(prefs, prefs::kVariationsSafeSeedLocale));
-  EXPECT_TRUE(PrefHasDefaultValue(prefs, prefs::kVariationsSafeSeedMilestone));
   EXPECT_TRUE(PrefHasDefaultValue(
       prefs, prefs::kVariationsSafeSeedPermanentConsistencyCountry));
   EXPECT_TRUE(PrefHasDefaultValue(
@@ -416,7 +423,12 @@
     std::string compressed_seed_data = Gzip(SerializeSeed(seed));
     std::string base64_seed_data = SerializeSeedBase64(seed);
     seed_store.GetSeedReaderWriterForTesting()->StoreValidatedSeedInfo(
-        compressed_seed_data, base64_seed_data, test_signature);
+        ValidatedSeedInfo{
+            .compressed_seed_data = compressed_seed_data,
+            .base64_seed_data = base64_seed_data,
+            .signature = test_signature,
+            .milestone = 1,
+        });
   }
 
   std::string seed_data_;
@@ -438,7 +450,12 @@
   TestVariationsSeedStore seed_store(&prefs_, temp_dir_.GetPath());
   ASSERT_EQ(base::FieldTrialList::FindFullName(kSeedFileTrial), GetParam());
   seed_store.GetSeedReaderWriterForTesting()->StoreValidatedSeedInfo(
-      compressed_seed, base64_seed, base64_seed_signature);
+      ValidatedSeedInfo{
+          .compressed_seed_data = compressed_seed,
+          .base64_seed_data = base64_seed,
+          .signature = base64_seed_signature,
+          .milestone = 1,
+      });
   const std::string expected_seed =
       GetParam() == kSeedFilesGroup ? compressed_seed : base64_seed;
 
@@ -476,8 +493,10 @@
   ASSERT_EQ(base::FieldTrialList::FindFullName(kSeedFileTrial), GetParam());
   SetAllSeedsAndSeedPrefsToNonDefaultValues(&prefs_, seed_store);
   seed_store.GetSeedReaderWriterForTesting()->StoreValidatedSeedInfo(
-      Gzip(seed_data), GzipAndBase64Encode(seed_data),
-      "a deeply compromised signature.");
+      ValidatedSeedInfo{.compressed_seed_data = Gzip(seed_data),
+                        .base64_seed_data = GzipAndBase64Encode(seed_data),
+                        .signature = "a deeply compromised signature.",
+                        .milestone = 1});
 
   base::HistogramTester histogram_tester;
   VariationsSeed loaded_seed;
@@ -500,9 +519,12 @@
   ASSERT_EQ(base::FieldTrialList::FindFullName(kSeedFileTrial), GetParam());
   SetAllSeedsAndSeedPrefsToNonDefaultValues(&prefs_, seed_store);
   seed_store.GetSeedReaderWriterForTesting()->StoreValidatedSeedInfo(
-      Gzip("Not a proto"), GzipAndBase64Encode("Not a proto"),
-      "ignored signature");
-
+      ValidatedSeedInfo{
+          .compressed_seed_data = Gzip("Not a proto"),
+          .base64_seed_data = GzipAndBase64Encode("Not a proto"),
+          .signature = "ignored signature",
+          .milestone = 1,
+      });
   base::HistogramTester histogram_tester;
   VariationsSeed loaded_seed;
   std::string loaded_seed_data;
@@ -527,7 +549,12 @@
   ASSERT_EQ(base::FieldTrialList::FindFullName(kSeedFileTrial), GetParam());
   SetAllSeedsAndSeedPrefsToNonDefaultValues(&prefs_, seed_store);
   seed_store.GetSeedReaderWriterForTesting()->StoreValidatedSeedInfo(
-      Gzip(seed_data), GzipAndBase64Encode(seed_data), /*signature=*/"");
+      ValidatedSeedInfo{
+          .compressed_seed_data = Gzip(seed_data),
+          .base64_seed_data = GzipAndBase64Encode(seed_data),
+          .signature = "",
+          .milestone = 1,
+      });
 
   base::HistogramTester histogram_tester;
   VariationsSeed loaded_seed;
@@ -557,7 +584,12 @@
   ASSERT_EQ(base::FieldTrialList::FindFullName(kSeedFileTrial), GetParam());
   SetAllSeedsAndSeedPrefsToNonDefaultValues(&prefs_, seed_store);
   seed_store.GetSeedReaderWriterForTesting()->StoreValidatedSeedInfo(
-      Gzip(seed_data), GzipAndBase64Encode(seed_data), /*signature=*/"");
+      ValidatedSeedInfo{
+          .compressed_seed_data = Gzip(seed_data),
+          .base64_seed_data = GzipAndBase64Encode(seed_data),
+          .signature = "",
+          .milestone = 1,
+      });
 
   base::HistogramTester histogram_tester;
   VariationsSeed loaded_seed;
@@ -574,9 +606,12 @@
   EXPECT_THAT(
       seed_store.GetSeedReaderWriterForTesting()->GetSeedData().signature,
       IsEmpty());
+  EXPECT_NE(seed_store.GetSeedReaderWriterForTesting()->GetSeedData().milestone,
+            0);
   if (ShouldUseLocalStateSeed()) {
     EXPECT_FALSE(PrefHasDefaultValue(prefs_, prefs::kVariationsCompressedSeed));
     EXPECT_FALSE(PrefHasDefaultValue(prefs_, prefs::kVariationsSeedSignature));
+    EXPECT_FALSE(PrefHasDefaultValue(prefs_, prefs::kVariationsSeedMilestone));
   }
   EXPECT_FALSE(PrefHasDefaultValue(prefs_, prefs::kVariationsLastFetchTime));
   EXPECT_FALSE(PrefHasDefaultValue(prefs_, prefs::kVariationsSeedDate));
@@ -611,10 +646,19 @@
   TestVariationsSeedStore seed_store(&prefs_, temp_dir_.GetPath());
   ASSERT_EQ(base::FieldTrialList::FindFullName(kSeedFileTrial), GetParam());
   seed_store.GetSeedReaderWriterForTesting()->StoreValidatedSeedInfo(
-      kIdenticalToSafeSeedSentinel, kIdenticalToSafeSeedSentinel,
-      base64_seed_signature);
+      ValidatedSeedInfo{
+          .compressed_seed_data = kIdenticalToSafeSeedSentinel,
+          .base64_seed_data = kIdenticalToSafeSeedSentinel,
+          .signature = base64_seed_signature,
+          .milestone = 2,
+      });
   seed_store.GetSafeSeedReaderWriterForTesting()->StoreValidatedSeedInfo(
-      Gzip(seed_data), GzipAndBase64Encode(seed_data), base64_seed_signature);
+      ValidatedSeedInfo{
+          .compressed_seed_data = Gzip(seed_data),
+          .base64_seed_data = GzipAndBase64Encode(seed_data),
+          .signature = base64_seed_signature,
+          .milestone = 1,
+      });
 
   base::HistogramTester histogram_tester;
   VariationsSeed loaded_seed;
@@ -644,8 +688,12 @@
   compressed_seed[5] ^= 0xFF;
   compressed_seed[10] ^= 0xFF;
   seed_store.GetSeedReaderWriterForTesting()->StoreValidatedSeedInfo(
-      compressed_seed, base::Base64Encode(compressed_seed),
-      "ignored signature");
+      ValidatedSeedInfo{
+          .compressed_seed_data = compressed_seed,
+          .base64_seed_data = base::Base64Encode(compressed_seed),
+          .signature = "ignored signature",
+          .milestone = 1,
+      });
 
   base::HistogramTester histogram_tester;
   ASSERT_FALSE(MakeSeedStoreLoadStoredSeed(seed_store));
@@ -665,8 +713,12 @@
   // 51MiB of uncompressed data to exceed 50MiB limit.
   const std::string compressed_seed = Gzip(std::string(51 * 1024 * 1024, 'A'));
   seed_store.GetSeedReaderWriterForTesting()->StoreValidatedSeedInfo(
-      compressed_seed, base::Base64Encode(compressed_seed),
-      "ignored signature");
+      ValidatedSeedInfo{
+          .compressed_seed_data = compressed_seed,
+          .base64_seed_data = base::Base64Encode(compressed_seed),
+          .signature = "ignored signature",
+          .milestone = 1,
+      });
 
   base::HistogramTester histogram_tester;
   VariationsSeed loaded_seed;
@@ -698,7 +750,12 @@
   ASSERT_EQ(base::FieldTrialList::FindFullName(kSeedFileTrial), GetParam());
   SetAllSeedsAndSeedPrefsToNonDefaultValues(&prefs_, seed_store);
   seed_store.GetSeedReaderWriterForTesting()->StoreValidatedSeedInfo(
-      "invalid seed data", "invalid seed data", "ignored signature");
+      ValidatedSeedInfo{
+          .compressed_seed_data = "invalid seed data",
+          .base64_seed_data = "invalid seed data",
+          .signature = "ignored signature",
+          .milestone = 1,
+      });
 
   base::HistogramTester histogram_tester;
   ASSERT_FALSE(MakeSeedStoreLoadStoredSeed(seed_store));
@@ -926,8 +983,13 @@
   ASSERT_EQ(base::FieldTrialList::FindFullName(kSeedFileTrial),
             GetParam().field_trial_group);
   seed_store.GetSeedReaderWriterForTesting()->StoreValidatedSeedInfo(
-      Gzip(kSeedDeltaTestData.GetInitialSeedData()),
-      kSeedDeltaTestData.GetInitialSeedDataAsPrefValue(), "ignored signature");
+      ValidatedSeedInfo{
+          .compressed_seed_data = Gzip(kSeedDeltaTestData.GetInitialSeedData()),
+          .base64_seed_data =
+              kSeedDeltaTestData.GetInitialSeedDataAsPrefValue(),
+          .signature = "ignored signature",
+          .milestone = 1,
+      });
 
   ASSERT_TRUE(StoreSeedData(seed_store, kSeedDeltaTestData.GetDeltaData(),
                             {.is_delta_compressed = true}));
@@ -939,8 +1001,13 @@
   ASSERT_EQ(base::FieldTrialList::FindFullName(kSeedFileTrial),
             GetParam().field_trial_group);
   seed_store.GetSeedReaderWriterForTesting()->StoreValidatedSeedInfo(
-      Gzip(kSeedDeltaTestData.GetInitialSeedData()),
-      kSeedDeltaTestData.GetInitialSeedDataAsPrefValue(), "ignored signature");
+      ValidatedSeedInfo{
+          .compressed_seed_data = Gzip(kSeedDeltaTestData.GetInitialSeedData()),
+          .base64_seed_data =
+              kSeedDeltaTestData.GetInitialSeedDataAsPrefValue(),
+          .signature = "ignored signature",
+          .milestone = 1,
+      });
 
   ASSERT_TRUE(StoreSeedData(seed_store, Gzip(kSeedDeltaTestData.GetDeltaData()),
                             {
@@ -969,8 +1036,13 @@
   ASSERT_EQ(base::FieldTrialList::FindFullName(kSeedFileTrial),
             GetParam().field_trial_group);
   seed_store.GetSeedReaderWriterForTesting()->StoreValidatedSeedInfo(
-      Gzip(kSeedDeltaTestData.GetInitialSeedData()),
-      kSeedDeltaTestData.GetInitialSeedDataAsPrefValue(), "ignored signature");
+      ValidatedSeedInfo{
+          .compressed_seed_data = Gzip(kSeedDeltaTestData.GetInitialSeedData()),
+          .base64_seed_data =
+              kSeedDeltaTestData.GetInitialSeedDataAsPrefValue(),
+          .signature = "ignored signature",
+          .milestone = 1,
+      });
 
   store_success_ = true;
   // Provide a gzipped delta, when gzip is not expected.
@@ -986,8 +1058,12 @@
   ASSERT_EQ(base::FieldTrialList::FindFullName(kSeedFileTrial),
             GetParam().field_trial_group);
   seed_store.GetSafeSeedReaderWriterForTesting()->StoreValidatedSeedInfo(
-      Gzip(serialized_seed), GzipAndBase64Encode(serialized_seed),
-      "ignored signature");
+      ValidatedSeedInfo{
+          .compressed_seed_data = Gzip(serialized_seed),
+          .base64_seed_data = GzipAndBase64Encode(serialized_seed),
+          .signature = "ignored signature",
+          .milestone = 1,
+      });
   ASSERT_TRUE(StoreSeedData(seed_store, serialized_seed));
 
   // Verify that the pref has a sentinel value, rather than the full string.
@@ -1021,8 +1097,12 @@
   ASSERT_EQ(base::FieldTrialList::FindFullName(kSeedFileTrial),
             GetParam().field_trial_group);
   seed_store.GetSeedReaderWriterForTesting()->StoreValidatedSeedInfo(
-      Gzip(seed_data), GzipAndBase64Encode(seed_data),
-      "a completely ignored signature");
+      ValidatedSeedInfo{
+          .compressed_seed_data = Gzip(seed_data),
+          .base64_seed_data = GzipAndBase64Encode(seed_data),
+          .signature = "a completely ignored signature",
+          .milestone = 1,
+      });
   EXPECT_EQ("123", seed_store.GetLatestSerialNumber());
 
   VariationsSeed new_seed = CreateTestSeed();
@@ -1063,7 +1143,12 @@
   TestVariationsSeedStore seed_store(&prefs_, temp_dir_.GetPath());
   ASSERT_EQ(base::FieldTrialList::FindFullName(kSeedFileTrial), GetParam());
   seed_store.GetSafeSeedReaderWriterForTesting()->StoreValidatedSeedInfo(
-      compressed_seed, base64_seed, "a test signature, ignored.");
+      ValidatedSeedInfo{
+          .compressed_seed_data = compressed_seed,
+          .base64_seed_data = base64_seed,
+          .signature = "a test signature, ignored.",
+          .milestone = 1,
+      });
   prefs_.SetTime(prefs::kVariationsSafeSeedDate, reference_date);
   prefs_.SetTime(prefs::kVariationsSafeSeedFetchTime,
                  reference_date - base::Days(3));
@@ -1123,8 +1208,12 @@
   ASSERT_EQ(base::FieldTrialList::FindFullName(kSeedFileTrial), GetParam());
   SetAllSeedsAndSeedPrefsToNonDefaultValues(&prefs_, seed_store);
   seed_store.GetSafeSeedReaderWriterForTesting()->StoreValidatedSeedInfo(
-      Gzip(seed_data), GzipAndBase64Encode((seed_data)),
-      "a deeply compromised signature.");
+      ValidatedSeedInfo{
+          .compressed_seed_data = Gzip(seed_data),
+          .base64_seed_data = GzipAndBase64Encode(seed_data),
+          .signature = "a deeply compromised signature.",
+          .milestone = 1,
+      });
 
   base::HistogramTester histogram_tester;
   VariationsSeed loaded_seed;
@@ -1180,8 +1269,12 @@
   compressed_seed[5] ^= 0xFF;
   compressed_seed[10] ^= 0xFF;
   seed_store.GetSafeSeedReaderWriterForTesting()->StoreValidatedSeedInfo(
-      compressed_seed, base::Base64Encode(compressed_seed),
-      "ignored signature");
+      ValidatedSeedInfo{
+          .compressed_seed_data = compressed_seed,
+          .base64_seed_data = base::Base64Encode(compressed_seed),
+          .signature = "ignored signature",
+          .milestone = 1,
+      });
 
   base::HistogramTester histogram_tester;
   VariationsSeed loaded_seed;
@@ -1216,8 +1309,12 @@
   // 51MiB of uncompressed data to exceed 50MiB limit.
   const std::string compressed_seed = Gzip(std::string(51 * 1024 * 1024, 'A'));
   seed_store.GetSafeSeedReaderWriterForTesting()->StoreValidatedSeedInfo(
-      compressed_seed, base::Base64Encode(compressed_seed),
-      "ignored signature");
+      ValidatedSeedInfo{
+          .compressed_seed_data = compressed_seed,
+          .base64_seed_data = base::Base64Encode(compressed_seed),
+          .signature = "ignored signature",
+          .milestone = 1,
+      });
 
   base::HistogramTester histogram_tester;
   VariationsSeed loaded_seed;
@@ -1259,7 +1356,12 @@
   ASSERT_EQ(base::FieldTrialList::FindFullName(kSeedFileTrial), GetParam());
   SetAllSeedsAndSeedPrefsToNonDefaultValues(&prefs_, seed_store);
   seed_store.GetSafeSeedReaderWriterForTesting()->StoreValidatedSeedInfo(
-      "invalid seed data", "invalid seed data", "ignored signature");
+      ValidatedSeedInfo{
+          .compressed_seed_data = "invalid seed data",
+          .base64_seed_data = "invalid seed data",
+          .signature = "ignored signature",
+          .milestone = 1,
+      });
 
   base::HistogramTester histogram_tester;
   VariationsSeed loaded_seed;
@@ -1552,10 +1654,19 @@
   ASSERT_EQ(base::FieldTrialList::FindFullName(kSeedFileTrial),
             GetParam().field_trial_group);
   seed_store.GetSafeSeedReaderWriterForTesting()->StoreValidatedSeedInfo(
-      compressed_old_seed, base64_old_seed, "a completely ignored signature");
+      ValidatedSeedInfo{
+          .compressed_seed_data = compressed_old_seed,
+          .base64_seed_data = base64_old_seed,
+          .signature = "a completely ignored signature",
+          .milestone = 1,
+      });
   seed_store.GetSeedReaderWriterForTesting()->StoreValidatedSeedInfo(
-      kIdenticalToSafeSeedSentinel, kIdenticalToSafeSeedSentinel,
-      "a completely ignored signature");
+      ValidatedSeedInfo{
+          .compressed_seed_data = kIdenticalToSafeSeedSentinel,
+          .base64_seed_data = kIdenticalToSafeSeedSentinel,
+          .signature = "a completely ignored signature",
+          .milestone = 2,
+      });
   base::HistogramTester histogram_tester;
   ASSERT_TRUE(seed_store.StoreSafeSeed(
       new_seed_data, "a completely ignored signature",
@@ -1688,10 +1799,19 @@
   ASSERT_EQ(base::FieldTrialList::FindFullName(kSeedFileTrial),
             GetParam().field_trial_group);
   seed_store.GetSafeSeedReaderWriterForTesting()->StoreValidatedSeedInfo(
-      compressed_old_seed, base64_old_seed, "a completely ignored signature");
+      ValidatedSeedInfo{
+          .compressed_seed_data = compressed_old_seed,
+          .base64_seed_data = base64_old_seed,
+          .signature = "a completely ignored signature",
+          .milestone = 1,
+      });
   seed_store.GetSeedReaderWriterForTesting()->StoreValidatedSeedInfo(
-      kIdenticalToSafeSeedSentinel, kIdenticalToSafeSeedSentinel,
-      "a completely ignored signature");
+      ValidatedSeedInfo{
+          .compressed_seed_data = kIdenticalToSafeSeedSentinel,
+          .base64_seed_data = kIdenticalToSafeSeedSentinel,
+          .signature = "a completely ignored signature",
+          .milestone = 2,
+      });
   base::HistogramTester histogram_tester;
   ASSERT_TRUE(seed_store.StoreSafeSeed(
       new_seed_data, "a completely ignored signature",
@@ -1753,7 +1873,12 @@
   ASSERT_EQ(base::FieldTrialList::FindFullName(kSeedFileTrial),
             GetParam().field_trial_group);
   seed_store.GetSeedReaderWriterForTesting()->StoreValidatedSeedInfo(
-      compressed_seed, base64_seed, "ignored signature");
+      ValidatedSeedInfo{
+          .compressed_seed_data = compressed_seed,
+          .base64_seed_data = base64_seed,
+          .signature = "ignored signature",
+          .milestone = 92,
+      });
   prefs_.SetTime(prefs::kVariationsLastFetchTime, last_fetch_time);
   const std::string expected_seed =
       GetParam().field_trial_group == kSeedFilesGroup ? compressed_seed
@@ -1902,9 +2027,19 @@
   TestVariationsSeedStore seed_store(&prefs_, temp_dir_.GetPath());
   ASSERT_EQ(base::FieldTrialList::FindFullName(kSeedFileTrial), GetParam());
   seed_store.GetSafeSeedReaderWriterForTesting()->StoreValidatedSeedInfo(
-      "one", "one", "ignored signature");
+      ValidatedSeedInfo{
+          .compressed_seed_data = "one",
+          .base64_seed_data = "one",
+          .signature = "ignored signature",
+          .milestone = 1,
+      });
   seed_store.GetSafeSeedReaderWriterForTesting()->StoreValidatedSeedInfo(
-      "not one", "not one", "ignored signature");
+      ValidatedSeedInfo{
+          .compressed_seed_data = "not one",
+          .base64_seed_data = "not one",
+          .signature = "ignored signature",
+          .milestone = 2,
+      });
   prefs_.SetTime(prefs::kVariationsLastFetchTime, WrapTime(1));
   prefs_.SetTime(prefs::kVariationsSafeSeedFetchTime, WrapTime(0));
   seed_store.RecordLastFetchTime(WrapTime(11));
@@ -1924,10 +2059,19 @@
   TestVariationsSeedStore seed_store(&prefs_, temp_dir_.GetPath());
   ASSERT_EQ(base::FieldTrialList::FindFullName(kSeedFileTrial), GetParam());
   seed_store.GetSeedReaderWriterForTesting()->StoreValidatedSeedInfo(
-      kIdenticalToSafeSeedSentinel, kIdenticalToSafeSeedSentinel,
-      "ignored signature");
+      ValidatedSeedInfo{
+          .compressed_seed_data = kIdenticalToSafeSeedSentinel,
+          .base64_seed_data = kIdenticalToSafeSeedSentinel,
+          .signature = "ignored signature",
+          .milestone = 1,
+      });
   seed_store.GetSafeSeedReaderWriterForTesting()->StoreValidatedSeedInfo(
-      "some seed", "some seed", "ignored signature");
+      ValidatedSeedInfo{
+          .compressed_seed_data = "some seed",
+          .base64_seed_data = "some seed",
+          .signature = "ignored signature",
+          .milestone = 1,
+      });
   prefs_.SetTime(prefs::kVariationsLastFetchTime, WrapTime(1));
   prefs_.SetTime(prefs::kVariationsSafeSeedFetchTime, WrapTime(0));
   seed_store.RecordLastFetchTime(WrapTime(11));
@@ -1951,8 +2095,12 @@
   TestVariationsSeedStore seed_store(&prefs_, temp_dir_.GetPath());
   ASSERT_EQ(base::FieldTrialList::FindFullName(kSeedFileTrial), GetParam());
   seed_store.GetSeedReaderWriterForTesting()->StoreValidatedSeedInfo(
-      Gzip(seed_data), GzipAndBase64Encode(seed_data),
-      "a completely ignored signature");
+      ValidatedSeedInfo{
+          .compressed_seed_data = Gzip(seed_data),
+          .base64_seed_data = GzipAndBase64Encode(seed_data),
+          .signature = "a completely ignored signature",
+          .milestone = 1,
+      });
 
   EXPECT_EQ("123", seed_store.GetLatestSerialNumber());
 }
@@ -1964,7 +2112,12 @@
   TestVariationsSeedStore seed_store(&prefs_, temp_dir_.GetPath());
   ASSERT_EQ(base::FieldTrialList::FindFullName(kSeedFileTrial), GetParam());
   seed_store.GetSeedReaderWriterForTesting()->StoreValidatedSeedInfo(
-      "invalid seed data", "invalid seed data", "an unused signature");
+      ValidatedSeedInfo{
+          .compressed_seed_data = "invalid seed data",
+          .base64_seed_data = "invalid seed data",
+          .signature = "an unused signature",
+          .milestone = 1,
+      });
   EXPECT_EQ(std::string(), seed_store.GetLatestSerialNumber());
   EXPECT_TRUE(PrefHasDefaultValue(prefs_, prefs::kVariationsCompressedSeed));
   EXPECT_THAT(seed_store.GetSeedReaderWriterForTesting()->GetSeedData().data,
diff --git a/components/visited_url_ranking/internal/url_grouping/group_suggestions_manager.cc b/components/visited_url_ranking/internal/url_grouping/group_suggestions_manager.cc
index 1c4c065..1fe8743d 100644
--- a/components/visited_url_ranking/internal/url_grouping/group_suggestions_manager.cc
+++ b/components/visited_url_ranking/internal/url_grouping/group_suggestions_manager.cc
@@ -298,6 +298,7 @@
     const std::vector<scoped_refptr<segmentation_platform::InputContext>>&
         inputs,
     GroupSuggestionsDelegate::UserResponseMetadata user_response) {
+  RecordSuggestionUKM(shown_suggestion, inputs, user_response);
   if (user_response.user_response ==
           GroupSuggestionsDelegate::UserResponse::kNotShown ||
       user_response.user_response ==
@@ -308,7 +309,6 @@
   DCHECK_EQ(user_response.suggestion_id, shown_suggestion.suggestion_id);
   suggestion_tracker_->AddSuggestion(shown_suggestion,
                                      user_response.user_response);
-  RecordSuggestionUKM(shown_suggestion, inputs, user_response);
 }
 
 }  // namespace visited_url_ranking
diff --git a/components/viz/client/viz_client_export.h b/components/viz/client/viz_client_export.h
index 6aed35de..006adf5 100644
--- a/components/viz/client/viz_client_export.h
+++ b/components/viz/client/viz_client_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(VIZ_CLIENT_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(VIZ_CLIENT_IMPLEMENTATION)
 #define VIZ_CLIENT_EXPORT __attribute__((visibility("default")))
-#else
-#define VIZ_CLIENT_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/components/viz/common/quads/render_pass_io_unittest.cc b/components/viz/common/quads/render_pass_io_unittest.cc
index 65f4a47..e099485 100644
--- a/components/viz/common/quads/render_pass_io_unittest.cc
+++ b/components/viz/common/quads/render_pass_io_unittest.cc
@@ -234,7 +234,7 @@
           render_pass0->CreateAndAppendDrawQuad<TextureDrawQuad>();
       quad->SetAll(render_pass0->shared_quad_state_list.ElementAt(sqs_index),
                    gfx::Rect(10, 10, 300, 400), gfx::Rect(10, 10, 200, 400),
-                   false, ResourceId(100), false, gfx::PointF(0.f, 0.f),
+                   false, ResourceId(100), true, gfx::PointF(0.f, 0.f),
                    gfx::PointF(1.f, 1.f), SkColors::kTransparent, false, false,
                    gfx::ProtectedVideoType::kHardwareProtected);
       quad->is_stream_video = true;
@@ -256,7 +256,7 @@
           render_pass0->CreateAndAppendDrawQuad<TextureDrawQuad>();
       quad->SetAll(render_pass0->shared_quad_state_list.ElementAt(sqs_index),
                    gfx::Rect(0, 0, 100, 50), gfx::Rect(0, 0, 100, 50), false,
-                   ResourceId(9u), false, gfx::PointF(0.f, 0.f),
+                   ResourceId(9u), true, gfx::PointF(0.f, 0.f),
                    gfx::PointF(1.f, 1.f), SkColors::kBlue, true, false,
                    gfx::ProtectedVideoType::kHardwareProtected);
 
diff --git a/components/viz/common/viz_common_export.h b/components/viz/common/viz_common_export.h
index a383ced..f4355209 100644
--- a/components/viz/common/viz_common_export.h
+++ b/components/viz/common/viz_common_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(VIZ_COMMON_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(VIZ_COMMON_IMPLEMENTATION)
 #define VIZ_COMMON_EXPORT __attribute__((visibility("default")))
-#else
-#define VIZ_COMMON_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/components/viz/common/viz_metal_context_provider_export.h b/components/viz/common/viz_metal_context_provider_export.h
index edf4d63..d405ecc 100644
--- a/components/viz/common/viz_metal_context_provider_export.h
+++ b/components/viz/common/viz_metal_context_provider_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(VIZ_METAL_CONTEXT_PROVIDER_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(VIZ_METAL_CONTEXT_PROVIDER_IMPLEMENTATION)
 #define VIZ_METAL_CONTEXT_PROVIDER_EXPORT __attribute__((visibility("default")))
-#else
-#define VIZ_METAL_CONTEXT_PROVIDER_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/components/viz/host/viz_host_export.h b/components/viz/host/viz_host_export.h
index 2cc7dc7..9913f109 100644
--- a/components/viz/host/viz_host_export.h
+++ b/components/viz/host/viz_host_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(VIZ_HOST_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(VIZ_HOST_IMPLEMENTATION)
 #define VIZ_HOST_EXPORT __attribute__((visibility("default")))
-#else
-#define VIZ_HOST_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/components/viz/service/display/occlusion_culler_unittest.cc b/components/viz/service/display/occlusion_culler_unittest.cc
index 28f3f3c..55f1bd4 100644
--- a/components/viz/service/display/occlusion_culler_unittest.cc
+++ b/components/viz/service/display/occlusion_culler_unittest.cc
@@ -91,7 +91,7 @@
                                        bool is_overlay_candidate) {
     bool nearest_neighbor = false;
     bool needs_blending = false;
-    bool premultiplied_alpha = false;
+    bool premultiplied_alpha = true;
     gfx::Size resource_size_in_pixels = rect.size();
 
     TestResourceFactory::TestResourceContext resource_context;
diff --git a/components/viz/service/display/overlay_candidate_factory_unittest.cc b/components/viz/service/display/overlay_candidate_factory_unittest.cc
index 188171b..23a6b08 100644
--- a/components/viz/service/display/overlay_candidate_factory_unittest.cc
+++ b/components/viz/service/display/overlay_candidate_factory_unittest.cc
@@ -360,7 +360,7 @@
     sqs->quad_to_target_transform = quad_to_target_transform;
     TextureDrawQuad quad;
     quad.SetNew(sqs, quad_rect, quad_rect, false,
-                CreateResource(/*is_overlay_candidate=*/true, origin), false,
+                CreateResource(/*is_overlay_candidate=*/true, origin), true,
                 gfx::PointF(), gfx::PointF(1, 1), SkColors::kTransparent, false,
                 false, gfx::ProtectedVideoType::kClear);
     return quad;
@@ -771,7 +771,7 @@
     quad.SetNew(
         sqs, quad_rect, quad_rect, false,
         CreateResource(/*is_overlay_candidate=*/true, kTopLeft_GrSurfaceOrigin),
-        false, quad_uv_rect.origin(), quad_uv_rect.bottom_right(),
+        true, quad_uv_rect.origin(), quad_uv_rect.bottom_right(),
         SkColors::kTransparent, false, false, gfx::ProtectedVideoType::kClear);
 
     return quad;
diff --git a/components/viz/service/display/overlay_unittest.cc b/components/viz/service/display/overlay_unittest.cc
index 720a171..48be71b 100644
--- a/components/viz/service/display/overlay_unittest.cc
+++ b/components/viz/service/display/overlay_unittest.cc
@@ -772,7 +772,7 @@
       const gfx::Size& resource_size_in_pixels,
       SurfaceId test_surface_id = SurfaceId()) {
     bool needs_blending = false;
-    bool premultiplied_alpha = false;
+    bool premultiplied_alpha = true;
     bool low_latency_rendering = false;
     return CreateCandidateQuadAt(
         shared_quad_state, render_pass, rect, needs_blending,
@@ -788,7 +788,7 @@
       SharedImageFormat format,
       SurfaceId test_surface_id = SurfaceId()) {
     bool needs_blending = false;
-    bool premultiplied_alpha = false;
+    bool premultiplied_alpha = true;
     bool low_latency_rendering = false;
     gfx::Size resource_size_in_pixels = rect.size();
     return CreateCandidateQuadAt(
@@ -813,7 +813,7 @@
       const gfx::Rect& rect,
       SurfaceId test_surface_id = SurfaceId()) {
     bool needs_blending = false;
-    bool premultiplied_alpha = false;
+    bool premultiplied_alpha = true;
     bool low_latency_rendering = true;
     gfx::Size resource_size_in_pixels = rect.size();
     gfx::ProtectedVideoType protected_video_type =
@@ -831,7 +831,7 @@
       AggregatedRenderPass* render_pass,
       const gfx::Rect& rect) {
     bool needs_blending = true;
-    bool premultiplied_alpha = false;
+    bool premultiplied_alpha = true;
     bool low_latency_rendering = false;
     gfx::Size resource_size_in_pixels = rect.size();
 
@@ -1839,9 +1839,9 @@
         pass->CreateAndAppendDrawQuad<TextureDrawQuad>();
     quad_small->SetNew(shared_quad_state_a, kCandidateRectA, kCandidateRectA,
                        false /*needs_blending*/, resource_id_a,
-                       false /*premultiplied_alpha*/, kUVTopLeft,
-                       kUVBottomRight, SkColors::kTransparent,
-                       false /*nearest_neighbor*/, false /*secure_output_only*/,
+                       true /*premultiplied_alpha*/, kUVTopLeft, kUVBottomRight,
+                       SkColors::kTransparent, false /*nearest_neighbor*/,
+                       false /*secure_output_only*/,
                        gfx::ProtectedVideoType::kClear);
     AddExpectedRectToOverlayProcessor(gfx::RectF(kCandidateRectA));
 
@@ -1853,7 +1853,7 @@
 
     quad_big->SetNew(shared_quad_state_b, kCandidateRectB, kCandidateRectB,
                      false /*needs_blending*/, resource_id_b,
-                     false /*premultiplied_alpha*/, kUVTopLeft, kUVBottomRight,
+                     true /*premultiplied_alpha*/, kUVTopLeft, kUVBottomRight,
                      SkColors::kTransparent, false /*nearest_neighbor*/,
                      false /*secure_output_only*/,
                      gfx::ProtectedVideoType::kClear);
@@ -3116,7 +3116,7 @@
     original_quad->SetNew(
         pass->shared_quad_state_list.back(), pass->output_rect,
         pass->output_rect, false /*needs_blending*/, resource_id,
-        false /*premultiplied_alpha*/, kUVTopLeft, kUVBottomRight,
+        true /*premultiplied_alpha*/, kUVTopLeft, kUVBottomRight,
         SkColors::kTransparent, false /*nearest_neighbor*/,
         false /*secure_output_only*/, gfx::ProtectedVideoType::kClear);
 
@@ -3316,7 +3316,7 @@
       quad_candidate_no_occlusion->SetNew(
           sqs, kOverlayTopLeftRect, kOverlayTopLeftRect,
           /*needs_blending=*/false, no_occlusion_quad_resource_id,
-          /*premultiplied=*/false, kUVTopLeft, kUVBottomRight,
+          /*premultiplied=*/true, kUVTopLeft, kUVBottomRight,
           SkColors::kTransparent, /*nearest=*/false,
           /*secure_output=*/false, gfx::ProtectedVideoType::kClear);
       TrackingIdData track_data_top_left{
diff --git a/components/viz/service/display/renderer_pixeltest.cc b/components/viz/service/display/renderer_pixeltest.cc
index a33be0d5..2cbd748 100644
--- a/components/viz/service/display/renderer_pixeltest.cc
+++ b/components/viz/service/display/renderer_pixeltest.cc
@@ -4948,7 +4948,7 @@
 
   auto* quad = pass->CreateAndAppendDrawQuad<TextureDrawQuad>();
   quad->SetNew(shared_state, viewport, viewport, needs_blending,
-               mapped_resource, false, gfx::PointF(0, 0), gfx::PointF(1, 1),
+               mapped_resource, true, gfx::PointF(0, 0), gfx::PointF(1, 1),
                SkColors::kBlack, nearest_neighbor,
                /*secure_output_only=*/false, gfx::ProtectedVideoType::kClear);
 
diff --git a/components/viz/service/viz_service_export.h b/components/viz/service/viz_service_export.h
index e89b1fd..2a56680f 100644
--- a/components/viz/service/viz_service_export.h
+++ b/components/viz/service/viz_service_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(VIZ_SERVICE_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(VIZ_SERVICE_IMPLEMENTATION)
 #define VIZ_SERVICE_EXPORT __attribute__((visibility("default")))
-#else
-#define VIZ_SERVICE_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/components/web_modal/web_modal_export.h b/components/web_modal/web_modal_export.h
index 158e79e..0a4f3306 100644
--- a/components/web_modal/web_modal_export.h
+++ b/components/web_modal/web_modal_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(WEB_MODAL_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(WEB_MODAL_IMPLEMENTATION)
 #define WEB_MODAL_EXPORT __attribute__((visibility("default")))
-#else
-#define WEB_MODAL_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/components/webauthn/core/browser/immediate_request_rate_limiter.h b/components/webauthn/core/browser/immediate_request_rate_limiter.h
index 5ecdfb4..fc28a82d 100644
--- a/components/webauthn/core/browser/immediate_request_rate_limiter.h
+++ b/components/webauthn/core/browser/immediate_request_rate_limiter.h
@@ -6,17 +6,16 @@
 #define COMPONENTS_WEBAUTHN_CORE_BROWSER_IMMEDIATE_REQUEST_RATE_LIMITER_H_
 
 #include "base/containers/flat_map.h"
+#include "components/keyed_service/core/keyed_service.h"
 #include "components/webauthn/core/browser/rate_limiter_slide_window.h"
 #include "url/origin.h"
 
 namespace webauthn {
 
-class ImmediateRequestRateLimiter {
+class ImmediateRequestRateLimiter : public KeyedService {
  public:
   ImmediateRequestRateLimiter();
-  ~ImmediateRequestRateLimiter();
-
-  static ImmediateRequestRateLimiter& GetInstance();
+  ~ImmediateRequestRateLimiter() override;
 
   bool IsRequestAllowed(const url::Origin& origin);
 
diff --git a/components/webdata/common/webdata_export.h b/components/webdata/common/webdata_export.h
index fbeffc0..1a4b2eed 100644
--- a/components/webdata/common/webdata_export.h
+++ b/components/webdata/common/webdata_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(WEBDATA_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(WEBDATA_IMPLEMENTATION)
 #define WEBDATA_EXPORT __attribute__((visibility("default")))
-#else
-#define WEBDATA_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/components/wifi/wifi_export.h b/components/wifi/wifi_export.h
index 0070df1..9dc2ce354 100644
--- a/components/wifi/wifi_export.h
+++ b/components/wifi/wifi_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(WIFI_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(WIFI_IMPLEMENTATION)
 #define WIFI_EXPORT __attribute__((visibility("default")))
-#else
-#define WIFI_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
index 586f1a6..4008b7bd 100644
--- a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
@@ -239,6 +239,28 @@
   base::test::ScopedFeatureList feature_list_;
 };
 
+class SelectReparentInputDumpAccessibilityTreeTest
+    : public DumpAccessibilityTreeTest {
+ protected:
+  SelectReparentInputDumpAccessibilityTreeTest() {
+    feature_list_.InitWithFeatures(
+        {{blink::features::kCustomizableSelect,
+          blink::features::kSelectParserRelaxation,
+          blink::features::kSelectAccessibilityReparentInput}},
+        {/* disabled_features */});
+  }
+
+  ~SelectReparentInputDumpAccessibilityTreeTest() override {
+    // Ensure that the feature lists are destroyed in the same order they
+    // were created in.
+    scoped_feature_list_.Reset();
+    feature_list_.Reset();
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
 class OnScreenModeDumpAccessibilityTreeTest : public DumpAccessibilityTreeTest {
 };
 
@@ -282,6 +304,12 @@
 
 INSTANTIATE_TEST_SUITE_P(
     All,
+    SelectReparentInputDumpAccessibilityTreeTest,
+    ::testing::ValuesIn(DumpAccessibilityTestBase::TreeTestPasses()),
+    DumpAccessibilityTreeTestPassToString());
+
+INSTANTIATE_TEST_SUITE_P(
+    All,
     OnScreenModeDumpAccessibilityTreeTest,
     ::testing::ValuesIn(DumpAccessibilityTestBase::TreeTestPasses()),
     DumpAccessibilityTreeTestPassToString());
@@ -2283,6 +2311,11 @@
   RunHtmlTest(FILE_PATH_LITERAL("select-slowly-build.html"));
 }
 
+IN_PROC_BROWSER_TEST_P(SelectReparentInputDumpAccessibilityTreeTest,
+                       AccessibilityCustomSelectWithInput) {
+  RunHtmlTest(FILE_PATH_LITERAL("select-with-input.html"));
+}
+
 IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilityDd) {
   RunHtmlTest(FILE_PATH_LITERAL("dd.html"));
 }
diff --git a/content/browser/android/content_feature_map.cc b/content/browser/android/content_feature_map.cc
index 208670c..17520c84 100644
--- a/content/browser/android/content_feature_map.cc
+++ b/content/browser/android/content_feature_map.cc
@@ -36,6 +36,7 @@
     &features::kAndroidFallbackToNextSlot,
     &features::kAndroidOpenPdfInline,
     &features::kFedCm,
+    &features::kGroupRebindingForGroupImportance,
     &features::kHidePastePopupOnGSB,
     &features::kReduceGpuPriorityOnBackground,
     &features::kContinueGestureOnLosingFocus,
diff --git a/content/browser/back_forward_cache_internal_browsertest.cc b/content/browser/back_forward_cache_internal_browsertest.cc
index 87ec411..85a599d 100644
--- a/content/browser/back_forward_cache_internal_browsertest.cc
+++ b/content/browser/back_forward_cache_internal_browsertest.cc
@@ -3516,10 +3516,9 @@
   // NavigationThrottles.
   content::ShellContentBrowserClient::Get()
       ->set_create_throttles_for_navigation_callback(base::BindLambdaForTesting(
-          [&did_register_throttles](content::NavigationHandle* handle)
-              -> std::vector<std::unique_ptr<content::NavigationThrottle>> {
+          [&did_register_throttles](
+              content::NavigationThrottleRegistry& registry) -> void {
             did_register_throttles = true;
-            return std::vector<std::unique_ptr<content::NavigationThrottle>>();
           }));
 
   // 2) Navigate to B.
diff --git a/content/browser/cache_storage/cache_storage.cc b/content/browser/cache_storage/cache_storage.cc
index ad6cf28..7263aff1 100644
--- a/content/browser/cache_storage/cache_storage.cc
+++ b/content/browser/cache_storage/cache_storage.cc
@@ -56,7 +56,6 @@
 #include "third_party/blink/public/mojom/quota/quota_types.mojom.h"
 
 using blink::mojom::CacheStorageError;
-using blink::mojom::StorageType;
 
 namespace content {
 
diff --git a/content/browser/cache_storage/cache_storage_manager_unittest.cc b/content/browser/cache_storage/cache_storage_manager_unittest.cc
index aff91cd..058e79e 100644
--- a/content/browser/cache_storage/cache_storage_manager_unittest.cc
+++ b/content/browser/cache_storage/cache_storage_manager_unittest.cc
@@ -99,7 +99,6 @@
   kThirdPartyNamed,
 };
 
-using blink::mojom::StorageType;
 using ResponseHeaderMap = base::flat_map<std::string, std::string>;
 
 constexpr char16_t kReplacementCharacter = 0xFFFD;
diff --git a/content/browser/child_process_launcher_helper_android.cc b/content/browser/child_process_launcher_helper_android.cc
index 4f91da1..3459186a 100644
--- a/content/browser/child_process_launcher_helper_android.cc
+++ b/content/browser/child_process_launcher_helper_android.cc
@@ -153,29 +153,31 @@
   size_t file_count = files_to_register->GetMappingSize();
   DCHECK(file_count > 0);
 
-  ScopedJavaLocalRef<jclass> j_file_info_class = base::android::GetClass(
-      env, "org/chromium/base/process_launcher/FileDescriptorInfo");
-  ScopedJavaLocalRef<jobjectArray> j_file_infos(
-      env, env->NewObjectArray(file_count, j_file_info_class.obj(), NULL));
-  base::android::CheckException(env);
-
+  std::vector<int32_t> ids(file_count);
+  std::vector<int32_t> fds(file_count);
+  std::vector<bool> auto_closes(file_count);
+  std::vector<int64_t> offsets(file_count);
+  std::vector<int64_t> sizes(file_count);
   for (size_t i = 0; i < file_count; ++i) {
     int fd = files_to_register->GetFDAt(i);
     CHECK(0 <= fd);
-    int id = files_to_register->GetIDAt(i);
+    fds[i] = fd;
+    ids[i] = files_to_register->GetIDAt(i);
     const auto& region = files_to_register->GetRegionAt(i);
+    offsets[i] = region.offset;
+    sizes[i] = region.size;
     bool auto_close = files_to_register->OwnsFD(fd);
     if (auto_close) {
       std::ignore = files_to_register->ReleaseFD(fd).release();
     }
-
-    ScopedJavaLocalRef<jobject> j_file_info =
-        Java_ChildProcessLauncherHelperImpl_makeFdInfo(
-            env, id, fd, auto_close, region.offset, region.size);
-    CHECK(j_file_info.obj());
-    env->SetObjectArrayElement(j_file_infos.obj(), i, j_file_info.obj());
+    auto_closes[i] = auto_close;
   }
 
+  ScopedJavaLocalRef<jobjectArray> j_file_infos =
+      Java_ChildProcessLauncherHelperImpl_makeFdInfos(
+          env, ids, fds, auto_closes, offsets, sizes);
+  CHECK(j_file_infos.obj());
+
   AddRef();  // Balanced by OnChildProcessStarted.
   java_peer_.Reset(Java_ChildProcessLauncherHelperImpl_createAndStart(
       env, reinterpret_cast<intptr_t>(this), j_argv, j_file_infos,
diff --git a/content/browser/device_posture/foldable_apis_origin_trial_browsertest.cc b/content/browser/device_posture/foldable_apis_origin_trial_browsertest.cc
deleted file mode 100644
index 9077af31..0000000
--- a/content/browser/device_posture/foldable_apis_origin_trial_browsertest.cc
+++ /dev/null
@@ -1,174 +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 <memory>
-
-#include "base/test/scoped_feature_list.h"
-#include "content/browser/web_contents/web_contents_impl.h"
-#include "content/public/browser/render_widget_host_observer.h"
-#include "content/public/test/browser_test.h"
-#include "content/public/test/browser_test_utils.h"
-#include "content/public/test/content_browser_test.h"
-#include "content/public/test/content_browser_test_utils.h"
-#include "content/public/test/url_loader_interceptor.h"
-#include "content/shell/browser/shell.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "url/gurl.h"
-
-namespace content {
-
-namespace {
-
-constexpr char kBaseDataDir[] = "content/test/data/device_posture";
-
-class TestRenderWidgetHostObserver : public RenderWidgetHostObserver {
- public:
-  explicit TestRenderWidgetHostObserver(RenderWidgetHost* widget_host)
-      : widget_host_(widget_host) {
-    widget_host_->AddObserver(this);
-  }
-
-  ~TestRenderWidgetHostObserver() override {
-    widget_host_->RemoveObserver(this);
-  }
-
-  // RenderWidgetHostObserver:
-  void RenderWidgetHostDidUpdateVisualProperties(
-      RenderWidgetHost* widget_host) override {
-    run_loop_.Quit();
-  }
-
-  void WaitForVisualPropertiesUpdate() { run_loop_.Run(); }
-
- private:
-  raw_ptr<RenderWidgetHost> widget_host_ = nullptr;
-  base::RunLoop run_loop_;
-};
-
-class FoldableAPIsOriginTrialBrowserTest : public ContentBrowserTest {
- public:
-  ~FoldableAPIsOriginTrialBrowserTest() override = default;
-
-  void SetUpOnMainThread() override {
-    ContentBrowserTest::SetUpOnMainThread();
-
-    // We need to use URLLoaderInterceptor (rather than a EmbeddedTestServer),
-    // because origin trial token is associated with a fixed origin, whereas
-    // EmbeddedTestServer serves content on a random port.
-    interceptor_ = URLLoaderInterceptor::ServeFilesFromDirectoryAtOrigin(
-        kBaseDataDir, GURL("https://example.test/"));
-  }
-
-  RenderWidgetHostViewBase* view() {
-    return static_cast<RenderWidgetHostViewBase*>(
-        shell()->web_contents()->GetRenderWidgetHostView());
-  }
-
-  WebContentsImpl* web_contents_impl() {
-    return static_cast<WebContentsImpl*>(shell()->web_contents());
-  }
-
-  void SetUpFoldableState() {
-    const int kDisplayFeatureLength = 10;
-    DisplayFeature emulated_display_feature{
-        DisplayFeature::Orientation::kVertical,
-        /* offset */ view()->GetVisibleViewportSize().width() / 2 -
-            kDisplayFeatureLength / 2,
-        /* mask_length */ kDisplayFeatureLength};
-    view()->OverrideDisplayFeatureForEmulation(&emulated_display_feature);
-    FrameTreeNode* root = web_contents_impl()->GetPrimaryFrameTree().root();
-    RenderWidgetHostImpl* root_widget =
-        root->current_frame_host()->GetRenderWidgetHost();
-    // We need to wait that visual properties are updated before we test the
-    // CSS APIs of Viewport Segments.
-    while (root_widget->visual_properties_ack_pending_for_testing()) {
-      TestRenderWidgetHostObserver(root_widget).WaitForVisualPropertiesUpdate();
-    }
-  }
-
-  void TearDownOnMainThread() override {
-    interceptor_.reset();
-    ContentBrowserTest::TearDownOnMainThread();
-    view()->DisableDisplayFeatureOverrideForEmulation();
-  }
-
-  bool HasViewportSegmentsApi() {
-    return EvalJs(
-               shell(),
-               "window.viewport != undefined && 'segments' in window.viewport")
-        .ExtractBool();
-  }
-
-  bool HasViewportSegmentsCSSApi() {
-    return EvalJs(
-               shell(),
-               "window.matchMedia('(horizontal-viewport-segments: 2)').matches")
-        .ExtractBool();
-  }
-
-  bool HasViewportSegmentsEnvVariablesCSSApi() {
-    return EvalJs(shell(),
-                  "getComputedStyle(document.getElementById('content')).width "
-                  "!= '0px'")
-        .ExtractBool();
-  }
-
- protected:
-  const GURL kValidTokenUrl{"https://example.test/valid_token.html"};
-  const GURL kNoTokenUrl{"https://example.test/no_token.html"};
-
-  std::unique_ptr<content::URLLoaderInterceptor> interceptor_;
-};
-
-IN_PROC_BROWSER_TEST_F(FoldableAPIsOriginTrialBrowserTest,
-                       ValidOriginTrialToken) {
-  ASSERT_TRUE(NavigateToURL(shell(), kValidTokenUrl));
-  SetUpFoldableState();
-  EXPECT_TRUE(HasViewportSegmentsApi());
-  EXPECT_TRUE(HasViewportSegmentsCSSApi());
-  EXPECT_TRUE(HasViewportSegmentsEnvVariablesCSSApi());
-}
-
-IN_PROC_BROWSER_TEST_F(FoldableAPIsOriginTrialBrowserTest, NoOriginTrialToken) {
-  ASSERT_TRUE(NavigateToURL(shell(), kNoTokenUrl));
-  SetUpFoldableState();
-  EXPECT_FALSE(HasViewportSegmentsApi());
-  EXPECT_FALSE(HasViewportSegmentsCSSApi());
-  EXPECT_FALSE(HasViewportSegmentsEnvVariablesCSSApi());
-}
-
-class FoldableAPIsOriginTrialKillSwitchBrowserTest
-    : public FoldableAPIsOriginTrialBrowserTest {
- public:
-  FoldableAPIsOriginTrialKillSwitchBrowserTest() {
-    scoped_feature_list_.Reset();
-    scoped_feature_list_.InitWithFeatures({},
-                                          {blink::features::kViewportSegments});
-  }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-};
-
-IN_PROC_BROWSER_TEST_F(FoldableAPIsOriginTrialKillSwitchBrowserTest,
-                       ValidOriginTrialToken) {
-  ASSERT_TRUE(NavigateToURL(shell(), kValidTokenUrl));
-  SetUpFoldableState();
-  EXPECT_FALSE(HasViewportSegmentsApi());
-  EXPECT_FALSE(HasViewportSegmentsCSSApi());
-  EXPECT_FALSE(HasViewportSegmentsEnvVariablesCSSApi());
-}
-
-IN_PROC_BROWSER_TEST_F(FoldableAPIsOriginTrialKillSwitchBrowserTest,
-                       NoOriginTrialToken) {
-  ASSERT_TRUE(NavigateToURL(shell(), kNoTokenUrl));
-  SetUpFoldableState();
-  EXPECT_FALSE(HasViewportSegmentsApi());
-  EXPECT_FALSE(HasViewportSegmentsCSSApi());
-  EXPECT_FALSE(HasViewportSegmentsEnvVariablesCSSApi());
-}
-
-}  // namespace
-
-}  // namespace content
diff --git a/content/browser/download/download_manager_impl.cc b/content/browser/download/download_manager_impl.cc
index 4a222bd..d3ce52f 100644
--- a/content/browser/download/download_manager_impl.cc
+++ b/content/browser/download/download_manager_impl.cc
@@ -770,9 +770,28 @@
     info->save_info->total_bytes = info->total_bytes;
   }
   content::devtools_instrumentation::WillBeginDownload(info.get(), download);
+  // Check if the download is a duplicate. Only GET download URL that has
+  // existed are considered duplicate.
+  bool is_duplicate = duplicate_file_exists && (info->method == "GET");
+  if (is_duplicate) {
+    bool found_same_url = false;
+    // If there is another download with the same path, the download is
+    // not a duplicate.
+    for (auto it = downloads_.begin(); it != downloads_.end(); ++it) {
+      if (it->second->GetTargetFilePath() == duplicate_download_file_path) {
+        if (it->second->GetURL() != info->url()) {
+          is_duplicate = false;
+          break;
+        } else {
+          found_same_url = true;
+        }
+      }
+    }
+    is_duplicate = is_duplicate && found_same_url;
+  }
   std::move(callback).Run(
       std::move(info), download,
-      duplicate_file_exists ? duplicate_download_file_path : base::FilePath(),
+      is_duplicate ? duplicate_download_file_path : base::FilePath(),
       should_persist_new_download_);
   if (download) {
     // For new downloads, we notify here, rather than earlier, so that
diff --git a/content/browser/indexed_db/BUILD.gn b/content/browser/indexed_db/BUILD.gn
index 6bed2df..aa96ae0 100644
--- a/content/browser/indexed_db/BUILD.gn
+++ b/content/browser/indexed_db/BUILD.gn
@@ -72,6 +72,12 @@
     "instance/lock_request_data.h",
     "instance/pending_connection.cc",
     "instance/pending_connection.h",
+    "instance/sqlite/backing_store_database_impl.cc",
+    "instance/sqlite/backing_store_database_impl.h",
+    "instance/sqlite/backing_store_impl.cc",
+    "instance/sqlite/backing_store_impl.h",
+    "instance/sqlite/backing_store_transaction_impl.cc",
+    "instance/sqlite/backing_store_transaction_impl.h",
     "instance/transaction.cc",
     "instance/transaction.h",
     "list_set.h",
diff --git a/content/browser/indexed_db/instance/backing_store.h b/content/browser/indexed_db/instance/backing_store.h
index f6c58fb..77384e0 100644
--- a/content/browser/indexed_db/instance/backing_store.h
+++ b/content/browser/indexed_db/instance/backing_store.h
@@ -158,9 +158,6 @@
                                            const blink::IndexedDBKey& key,
                                            IndexedDBValue* value,
                                            RecordIdentifier* record) = 0;
-    [[nodiscard]] virtual Status DeleteRecord(
-        int64_t object_store_id,
-        const RecordIdentifier& record) = 0;
     [[nodiscard]] virtual Status DeleteRange(
         int64_t object_store_id,
         const blink::IndexedDBKeyRange&) = 0;
diff --git a/content/browser/indexed_db/instance/fake_transaction.cc b/content/browser/indexed_db/instance/fake_transaction.cc
index a070598..468c84a 100644
--- a/content/browser/indexed_db/instance/fake_transaction.cc
+++ b/content/browser/indexed_db/instance/fake_transaction.cc
@@ -102,12 +102,6 @@
   return wrapped_transaction_->PutRecord(object_store_id, key, value, record);
 }
 
-Status FakeTransaction::DeleteRecord(
-    int64_t object_store_id,
-    const BackingStore::RecordIdentifier& record) {
-  return wrapped_transaction_->DeleteRecord(object_store_id, record);
-}
-
 Status FakeTransaction::DeleteRange(int64_t object_store_id,
                                     const blink::IndexedDBKeyRange& key_range) {
   return wrapped_transaction_->DeleteRange(object_store_id, key_range);
diff --git a/content/browser/indexed_db/instance/fake_transaction.h b/content/browser/indexed_db/instance/fake_transaction.h
index 7c705315..31d5590 100644
--- a/content/browser/indexed_db/instance/fake_transaction.h
+++ b/content/browser/indexed_db/instance/fake_transaction.h
@@ -53,8 +53,6 @@
                    const blink::IndexedDBKey& key,
                    IndexedDBValue* value,
                    BackingStore::RecordIdentifier* record) override;
-  Status DeleteRecord(int64_t object_store_id,
-                      const BackingStore::RecordIdentifier& record) override;
   Status DeleteRange(int64_t object_store_id,
                      const blink::IndexedDBKeyRange&) override;
   Status GetKeyGeneratorCurrentNumber(int64_t object_store_id,
diff --git a/content/browser/indexed_db/instance/leveldb/backing_store.cc b/content/browser/indexed_db/instance/leveldb/backing_store.cc
index c1a0a9c9..bfbed18a 100644
--- a/content/browser/indexed_db/instance/leveldb/backing_store.cc
+++ b/content/browser/indexed_db/instance/leveldb/backing_store.cc
@@ -1611,7 +1611,7 @@
 }
 
 // static
-std::tuple<std::unique_ptr<BackingStore>,
+std::tuple<std::unique_ptr<indexed_db::BackingStore>,
            Status,
            IndexedDBDataLossInfo,
            bool /* is_disk_full */>
@@ -2354,31 +2354,6 @@
       LevelDBScopeDeletionMode::kImmediateWithRangeEndExclusive));
 }
 
-Status BackingStore::Transaction::DeleteRecord(
-    int64_t object_store_id,
-    const RecordIdentifier& record_identifier) {
-  TRACE_EVENT0("IndexedDB", "BackingStore::DeleteRecord");
-  if (!KeyPrefix::ValidIds(database_id(), object_store_id)) {
-    return InvalidDBKeyStatus();
-  }
-  TransactionalLevelDBTransaction* leveldb_transaction = transaction();
-
-  const std::string object_store_data_key = ObjectStoreDataKey::Encode(
-      database_id(), object_store_id, record_identifier.primary_key());
-  Status s(leveldb_transaction->Remove(object_store_data_key));
-  if (!s.ok()) {
-    return s;
-  }
-  s = PutExternalObjectsIfNeeded(object_store_data_key, nullptr);
-  if (!s.ok()) {
-    return s;
-  }
-
-  const std::string exists_entry_key = ExistsEntryKey::Encode(
-      database_id(), object_store_id, record_identifier.primary_key());
-  return Status(leveldb_transaction->Remove(exists_entry_key));
-}
-
 Status BackingStore::Transaction::DeleteRange(
     int64_t object_store_id,
     const IndexedDBKeyRange& key_range) {
diff --git a/content/browser/indexed_db/instance/leveldb/backing_store.h b/content/browser/indexed_db/instance/leveldb/backing_store.h
index cf627e5..5659f52 100644
--- a/content/browser/indexed_db/instance/leveldb/backing_store.h
+++ b/content/browser/indexed_db/instance/leveldb/backing_store.h
@@ -182,8 +182,6 @@
                      IndexedDBValue* value,
                      RecordIdentifier* record) override;
     Status ClearObjectStore(int64_t object_store_id) override;
-    Status DeleteRecord(int64_t object_store_id,
-                        const RecordIdentifier& record) override;
     Status DeleteRange(int64_t object_store_id,
                        const blink::IndexedDBKeyRange&) override;
     Status GetKeyGeneratorCurrentNumber(int64_t object_store_id,
@@ -527,7 +525,7 @@
       blink::mojom::IDBTransactionDurability durability);
 
   // Create and initialize a BackingStore; verify and report its status.
-  static std::tuple<std::unique_ptr<BackingStore>,
+  static std::tuple<std::unique_ptr<indexed_db::BackingStore>,
                     Status,
                     IndexedDBDataLossInfo,
                     bool /* is_disk_full */>
diff --git a/content/browser/indexed_db/instance/sqlite/backing_store_database_impl.cc b/content/browser/indexed_db/instance/sqlite/backing_store_database_impl.cc
new file mode 100644
index 0000000..20e0526
--- /dev/null
+++ b/content/browser/indexed_db/instance/sqlite/backing_store_database_impl.cc
@@ -0,0 +1,116 @@
+// 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 "content/browser/indexed_db/instance/sqlite/backing_store_database_impl.h"
+
+#include "base/check.h"
+#include "base/notimplemented.h"
+#include "content/browser/indexed_db/instance/sqlite/backing_store_transaction_impl.h"
+#include "content/browser/indexed_db/status.h"
+#include "sql/database.h"
+#include "sql/meta_table.h"
+#include "third_party/blink/public/common/indexeddb/indexeddb_metadata.h"
+
+namespace content::indexed_db::sqlite {
+
+struct MetadataConstants {
+ public:
+  // These are schema versions of our implementation of `sql::Database`; not the
+  // version supplied by the application for the IndexedDB database.
+  static constexpr int kCurrentSchemaVersion = 1;
+  static constexpr int kCompatibleSchemaVersion = 1;
+
+  // Keys of properties stored in the meta table.
+  struct Keys {
+    // Metadata relevant to the IndexedDB database. Though these are strictly
+    // "data" and not "metadata", they are stored in the meta table for
+    // convenience.
+    struct Idb {
+      // The application-supplied version of the IndexedDB database.
+      static constexpr char kVersion[] = "idb.version";
+      static constexpr char kMaxObjectStoreId[] = "idb.max_object_store_id";
+    };
+  };
+};
+
+BackingStoreDatabaseImpl::BackingStoreDatabaseImpl(
+    const std::u16string& name,
+    base::WeakPtr<DatabaseConnection> open_db)
+    : connection_(open_db), metadata_(name) {
+  CHECK(connection_->db()->is_open());
+  bool new_db = !sql::MetaTable::DoesTableExist(connection_->db());
+  sql::MetaTable* meta_table = connection_->meta_table();
+  CHECK(meta_table->Init(connection_->db(),
+                         MetadataConstants::kCurrentSchemaVersion,
+                         MetadataConstants::kCompatibleSchemaVersion));
+  if (new_db) {
+    metadata_.version = blink::IndexedDBDatabaseMetadata::NO_VERSION;
+    metadata_.max_object_store_id = 0;
+    meta_table->SetValue(MetadataConstants::Keys::Idb::kVersion,
+                         metadata_.version);
+    meta_table->SetValue(MetadataConstants::Keys::Idb::kMaxObjectStoreId,
+                         metadata_.max_object_store_id);
+  } else {
+    meta_table->GetValue(MetadataConstants::Keys::Idb::kVersion,
+                         &metadata_.version);
+    meta_table->GetValue(MetadataConstants::Keys::Idb::kMaxObjectStoreId,
+                         &metadata_.max_object_store_id);
+  }
+}
+
+BackingStoreDatabaseImpl::~BackingStoreDatabaseImpl() {
+  if (connection_) {
+    // The underlying `DatabaseConnection` can be reused by a new instance.
+    connection_->meta_table()->Reset();
+  }
+}
+
+void BackingStoreDatabaseImpl::RollbackUpgrade(UpgradePassKey& pass_key) {
+  metadata_ = std::move(pass_key.metadata_snapshot_);
+}
+
+Status BackingStoreDatabaseImpl::SetDatabaseVersion(UpgradePassKey&,
+                                                    int64_t version) {
+  connection_->meta_table()->SetValue(MetadataConstants::Keys::Idb::kVersion,
+                                      version);
+  metadata_.version = version;
+  return Status::OK();
+}
+
+const blink::IndexedDBDatabaseMetadata&
+BackingStoreDatabaseImpl::GetMetadata() {
+  return metadata_;
+}
+
+PartitionedLockId BackingStoreDatabaseImpl::GetLockId(
+    int64_t object_store_id) const {
+  NOTIMPLEMENTED();
+  return PartitionedLockId();
+}
+
+std::unique_ptr<BackingStore::Transaction>
+BackingStoreDatabaseImpl::CreateTransaction(
+    blink::mojom::IDBTransactionDurability durability,
+    blink::mojom::IDBTransactionMode mode) {
+  // TODO(crbug.com/40253999): Handle `durability` and assert preconditions for
+  // `mode`.
+  if (mode == blink::mojom::IDBTransactionMode::VersionChange) {
+    return std::make_unique<BackingStoreVersionChangeTransaction>(
+        GetWeakPtr(), UpgradePassKey(metadata_));
+  }
+  return std::make_unique<BackingStoreTransactionImpl>(GetWeakPtr());
+}
+
+Status BackingStoreDatabaseImpl::DeleteDatabase(
+    std::vector<PartitionedLock> locks,
+    base::OnceClosure on_complete) {
+  NOTIMPLEMENTED();
+  return Status::OK();
+}
+
+base::WeakPtr<BackingStoreDatabaseImpl> BackingStoreDatabaseImpl::GetWeakPtr() {
+  return weak_factory_.GetWeakPtr();
+}
+
+}  // namespace content::indexed_db::sqlite
diff --git a/content/browser/indexed_db/instance/sqlite/backing_store_database_impl.h b/content/browser/indexed_db/instance/sqlite/backing_store_database_impl.h
new file mode 100644
index 0000000..72c4b465
--- /dev/null
+++ b/content/browser/indexed_db/instance/sqlite/backing_store_database_impl.h
@@ -0,0 +1,77 @@
+// 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 CONTENT_BROWSER_INDEXED_DB_INSTANCE_SQLITE_BACKING_STORE_DATABASE_IMPL_H_
+#define CONTENT_BROWSER_INDEXED_DB_INSTANCE_SQLITE_BACKING_STORE_DATABASE_IMPL_H_
+
+#include "base/memory/weak_ptr.h"
+#include "content/browser/indexed_db/instance/backing_store.h"
+#include "content/browser/indexed_db/instance/sqlite/backing_store_impl.h"
+#include "third_party/blink/public/common/indexeddb/indexeddb_metadata.h"
+
+namespace sql {
+class Database;
+}  // namespace sql
+
+namespace content::indexed_db::sqlite {
+
+class DatabaseConnection;
+
+// Owns metadata maintenance. There is at most one instance per
+// `DatabaseConnection`.
+class BackingStoreDatabaseImpl : public BackingStore::Database {
+ public:
+  // Enables calling methods that upgrade the IndexedDB database.
+  // This is modeled after `base::PassKey` which cannot be used directly since
+  // we want to store some data inside the key too.
+  class UpgradePassKey {
+   public:
+    UpgradePassKey(const UpgradePassKey&) = delete;
+    UpgradePassKey& operator=(const UpgradePassKey&) = delete;
+    UpgradePassKey(UpgradePassKey&&) = default;
+    UpgradePassKey& operator=(UpgradePassKey&&) = default;
+
+   private:
+    friend class BackingStoreDatabaseImpl;
+    explicit UpgradePassKey(blink::IndexedDBDatabaseMetadata metadata_snapshot)
+        : metadata_snapshot_(std::move(metadata_snapshot)) {}
+
+    // The metadata before the upgrade.
+    blink::IndexedDBDatabaseMetadata metadata_snapshot_;
+  };
+
+  BackingStoreDatabaseImpl(const std::u16string& name,
+                           base::WeakPtr<DatabaseConnection> open_db);
+  BackingStoreDatabaseImpl(const BackingStoreDatabaseImpl&) = delete;
+  BackingStoreDatabaseImpl& operator=(const BackingStoreDatabaseImpl&) = delete;
+  ~BackingStoreDatabaseImpl() override;
+
+  sql::Database* db() const { return connection_->db(); }
+
+  // Rolls back all changes made through this pass key.
+  void RollbackUpgrade(UpgradePassKey&);
+
+  // Methods to upgrade the IndexedDB database. Require a valid pass key.
+  Status SetDatabaseVersion(UpgradePassKey&, int64_t version);
+
+  // BackingStore::Database:
+  const blink::IndexedDBDatabaseMetadata& GetMetadata() override;
+  PartitionedLockId GetLockId(int64_t object_store_id) const override;
+  std::unique_ptr<BackingStore::Transaction> CreateTransaction(
+      blink::mojom::IDBTransactionDurability durability,
+      blink::mojom::IDBTransactionMode mode) override;
+  Status DeleteDatabase(std::vector<PartitionedLock> locks,
+                        base::OnceClosure on_complete) override;
+
+ private:
+  base::WeakPtr<BackingStoreDatabaseImpl> GetWeakPtr();
+
+  base::WeakPtr<DatabaseConnection> connection_;
+  blink::IndexedDBDatabaseMetadata metadata_;
+  base::WeakPtrFactory<BackingStoreDatabaseImpl> weak_factory_{this};
+};
+
+}  // namespace content::indexed_db::sqlite
+
+#endif  // CONTENT_BROWSER_INDEXED_DB_INSTANCE_SQLITE_BACKING_STORE_DATABASE_IMPL_H_
diff --git a/content/browser/indexed_db/instance/sqlite/backing_store_impl.cc b/content/browser/indexed_db/instance/sqlite/backing_store_impl.cc
new file mode 100644
index 0000000..7673ad98
--- /dev/null
+++ b/content/browser/indexed_db/instance/sqlite/backing_store_impl.cc
@@ -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.
+
+#include "content/browser/indexed_db/instance/sqlite/backing_store_impl.h"
+
+#include "base/check.h"
+#include "base/files/file_path.h"
+#include "base/notimplemented.h"
+#include "content/browser/indexed_db/indexed_db_data_loss_info.h"
+#include "content/browser/indexed_db/instance/sqlite/backing_store_database_impl.h"
+#include "content/browser/indexed_db/status.h"
+#include "sql/database.h"
+#include "sql/meta_table.h"
+
+namespace content::indexed_db::sqlite {
+
+DatabaseConnection::DatabaseConnection(std::unique_ptr<sql::Database> db)
+    : db_(std::move(db)), meta_table_(std::make_unique<sql::MetaTable>()) {}
+
+DatabaseConnection::~DatabaseConnection() = default;
+
+base::WeakPtr<DatabaseConnection> DatabaseConnection::GetWeakPtr() {
+  return weak_factory_.GetWeakPtr();
+}
+
+std::tuple<std::unique_ptr<BackingStore>, Status, IndexedDBDataLossInfo, bool>
+BackingStoreImpl::OpenAndVerify(base::FilePath data_path) {
+  return {
+      std::make_unique<BackingStoreImpl>(std::move(data_path)),
+      Status::OK(),
+      IndexedDBDataLossInfo(),
+      false,
+  };
+}
+
+BackingStoreImpl::BackingStoreImpl(base::FilePath data_path)
+    : data_path_(std::move(data_path)) {}
+
+BackingStoreImpl::~BackingStoreImpl() = default;
+
+void BackingStoreImpl::TearDown(base::WaitableEvent* signal_on_destruction) {
+  NOTIMPLEMENTED();
+  signal_on_destruction->Signal();
+}
+
+void BackingStoreImpl::InvalidateBlobReferences() {
+  NOTIMPLEMENTED();
+}
+
+void BackingStoreImpl::StartPreCloseTasks(base::OnceClosure on_done) {
+  NOTIMPLEMENTED();
+  std::move(on_done).Run();
+}
+
+void BackingStoreImpl::StopPreCloseTasks() {
+  NOTIMPLEMENTED();
+}
+
+int64_t BackingStoreImpl::GetInMemorySize() const {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+Status BackingStoreImpl::GetDatabaseNames(std::vector<std::u16string>* names) {
+  NOTIMPLEMENTED();
+  return Status::OK();
+}
+
+Status BackingStoreImpl::GetDatabaseNamesAndVersions(
+    std::vector<blink::mojom::IDBNameAndVersionPtr>* names_and_versions) {
+  NOTIMPLEMENTED();
+  return Status::OK();
+}
+
+base::expected<std::unique_ptr<BackingStore::Database>, Status>
+BackingStoreImpl::CreateOrOpenDatabase(const std::u16string& name) {
+  auto it = open_connections_.find(name);
+  if (it == open_connections_.end()) {
+    // TODO(crbug.com/40253999): Create new tag(s) for metrics.
+    constexpr sql::Database::Tag kSqlTag = "Test";
+    auto db = std::make_unique<sql::Database>(
+        sql::DatabaseOptions().set_exclusive_locking(true).set_wal_mode(true),
+        kSqlTag);
+    // TODO(crbug.com/40253999): Support on-disk databases.
+    CHECK(db->OpenInMemory());
+    auto result = open_connections_.try_emplace(name, std::move(db));
+    CHECK(result.second);
+    it = result.first;
+  }
+  return std::make_unique<BackingStoreDatabaseImpl>(name,
+                                                    it->second.GetWeakPtr());
+}
+
+uintptr_t BackingStoreImpl::GetIdentifierForMemoryDump() {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+void BackingStoreImpl::FlushForTesting() {
+  NOTIMPLEMENTED();
+}
+
+}  // namespace content::indexed_db::sqlite
diff --git a/content/browser/indexed_db/instance/sqlite/backing_store_impl.h b/content/browser/indexed_db/instance/sqlite/backing_store_impl.h
new file mode 100644
index 0000000..c07b41a
--- /dev/null
+++ b/content/browser/indexed_db/instance/sqlite/backing_store_impl.h
@@ -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.
+
+#ifndef CONTENT_BROWSER_INDEXED_DB_INSTANCE_SQLITE_BACKING_STORE_IMPL_H_
+#define CONTENT_BROWSER_INDEXED_DB_INSTANCE_SQLITE_BACKING_STORE_IMPL_H_
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+#include "base/files/file_path.h"
+#include "base/memory/weak_ptr.h"
+#include "content/browser/indexed_db/instance/backing_store.h"
+
+namespace sql {
+class Database;
+class MetaTable;
+}  // namespace sql
+
+namespace content::indexed_db {
+
+struct IndexedDBDataLossInfo;
+
+namespace sqlite {
+
+// Owns an open connection to a SQLite database and supports weak pointer
+// semantics.
+class DatabaseConnection {
+ public:
+  explicit DatabaseConnection(std::unique_ptr<sql::Database> db);
+  DatabaseConnection(const DatabaseConnection&) = delete;
+  DatabaseConnection& operator=(const DatabaseConnection&) = delete;
+  ~DatabaseConnection();
+
+  sql::Database* db() const { return db_.get(); }
+  sql::MetaTable* meta_table() const { return meta_table_.get(); }
+
+  base::WeakPtr<DatabaseConnection> GetWeakPtr();
+
+ private:
+  std::unique_ptr<sql::Database> db_;
+  std::unique_ptr<sql::MetaTable> meta_table_;
+  base::WeakPtrFactory<DatabaseConnection> weak_factory_{this};
+};
+
+class BackingStoreImpl : public BackingStore {
+ public:
+  static std::tuple<std::unique_ptr<BackingStore>,
+                    Status,
+                    IndexedDBDataLossInfo,
+                    bool /* is_disk_full */>
+  OpenAndVerify(base::FilePath data_path);
+
+  BackingStoreImpl(base::FilePath data_path);
+  BackingStoreImpl(const BackingStoreImpl&) = delete;
+  BackingStoreImpl& operator=(const BackingStoreImpl&) = delete;
+  ~BackingStoreImpl() override;
+
+  // BackingStore:
+  void TearDown(base::WaitableEvent* signal_on_destruction) override;
+  void InvalidateBlobReferences() override;
+  void StartPreCloseTasks(base::OnceClosure on_done) override;
+  void StopPreCloseTasks() override;
+  int64_t GetInMemorySize() const override;
+  Status GetDatabaseNames(std::vector<std::u16string>* names) override;
+  Status GetDatabaseNamesAndVersions(
+      std::vector<blink::mojom::IDBNameAndVersionPtr>* names_and_versions)
+      override;
+  base::expected<std::unique_ptr<BackingStore::Database>, Status>
+  CreateOrOpenDatabase(const std::u16string& name) override;
+  uintptr_t GetIdentifierForMemoryDump() override;
+  void FlushForTesting() override;
+
+ private:
+  const base::FilePath data_path_;
+  std::unordered_map<std::u16string, DatabaseConnection> open_connections_;
+};
+
+}  // namespace sqlite
+}  // namespace content::indexed_db
+
+#endif  // CONTENT_BROWSER_INDEXED_DB_INSTANCE_SQLITE_BACKING_STORE_IMPL_H_
diff --git a/content/browser/indexed_db/instance/sqlite/backing_store_transaction_impl.cc b/content/browser/indexed_db/instance/sqlite/backing_store_transaction_impl.cc
new file mode 100644
index 0000000..3cdd7a4
--- /dev/null
+++ b/content/browser/indexed_db/instance/sqlite/backing_store_transaction_impl.cc
@@ -0,0 +1,224 @@
+// 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 "content/browser/indexed_db/instance/sqlite/backing_store_transaction_impl.h"
+
+#include "base/check.h"
+#include "base/notimplemented.h"
+#include "base/notreached.h"
+#include "content/browser/indexed_db/instance/sqlite/backing_store_database_impl.h"
+#include "content/browser/indexed_db/status.h"
+#include "sql/transaction.h"
+
+namespace content::indexed_db::sqlite {
+
+BackingStoreTransactionImpl::BackingStoreTransactionImpl(
+    base::WeakPtr<BackingStoreDatabaseImpl> db)
+    : db_(db), transaction_(db_->db()) {}
+
+BackingStoreTransactionImpl::~BackingStoreTransactionImpl() = default;
+
+void BackingStoreTransactionImpl::Begin(std::vector<PartitionedLock> locks) {
+  CHECK(transaction_.Begin());
+}
+
+Status BackingStoreTransactionImpl::CommitPhaseOne(BlobWriteCallback callback) {
+  return std::move(callback).Run(
+      BlobWriteResult::kRunPhaseTwoAndReturnResult,
+      storage::mojom::WriteBlobToFileResult::kSuccess);
+}
+
+Status BackingStoreTransactionImpl::CommitPhaseTwo() {
+  transaction_.Commit();
+  return Status::OK();
+}
+
+void BackingStoreTransactionImpl::Rollback() {
+  transaction_.Rollback();
+}
+
+Status BackingStoreTransactionImpl::SetDatabaseVersion(int64_t version) {
+  NOTREACHED() << "Implemented by BackingStoreVersionChangeTransaction";
+}
+
+Status BackingStoreTransactionImpl::CreateObjectStore(
+    int64_t object_store_id,
+    const std::u16string& name,
+    blink::IndexedDBKeyPath key_path,
+    bool auto_increment) {
+  NOTREACHED() << "Implemented by BackingStoreVersionChangeTransaction";
+}
+
+Status BackingStoreTransactionImpl::DeleteObjectStore(int64_t object_store_id) {
+  NOTREACHED() << "Implemented by BackingStoreVersionChangeTransaction";
+}
+
+Status BackingStoreTransactionImpl::RenameObjectStore(
+    int64_t object_store_id,
+    const std::u16string& new_name) {
+  NOTREACHED() << "Implemented by BackingStoreVersionChangeTransaction";
+}
+
+Status BackingStoreTransactionImpl::ClearObjectStore(int64_t object_store_id) {
+  NOTREACHED() << "Implemented by BackingStoreVersionChangeTransaction";
+}
+
+Status BackingStoreTransactionImpl::CreateIndex(
+    int64_t object_store_id,
+    int64_t index_id,
+    const std::u16string& name,
+    blink::IndexedDBKeyPath key_path,
+    bool is_unique,
+    bool is_multi_entry) {
+  NOTREACHED() << "Implemented by BackingStoreVersionChangeTransaction";
+}
+
+Status BackingStoreTransactionImpl::DeleteIndex(int64_t object_store_id,
+                                                int64_t index_id) {
+  NOTREACHED() << "Implemented by BackingStoreVersionChangeTransaction";
+}
+
+Status BackingStoreTransactionImpl::RenameIndex(
+    int64_t object_store_id,
+    int64_t index_id,
+    const std::u16string& new_name) {
+  NOTREACHED() << "Implemented by BackingStoreVersionChangeTransaction";
+}
+
+Status BackingStoreTransactionImpl::GetRecord(int64_t object_store_id,
+                                              const blink::IndexedDBKey& key,
+                                              IndexedDBValue* record) {
+  NOTIMPLEMENTED();
+  return Status::OK();
+}
+
+Status BackingStoreTransactionImpl::PutRecord(
+    int64_t object_store_id,
+    const blink::IndexedDBKey& key,
+    IndexedDBValue* value,
+    BackingStore::RecordIdentifier* record) {
+  NOTIMPLEMENTED();
+  return Status::OK();
+}
+
+Status BackingStoreTransactionImpl::DeleteRange(
+    int64_t object_store_id,
+    const blink::IndexedDBKeyRange&) {
+  NOTIMPLEMENTED();
+  return Status::OK();
+}
+
+Status BackingStoreTransactionImpl::GetKeyGeneratorCurrentNumber(
+    int64_t object_store_id,
+    int64_t* current_number) {
+  NOTIMPLEMENTED();
+  return Status::OK();
+}
+
+Status BackingStoreTransactionImpl::MaybeUpdateKeyGeneratorCurrentNumber(
+    int64_t object_store_id,
+    int64_t new_state,
+    bool check_current) {
+  NOTIMPLEMENTED();
+  return Status::OK();
+}
+
+Status BackingStoreTransactionImpl::KeyExistsInObjectStore(
+    int64_t object_store_id,
+    const blink::IndexedDBKey& key,
+    BackingStore::RecordIdentifier* found_record_identifier,
+    bool* found) {
+  NOTIMPLEMENTED();
+  return Status::OK();
+}
+
+Status BackingStoreTransactionImpl::PutIndexDataForRecord(
+    int64_t object_store_id,
+    int64_t index_id,
+    const blink::IndexedDBKey& key,
+    const BackingStore::RecordIdentifier& record) {
+  NOTIMPLEMENTED();
+  return Status::OK();
+}
+
+Status BackingStoreTransactionImpl::GetPrimaryKeyViaIndex(
+    int64_t object_store_id,
+    int64_t index_id,
+    const blink::IndexedDBKey& key,
+    std::unique_ptr<blink::IndexedDBKey>* primary_key) {
+  NOTIMPLEMENTED();
+  return Status::OK();
+}
+
+Status BackingStoreTransactionImpl::KeyExistsInIndex(
+    int64_t object_store_id,
+    int64_t index_id,
+    const blink::IndexedDBKey& key,
+    std::unique_ptr<blink::IndexedDBKey>* found_primary_key,
+    bool* exists) {
+  NOTIMPLEMENTED();
+  return Status::OK();
+}
+
+std::unique_ptr<BackingStore::Cursor>
+BackingStoreTransactionImpl::OpenObjectStoreKeyCursor(
+    int64_t object_store_id,
+    const blink::IndexedDBKeyRange& key_range,
+    blink::mojom::IDBCursorDirection,
+    Status*) {
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
+std::unique_ptr<BackingStore::Cursor>
+BackingStoreTransactionImpl::OpenObjectStoreCursor(
+    int64_t object_store_id,
+    const blink::IndexedDBKeyRange& key_range,
+    blink::mojom::IDBCursorDirection,
+    Status*) {
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
+std::unique_ptr<BackingStore::Cursor>
+BackingStoreTransactionImpl::OpenIndexKeyCursor(
+    int64_t object_store_id,
+    int64_t index_id,
+    const blink::IndexedDBKeyRange& key_range,
+    blink::mojom::IDBCursorDirection,
+    Status*) {
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
+std::unique_ptr<BackingStore::Cursor>
+BackingStoreTransactionImpl::OpenIndexCursor(
+    int64_t object_store_id,
+    int64_t index_id,
+    const blink::IndexedDBKeyRange& key_range,
+    blink::mojom::IDBCursorDirection,
+    Status*) {
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
+BackingStoreVersionChangeTransaction::BackingStoreVersionChangeTransaction(
+    base::WeakPtr<BackingStoreDatabaseImpl> db,
+    BackingStoreDatabaseImpl::UpgradePassKey pass_key)
+    : BackingStoreTransactionImpl(db), pass_key_(std::move(pass_key)) {}
+
+BackingStoreVersionChangeTransaction::~BackingStoreVersionChangeTransaction() =
+    default;
+
+void BackingStoreVersionChangeTransaction::Rollback() {
+  BackingStoreTransactionImpl::Rollback();
+  db_->RollbackUpgrade(pass_key_);
+}
+
+Status BackingStoreVersionChangeTransaction::SetDatabaseVersion(
+    int64_t version) {
+  return db_->SetDatabaseVersion(pass_key_, version);
+}
+
+}  // namespace content::indexed_db::sqlite
diff --git a/content/browser/indexed_db/instance/sqlite/backing_store_transaction_impl.h b/content/browser/indexed_db/instance/sqlite/backing_store_transaction_impl.h
new file mode 100644
index 0000000..5d97093e
--- /dev/null
+++ b/content/browser/indexed_db/instance/sqlite/backing_store_transaction_impl.h
@@ -0,0 +1,135 @@
+// 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 CONTENT_BROWSER_INDEXED_DB_INSTANCE_SQLITE_BACKING_STORE_TRANSACTION_IMPL_H_
+#define CONTENT_BROWSER_INDEXED_DB_INSTANCE_SQLITE_BACKING_STORE_TRANSACTION_IMPL_H_
+
+#include "base/memory/weak_ptr.h"
+#include "content/browser/indexed_db/instance/backing_store.h"
+#include "content/browser/indexed_db/instance/sqlite/backing_store_database_impl.h"
+#include "sql/transaction.h"
+
+namespace content::indexed_db::sqlite {
+
+class BackingStoreTransactionImpl : public BackingStore::Transaction {
+ public:
+  explicit BackingStoreTransactionImpl(
+      base::WeakPtr<BackingStoreDatabaseImpl> db);
+  BackingStoreTransactionImpl(const BackingStoreTransactionImpl&) = delete;
+  BackingStoreTransactionImpl& operator=(const BackingStoreTransactionImpl&) =
+      delete;
+  ~BackingStoreTransactionImpl() override;
+
+  // BackingStore::Transaction:
+  void Begin(std::vector<PartitionedLock> locks) override;
+  Status CommitPhaseOne(BlobWriteCallback callback) override;
+  Status CommitPhaseTwo() override;
+  void Rollback() override;
+  void Reset() override {}
+  Status SetDatabaseVersion(int64_t version) override;
+  Status CreateObjectStore(int64_t object_store_id,
+                           const std::u16string& name,
+                           blink::IndexedDBKeyPath key_path,
+                           bool auto_increment) override;
+  Status DeleteObjectStore(int64_t object_store_id) override;
+  Status RenameObjectStore(int64_t object_store_id,
+                           const std::u16string& new_name) override;
+  Status ClearObjectStore(int64_t object_store_id) override;
+  Status CreateIndex(int64_t object_store_id,
+                     int64_t index_id,
+                     const std::u16string& name,
+                     blink::IndexedDBKeyPath key_path,
+                     bool is_unique,
+                     bool is_multi_entry) override;
+  Status DeleteIndex(int64_t object_store_id, int64_t index_id) override;
+  Status RenameIndex(int64_t object_store_id,
+                     int64_t index_id,
+                     const std::u16string& new_name) override;
+  Status GetRecord(int64_t object_store_id,
+                   const blink::IndexedDBKey& key,
+                   IndexedDBValue* record) override;
+  Status PutRecord(int64_t object_store_id,
+                   const blink::IndexedDBKey& key,
+                   IndexedDBValue* value,
+                   BackingStore::RecordIdentifier* record) override;
+  Status DeleteRange(int64_t object_store_id,
+                     const blink::IndexedDBKeyRange&) override;
+  Status GetKeyGeneratorCurrentNumber(int64_t object_store_id,
+                                      int64_t* current_number) override;
+  Status MaybeUpdateKeyGeneratorCurrentNumber(int64_t object_store_id,
+                                              int64_t new_state,
+                                              bool check_current) override;
+  Status KeyExistsInObjectStore(
+      int64_t object_store_id,
+      const blink::IndexedDBKey& key,
+      BackingStore::RecordIdentifier* found_record_identifier,
+      bool* found) override;
+  Status PutIndexDataForRecord(
+      int64_t object_store_id,
+      int64_t index_id,
+      const blink::IndexedDBKey& key,
+      const BackingStore::RecordIdentifier& record) override;
+  Status GetPrimaryKeyViaIndex(
+      int64_t object_store_id,
+      int64_t index_id,
+      const blink::IndexedDBKey& key,
+      std::unique_ptr<blink::IndexedDBKey>* primary_key) override;
+  Status KeyExistsInIndex(
+      int64_t object_store_id,
+      int64_t index_id,
+      const blink::IndexedDBKey& key,
+      std::unique_ptr<blink::IndexedDBKey>* found_primary_key,
+      bool* exists) override;
+  std::unique_ptr<BackingStore::Cursor> OpenObjectStoreKeyCursor(
+      int64_t object_store_id,
+      const blink::IndexedDBKeyRange& key_range,
+      blink::mojom::IDBCursorDirection,
+      Status*) override;
+  std::unique_ptr<BackingStore::Cursor> OpenObjectStoreCursor(
+      int64_t object_store_id,
+      const blink::IndexedDBKeyRange& key_range,
+      blink::mojom::IDBCursorDirection,
+      Status*) override;
+  std::unique_ptr<BackingStore::Cursor> OpenIndexKeyCursor(
+      int64_t object_store_id,
+      int64_t index_id,
+      const blink::IndexedDBKeyRange& key_range,
+      blink::mojom::IDBCursorDirection,
+      Status*) override;
+  std::unique_ptr<BackingStore::Cursor> OpenIndexCursor(
+      int64_t object_store_id,
+      int64_t index_id,
+      const blink::IndexedDBKeyRange& key_range,
+      blink::mojom::IDBCursorDirection,
+      Status*) override;
+
+ protected:
+  base::WeakPtr<BackingStoreDatabaseImpl> db_;
+  sql::Transaction transaction_;
+};
+
+class BackingStoreVersionChangeTransaction
+    : public BackingStoreTransactionImpl {
+ public:
+  BackingStoreVersionChangeTransaction(
+      base::WeakPtr<BackingStoreDatabaseImpl> db,
+      BackingStoreDatabaseImpl::UpgradePassKey pass_key);
+  BackingStoreVersionChangeTransaction(
+      const BackingStoreVersionChangeTransaction&) = delete;
+  BackingStoreVersionChangeTransaction& operator=(
+      const BackingStoreVersionChangeTransaction&) = delete;
+  ~BackingStoreVersionChangeTransaction() override;
+
+  // BackingStore::Transaction:
+  void Rollback() override;
+  Status SetDatabaseVersion(int64_t version) override;
+  // TODO(crbug.com/40253999): Implement the other relevant methods.
+
+ private:
+  BackingStoreDatabaseImpl::UpgradePassKey pass_key_;
+};
+
+}  // namespace content::indexed_db::sqlite
+
+#endif  // CONTENT_BROWSER_INDEXED_DB_INSTANCE_SQLITE_BACKING_STORE_TRANSACTION_IMPL_H_
diff --git a/content/browser/media/capture/desktop_capture_device.cc b/content/browser/media/capture/desktop_capture_device.cc
index 03c7429..a6a63ac 100644
--- a/content/browser/media/capture/desktop_capture_device.cc
+++ b/content/browser/media/capture/desktop_capture_device.cc
@@ -57,6 +57,10 @@
 #include "third_party/webrtc/modules/desktop_capture/mouse_cursor_monitor.h"
 #include "ui/gfx/icc_profile.h"
 
+#if BUILDFLAG(IS_WIN)
+#include "base/win/windows_version.h"
+#endif
+
 namespace content {
 
 namespace {
@@ -151,6 +155,43 @@
   }
 }
 
+#if BUILDFLAG(IS_WIN)
+bool IsWgcEnabledForScreenCapture() {
+  bool enabled =
+      base::FeatureList::IsEnabled(features::kWebRtcAllowWgcScreenCapturer);
+  if (enabled) {
+    return true;
+  }
+
+  if (base::FeatureList::GetInstance() &&
+      base::FeatureList::GetInstance()->IsFeatureOverridden(
+          features::kWebRtcAllowWgcScreenCapturer.name)) {
+    return enabled;
+  }
+
+  // Starting from WIN11 24H2 (build 26100), the Capture API returns empty
+  // frame when the captured content is unchanged, helping to maintain
+  // performance for 0Hz capture scenarios.
+  enabled = (base::win::GetVersion() >= base::win::Version::WIN11_24H2);
+  return enabled;
+}
+
+bool IsWgcZeroHzEnabledForScreenCapture() {
+  bool enabled =
+      base::FeatureList::IsEnabled(features::kWebRtcAllowWgcScreenZeroHz);
+  if (enabled) {
+    return true;
+  }
+
+  if (base::FeatureList::GetInstance() &&
+      base::FeatureList::GetInstance()->IsFeatureOverridden(
+          features::kWebRtcAllowWgcScreenZeroHz.name)) {
+    return enabled;
+  }
+  return IsWgcEnabledForScreenCapture();
+}
+#endif  // BUILDFLAG(IS_WIN)
+
 // Helper class which request that the system-global Windows timer interrupt
 // frequency be raised at construction. The corresponding deactivation is done
 // at destruction. How high the frequency is raised depends on the system's
@@ -871,7 +912,7 @@
   // set to true. GDI does not use this option.
   options.set_prefer_cursor_embedded(true);
 
-  if (base::FeatureList::IsEnabled(features::kWebRtcAllowWgcScreenCapturer)) {
+  if (IsWgcEnabledForScreenCapture()) {
     options.set_allow_wgc_screen_capturer(true);
 
     // 0Hz support is by default disabled for WGC but it can be enabled using
@@ -882,8 +923,7 @@
     // has changed and contain one (damage) region corresponding to the complete
     // screen or window being captured if any change is detected.
     if (source.type == DesktopMediaID::TYPE_SCREEN) {
-      options.set_allow_wgc_zero_hertz(
-          base::FeatureList::IsEnabled(features::kWebRtcAllowWgcScreenZeroHz));
+      options.set_allow_wgc_zero_hertz(IsWgcZeroHzEnabledForScreenCapture());
     }
   }
   if (base::FeatureList::IsEnabled(features::kWebRtcAllowWgcWindowCapturer)) {
@@ -1009,12 +1049,10 @@
 #endif
   bool zero_hertz_is_supported = true;
 #if BUILDFLAG(IS_WIN)
-  const bool wgc_screen_zero_hertz =
-      base::FeatureList::IsEnabled(features::kWebRtcAllowWgcScreenZeroHz);
+  const bool wgc_screen_zero_hertz = IsWgcZeroHzEnabledForScreenCapture();
   const bool wgc_window_zero_hertz =
       base::FeatureList::IsEnabled(features::kWebRtcAllowWgcWindowZeroHz);
-  const bool wgc_screen_capturer =
-      base::FeatureList::IsEnabled(features::kWebRtcAllowWgcScreenCapturer);
+  const bool wgc_screen_capturer = IsWgcEnabledForScreenCapture();
   const bool wgc_window_capturer =
       base::FeatureList::IsEnabled(features::kWebRtcAllowWgcWindowCapturer);
   if (!wgc_window_capturer && !wgc_screen_capturer) {
diff --git a/content/browser/preloading/prerender/prerender_browsertest.cc b/content/browser/preloading/prerender/prerender_browsertest.cc
index e8d7389..1fd69fc 100644
--- a/content/browser/preloading/prerender/prerender_browsertest.cc
+++ b/content/browser/preloading/prerender/prerender_browsertest.cc
@@ -4101,8 +4101,9 @@
 
  private:
   void DidFinishNavigation(NavigationHandle* handle) override {
-    if (handle->GetURL() != url_)
+    if (handle->GetURL() != url_) {
       return;
+    }
     redirect_chain_ = handle->GetRedirectChain();
   }
 
@@ -5409,8 +5410,9 @@
 
  private:
   void DidStartNavigation(NavigationHandle* handle) override {
-    if (handle->GetURL() != url_)
+    if (handle->GetURL() != url_) {
       return;
+    }
     did_navigate_ = true;
     was_activation_ = handle->IsPrerenderedPageActivation();
   }
@@ -5470,20 +5472,16 @@
   // WillStartRequest into all new navigations.
   ShellContentBrowserClient::Get()
       ->set_create_throttles_for_navigation_callback(base::BindLambdaForTesting(
-          [&throttle](NavigationHandle* handle)
-              -> std::vector<std::unique_ptr<NavigationThrottle>> {
-            std::vector<std::unique_ptr<NavigationThrottle>> throttles;
-
-            auto throttle_ptr =
-                std::make_unique<TestNavigationThrottle>(handle);
+          [&throttle](NavigationThrottleRegistry& registry) -> void {
+            auto throttle_ptr = std::make_unique<TestNavigationThrottle>(
+                &registry.GetNavigationHandle());
             CHECK(!throttle);
             throttle = throttle_ptr.get();
             throttle_ptr->SetResponse(
                 TestNavigationThrottle::WILL_START_REQUEST,
                 TestNavigationThrottle::SYNCHRONOUS, NavigationThrottle::DEFER);
 
-            throttles.push_back(std::move(throttle_ptr));
-            return throttles;
+            registry.AddThrottle(std::move(throttle_ptr));
           }));
 
   // Start a prerender and ensure that a NavigationThrottle can defer the
@@ -5768,13 +5766,11 @@
   // before PrerenderSubframeNavigationThrottle.
   ShellContentBrowserClient::Get()
       ->set_create_throttles_for_navigation_callback(base::BindLambdaForTesting(
-          [](NavigationHandle* handle)
-              -> std::vector<std::unique_ptr<NavigationThrottle>> {
-            std::vector<std::unique_ptr<NavigationThrottle>> throttles;
-            throttles.push_back(
+          [](NavigationThrottleRegistry& registry) -> void {
+            registry.AddThrottle(
                 std::make_unique<
-                    TestPrerenderCancellerSubframeNavigationThrottle>(handle));
-            return throttles;
+                    TestPrerenderCancellerSubframeNavigationThrottle>(
+                    &registry.GetNavigationHandle()));
           }));
 
   // Use ExecuteScriptAsync instead of EvalJs as inserted cross-origin iframe
@@ -6654,8 +6650,9 @@
   // Skip the test when the block type is kCertError. With the type, this test
   // times out due to https://crbug.com/1311887.
   // TODO(crbug.com/40220378): Enable the test with kCertError.
-  if (GetParam() == SSLPrerenderTestErrorBlockType::kCertError)
+  if (GetParam() == SSLPrerenderTestErrorBlockType::kCertError) {
     return;
+  }
 
   // Load an initial page and register a service worker that intercepts
   // resources requests.
@@ -8061,8 +8058,9 @@
 
   // Confirm that all the other prerender hosts are successfully cancelled.
   for (auto& url : prerender_urls) {
-    if (url == prerender_urls[0])
+    if (url == prerender_urls[0]) {
       continue;
+    }
     EXPECT_TRUE(GetHostForUrl(url).is_null());
   }
 
@@ -11999,10 +11997,11 @@
   void FrameDisplayStateChanged(RenderFrameHost* host,
                                 bool is_display_none) override {
     if (host == target_host_) {
-      if (callback_)
+      if (callback_) {
         std::move(callback_).Run();
-      else
+      } else {
         changed_count_++;
+      }
     }
   }
 
@@ -12509,8 +12508,9 @@
     // Note: Checking the origin of `request.GetURL()` doesn't work here because
     // the host part of the URL is translated (e.g., "a.test" to "127.0.0.1")
     // based on the host resolver rule before this point.
-    if (request.relative_url.find("cors") == std::string::npos)
+    if (request.relative_url.find("cors") == std::string::npos) {
       return nullptr;
+    }
 
     // Serves a fake response with the ACAO header.
     auto response = std::make_unique<net::test_server::BasicHttpResponse>();
@@ -13257,8 +13257,9 @@
   // child frames.
   size_t child_frame_count = 0;
   prerendered_rfh->ForEachRenderFrameHostImpl([&](RenderFrameHostImpl* rfh) {
-    if (rfh != prerendered_rfh)
+    if (rfh != prerendered_rfh) {
       child_frame_count++;
+    }
   });
   EXPECT_EQ(0lu, child_frame_count);
 
@@ -13444,8 +13445,9 @@
 
   static std::unique_ptr<net::test_server::HttpResponse> HandleRequest(
       const net::test_server::HttpRequest& request) {
-    if (request.relative_url.find("acceptch") == std::string::npos)
+    if (request.relative_url.find("acceptch") == std::string::npos) {
       return nullptr;
+    }
 
     // Serve a response indicating clients to provide full version of UA.
     auto response = std::make_unique<net::test_server::BasicHttpResponse>();
diff --git a/content/browser/renderer_host/navigation_request_browsertest.cc b/content/browser/renderer_host/navigation_request_browsertest.cc
index 552a481..f16f62c 100644
--- a/content/browser/renderer_host/navigation_request_browsertest.cc
+++ b/content/browser/renderer_host/navigation_request_browsertest.cc
@@ -272,32 +272,36 @@
   TestNavigationThrottle* navigation_throttle() { return navigation_throttle_; }
 
   void WaitForThrottleWillStart() {
-    if (will_start_called_)
+    if (will_start_called_) {
       return;
+    }
     will_start_loop_runner_ = new MessageLoopRunner();
     will_start_loop_runner_->Run();
     will_start_loop_runner_ = nullptr;
   }
 
   void WaitForThrottleWillRedirect() {
-    if (will_redirect_called_)
+    if (will_redirect_called_) {
       return;
+    }
     will_redirect_loop_runner_ = new MessageLoopRunner();
     will_redirect_loop_runner_->Run();
     will_redirect_loop_runner_ = nullptr;
   }
 
   void WaitForThrottleWillFail() {
-    if (will_fail_called_)
+    if (will_fail_called_) {
       return;
+    }
     will_fail_loop_runner_ = new MessageLoopRunner();
     will_fail_loop_runner_->Run();
     will_fail_loop_runner_ = nullptr;
   }
 
   void WaitForThrottleWillProcess() {
-    if (will_process_called_)
+    if (will_process_called_) {
       return;
+    }
     will_process_loop_runner_ = new MessageLoopRunner();
     will_process_loop_runner_->Run();
     will_process_loop_runner_ = nullptr;
@@ -314,10 +318,11 @@
 
   void Continue(NavigationThrottle::ThrottleCheckResult result) {
     ASSERT_NE(NavigationThrottle::DEFER, result.action());
-    if (result.action() == NavigationThrottle::PROCEED)
+    if (result.action() == NavigationThrottle::PROCEED) {
       navigation_throttle()->ResumeNavigation();
-    else
+    } else {
       navigation_throttle()->CancelNavigation(result);
+    }
   }
 
   int will_start_called() { return will_start_called_; }
@@ -333,26 +338,30 @@
  protected:
   virtual void DidCallWillStartRequest() {
     will_start_called_++;
-    if (will_start_loop_runner_)
+    if (will_start_loop_runner_) {
       will_start_loop_runner_->Quit();
+    }
   }
 
   virtual void DidCallWillRedirectRequest() {
     will_redirect_called_++;
-    if (will_redirect_loop_runner_)
+    if (will_redirect_loop_runner_) {
       will_redirect_loop_runner_->Quit();
+    }
   }
 
   virtual void DidCallWillFailRequest() {
     will_fail_called_++;
-    if (will_fail_loop_runner_)
+    if (will_fail_loop_runner_) {
       will_fail_loop_runner_->Quit();
+    }
   }
 
   virtual void DidCallWillProcessResponse() {
     will_process_called_++;
-    if (will_process_loop_runner_)
+    if (will_process_loop_runner_) {
       will_process_loop_runner_->Quit();
+    }
   }
 
   virtual void DidCallWillCommitWithoutUrlLoader() {
@@ -365,8 +374,9 @@
  private:
   void DidStartNavigation(NavigationHandle* handle) override {
     if (!expected_start_url_.is_empty() &&
-        handle->GetURL() != expected_start_url_)
+        handle->GetURL() != expected_start_url_) {
       return;
+    }
 
     std::unique_ptr<NavigationThrottle> throttle(new TestNavigationThrottle(
         handle, will_start_result_, will_redirect_result_, will_fail_result_,
@@ -391,11 +401,13 @@
   }
 
   void DidFinishNavigation(NavigationHandle* handle) override {
-    if (!navigation_throttle_)
+    if (!navigation_throttle_) {
       return;
+    }
 
-    if (handle == navigation_throttle_->navigation_handle())
+    if (handle == navigation_throttle_->navigation_handle()) {
       navigation_throttle_ = nullptr;
+    }
   }
 
   NavigationThrottle::ThrottleCheckResult will_start_result_;
@@ -2663,19 +2675,17 @@
   // |client_throttle| when its registered.
   content::ShellContentBrowserClient::Get()
       ->set_create_throttles_for_navigation_callback(base::BindLambdaForTesting(
-          [&client_throttle](content::NavigationHandle* handle)
-              -> std::vector<std::unique_ptr<content::NavigationThrottle>> {
-            std::vector<std::unique_ptr<content::NavigationThrottle>> throttles;
+          [&client_throttle](
+              content::NavigationThrottleRegistry& registry) -> void {
             std::unique_ptr<TestNavigationThrottle> throttle(
                 new TestNavigationThrottle(
-                    handle, NavigationThrottle::DEFER,
+                    &registry.GetNavigationHandle(), NavigationThrottle::DEFER,
                     NavigationThrottle::PROCEED, NavigationThrottle::PROCEED,
                     NavigationThrottle::PROCEED, NavigationThrottle::PROCEED,
                     base::DoNothing(), base::DoNothing(), base::DoNothing(),
                     base::DoNothing(), base::DoNothing()));
             client_throttle = throttle.get();
-            throttles.push_back(std::move(throttle));
-            return throttles;
+            registry.AddThrottle(std::move(throttle));
           }));
 
   // Add another similar throttle using the installer which will use
@@ -2938,8 +2948,9 @@
     auto add_suffix = [&names](std::vector<std::string> suffixes) {
       size_t original_size = names.size();
       for (size_t i = 0; i < original_size; i++) {
-        for (const std::string& suffix : suffixes)
+        for (const std::string& suffix : suffixes) {
           names.push_back(names[i] + suffix);
+        }
       }
     };
     add_suffix({kProcessSuffixes.at(process_type)});
@@ -4493,9 +4504,9 @@
           DCHECK_EQ(navigation_handle->GetNavigatingFrameType(),
                     GetParam() == TestMPArchType::kPrerender
                         ? FrameType::kPrerenderMainFrame
-                        : GetParam() == TestMPArchType::kFencedFrame
-                              ? FrameType::kFencedFrameRoot
-                              : FrameType::kPrimaryMainFrame);
+                    : GetParam() == TestMPArchType::kFencedFrame
+                        ? FrameType::kFencedFrameRoot
+                        : FrameType::kPrimaryMainFrame);
           EXPECT_FALSE(navigation_handle->ShouldUpdateHistory());
         }));
   };
@@ -4689,14 +4700,12 @@
   // to this throttle in `client_throttle` on registration.
   content::ShellContentBrowserClient::Get()
       ->set_create_throttles_for_navigation_callback(base::BindLambdaForTesting(
-          [&client_throttle](content::NavigationHandle* handle)
-              -> std::vector<std::unique_ptr<content::NavigationThrottle>> {
-            std::vector<std::unique_ptr<content::NavigationThrottle>> throttles;
-            auto throttle =
-                std::make_unique<ResponseBodyNavigationThrottle>(handle);
+          [&client_throttle](
+              content::NavigationThrottleRegistry& registry) -> void {
+            auto throttle = std::make_unique<ResponseBodyNavigationThrottle>(
+                &registry.GetNavigationHandle());
             client_throttle = throttle.get();
-            throttles.push_back(std::move(throttle));
-            return throttles;
+            registry.AddThrottle(std::move(throttle));
           }));
 
   // Start navigating.
@@ -4732,14 +4741,12 @@
   // to this throttle in `client_throttle` on registration.
   content::ShellContentBrowserClient::Get()
       ->set_create_throttles_for_navigation_callback(base::BindLambdaForTesting(
-          [&client_throttle](content::NavigationHandle* handle)
-              -> std::vector<std::unique_ptr<content::NavigationThrottle>> {
-            std::vector<std::unique_ptr<content::NavigationThrottle>> throttles;
-            auto throttle =
-                std::make_unique<ResponseBodyNavigationThrottle>(handle);
+          [&client_throttle](
+              content::NavigationThrottleRegistry& registry) -> void {
+            auto throttle = std::make_unique<ResponseBodyNavigationThrottle>(
+                &registry.GetNavigationHandle());
             client_throttle = throttle.get();
-            throttles.push_back(std::move(throttle));
-            return throttles;
+            registry.AddThrottle(std::move(throttle));
           }));
 
   // Start navigating.
@@ -4775,14 +4782,12 @@
   // to this throttle in `client_throttle` on registration.
   content::ShellContentBrowserClient::Get()
       ->set_create_throttles_for_navigation_callback(base::BindLambdaForTesting(
-          [&client_throttle](content::NavigationHandle* handle)
-              -> std::vector<std::unique_ptr<content::NavigationThrottle>> {
-            std::vector<std::unique_ptr<content::NavigationThrottle>> throttles;
-            auto throttle =
-                std::make_unique<ResponseBodyNavigationThrottle>(handle);
+          [&client_throttle](
+              content::NavigationThrottleRegistry& registry) -> void {
+            auto throttle = std::make_unique<ResponseBodyNavigationThrottle>(
+                &registry.GetNavigationHandle());
             client_throttle = throttle.get();
-            throttles.push_back(std::move(throttle));
-            return throttles;
+            registry.AddThrottle(std::move(throttle));
           }));
 
   // Start navigating to a page with a large body (>5 million characters).
diff --git a/content/browser/renderer_host/navigation_request_unittest.cc b/content/browser/renderer_host/navigation_request_unittest.cc
index 10505c2e..d4a96ab 100644
--- a/content/browser/renderer_host/navigation_request_unittest.cc
+++ b/content/browser/renderer_host/navigation_request_unittest.cc
@@ -592,13 +592,11 @@
 };
 
 class ThrottleTestContentBrowserClient : public ContentBrowserClient {
-  std::vector<std::unique_ptr<NavigationThrottle>> CreateThrottlesForNavigation(
+  void CreateThrottlesForNavigation(
       NavigationThrottleRegistry& registry) override {
-    std::vector<std::unique_ptr<NavigationThrottle>> throttle;
     registry.AddThrottle(
         std::make_unique<GetRenderFrameHostOnFailureNavigationThrottle>(
             &registry.GetNavigationHandle()));
-    return throttle;
   }
 };
 
diff --git a/content/browser/renderer_host/navigation_throttle_runner.cc b/content/browser/renderer_host/navigation_throttle_runner.cc
index 455a6c9a..b4f7e218 100644
--- a/content/browser/renderer_host/navigation_throttle_runner.cc
+++ b/content/browser/renderer_host/navigation_throttle_runner.cc
@@ -218,16 +218,7 @@
   // NavigationRequest.
   NavigationRequest* request = static_cast<NavigationRequest*>(delegate_);
 
-  std::vector<std::unique_ptr<NavigationThrottle>> throttles_under_migration =
-      request->GetDelegate()->CreateThrottlesForNavigation(*this);
-  // TODO(https://crbug.com/412524375): Remove `throttles_under_migration` and
-  // following code to move them to the member variable. When the call returns
-  // above, migrated throttles are already registered in `throttles_` via the
-  // new NavigationThrottleRegistry interface. So, we cannot assign the returned
-  // vector directly to `throttles_`.
-  throttles_.insert(throttles_.end(),
-                    std::make_move_iterator(throttles_under_migration.begin()),
-                    std::make_move_iterator(throttles_under_migration.end()));
+  request->GetDelegate()->CreateThrottlesForNavigation(*this);
 
   // Check for renderer-inititated main frame navigations to blocked URL schemes
   // (data, filesystem). This is done early as it may block the main frame
diff --git a/content/browser/renderer_host/navigator_delegate.h b/content/browser/renderer_host/navigator_delegate.h
index dd41492b..645edb8 100644
--- a/content/browser/renderer_host/navigator_delegate.h
+++ b/content/browser/renderer_host/navigator_delegate.h
@@ -120,12 +120,8 @@
   // Returns the NavigationThrottles to add to this navigation. Normally these
   // are defined by the content/ embedder, except in the case of interstitials
   // where no NavigationThrottles are added to the navigation.
-  // TODO(https://crbug.com/412524375): New code should add a NavigationThrottle
-  // via the given `registry` instead of returning a NavigationThrottle from
-  // this method. Once all existing NavigationThrottles are migrated, the
-  // returned type will be changed to `void`.
-  virtual std::vector<std::unique_ptr<NavigationThrottle>>
-  CreateThrottlesForNavigation(NavigationThrottleRegistry& registry) = 0;
+  virtual void CreateThrottlesForNavigation(
+      NavigationThrottleRegistry& registry) = 0;
 
   // Returns commit deferring conditions to add to this navigation.
   virtual std::vector<std::unique_ptr<CommitDeferringCondition>>
diff --git a/content/browser/service_worker/service_worker_quota_client.cc b/content/browser/service_worker/service_worker_quota_client.cc
index d3842b9..afd55e7 100644
--- a/content/browser/service_worker/service_worker_quota_client.cc
+++ b/content/browser/service_worker/service_worker_quota_client.cc
@@ -18,7 +18,6 @@
 #include "third_party/blink/public/mojom/quota/quota_types.mojom.h"
 #include "url/origin.h"
 
-using ::blink::mojom::StorageType;
 using ::storage::mojom::QuotaClient;
 
 namespace content {
diff --git a/content/browser/storage_partition_impl.cc b/content/browser/storage_partition_impl.cc
index b64480c..0698239c 100644
--- a/content/browser/storage_partition_impl.cc
+++ b/content/browser/storage_partition_impl.cc
@@ -2018,15 +2018,16 @@
     // routing_id are invalid. It can't be added yet because somehow routing_id
     // is valid here.
     if (service_worker_context_->context()) {
-      auto* container_host = service_worker_context_->context()
-                                 ->service_worker_client_owner()
-                                 .GetServiceWorkerClientByWindowId(*window_id);
-      if (container_host) {
-        if (container_host->GetRenderFrameHostId()) {
-          // Use ServiceWorkerContainerHost's GlobalRenderFrameHostId when
-          // the navigation commit has already started.
+      auto* service_worker_client =
+          service_worker_context_->context()
+              ->service_worker_client_owner()
+              .GetServiceWorkerClientByWindowId(*window_id);
+      if (service_worker_client) {
+        if (service_worker_client->GetRenderFrameHostId()) {
+          // Use ServiceWorkerClient's GlobalRenderFrameHostId when the
+          // navigation commit has already started.
           GlobalRenderFrameHostId render_frame_host_id =
-              container_host->GetRenderFrameHostId();
+              service_worker_client->GetRenderFrameHostId();
           context = URLLoaderNetworkContext::CreateForRenderFrameHost(
               render_frame_host_id);
 
@@ -2035,8 +2036,9 @@
           is_primary_main_frame_navigation = false;
           is_navigation_request = false;
         } else if (NavigationRequest* ongoing_navigation =
-                       container_host->GetOngoingNavigationRequestBeforeCommit(
-                           base::PassKey<StoragePartitionImpl>())) {
+                       service_worker_client
+                           ->GetOngoingNavigationRequestBeforeCommit(
+                               base::PassKey<StoragePartitionImpl>())) {
           // This auth request is for an ongoing navigation controlled
           // by service worker. The navigation request can be nullptr if user
           // has closed the WebContents.
@@ -2303,20 +2305,22 @@
     // routing_id are invalid. It can't be added yet because somehow routing_id
     // is valid here.
     if (service_worker_context_->context()) {
-      auto* container_host = service_worker_context_->context()
-                                 ->service_worker_client_owner()
-                                 .GetServiceWorkerClientByWindowId(*window_id);
-      if (container_host) {
-        if (container_host->GetRenderFrameHostId()) {
-          // Use ServiceWorkerContainerHost's GlobalRenderFrameHostId when
-          // the navigation commit has already started.
+      auto* service_worker_client =
+          service_worker_context_->context()
+              ->service_worker_client_owner()
+              .GetServiceWorkerClientByWindowId(*window_id);
+      if (service_worker_client) {
+        if (service_worker_client->GetRenderFrameHostId()) {
+          // Use ServiceWorkerClient's GlobalRenderFrameHostId when the
+          // navigation commit has already started.
           GlobalRenderFrameHostId render_frame_host_id =
-              container_host->GetRenderFrameHostId();
+              service_worker_client->GetRenderFrameHostId();
           context = URLLoaderNetworkContext::CreateForRenderFrameHost(
               render_frame_host_id);
         } else if (NavigationRequest* ongoing_navigation =
-                       container_host->GetOngoingNavigationRequestBeforeCommit(
-                           base::PassKey<StoragePartitionImpl>())) {
+                       service_worker_client
+                           ->GetOngoingNavigationRequestBeforeCommit(
+                               base::PassKey<StoragePartitionImpl>())) {
           // This certification request is for an ongoing navigation.
           // Overwrite the context; set `type` to kNavigationRequestContext.
           // TODO(crbug.com/40784852): Optimize locating logic.
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 45b84cb..6d39dcc1 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -9534,13 +9534,12 @@
   return GetPageVisibilityState() == PageVisibilityState::kHidden;
 }
 
-std::vector<std::unique_ptr<NavigationThrottle>>
-WebContentsImpl::CreateThrottlesForNavigation(
+void WebContentsImpl::CreateThrottlesForNavigation(
     NavigationThrottleRegistry& registry) {
   OPTIONAL_TRACE_EVENT1("content",
                         "WebContentsImpl::CreateThrottlesForNavigation",
                         "navigation", registry.GetNavigationHandle());
-  return GetContentClient()->browser()->CreateThrottlesForNavigation(registry);
+  GetContentClient()->browser()->CreateThrottlesForNavigation(registry);
 }
 
 std::vector<std::unique_ptr<CommitDeferringCondition>>
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index eabf30ff..98fdc7f7 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -1067,7 +1067,7 @@
       bool is_outermost_main_frame_navigation) override;
   const blink::UserAgentOverride& GetUserAgentOverride(
       FrameTree& frame_tree) override;
-  std::vector<std::unique_ptr<NavigationThrottle>> CreateThrottlesForNavigation(
+  void CreateThrottlesForNavigation(
       NavigationThrottleRegistry& registry) override;
   std::vector<std::unique_ptr<CommitDeferringCondition>>
   CreateDeferringConditionsForNavigationCommit(
diff --git a/content/browser/webid/federated_auth_request_impl.cc b/content/browser/webid/federated_auth_request_impl.cc
index e5b951c..1bd76e3 100644
--- a/content/browser/webid/federated_auth_request_impl.cc
+++ b/content/browser/webid/federated_auth_request_impl.cc
@@ -469,8 +469,12 @@
 FederatedAuthRequestImpl::IdentityProviderGetInfo::IdentityProviderGetInfo(
     blink::mojom::IdentityProviderRequestOptionsPtr provider,
     blink::mojom::RpContext rp_context,
-    blink::mojom::RpMode rp_mode)
-    : provider(std::move(provider)), rp_context(rp_context), rp_mode(rp_mode) {}
+    blink::mojom::RpMode rp_mode,
+    std::optional<blink::mojom::Format> format)
+    : provider(std::move(provider)),
+      rp_context(rp_context),
+      rp_mode(rp_mode),
+      format(format) {}
 
 FederatedAuthRequestImpl::IdentityProviderGetInfo::~IdentityProviderGetInfo() =
     default;
@@ -485,6 +489,7 @@
   provider = other.provider->Clone();
   rp_context = other.rp_context;
   rp_mode = other.rp_mode;
+  format = other.format;
   return *this;
 }
 
@@ -493,12 +498,14 @@
     IdpNetworkRequestManager::Endpoints endpoints,
     IdentityProviderMetadata metadata,
     blink::mojom::RpContext rp_context,
-    blink::mojom::RpMode rp_mode)
+    blink::mojom::RpMode rp_mode,
+    std::optional<blink::mojom::Format> format)
     : provider(provider->Clone()),
       endpoints(std::move(endpoints)),
       metadata(std::move(metadata)),
       rp_context(rp_context),
-      rp_mode(rp_mode) {}
+      rp_mode(rp_mode),
+      format(format) {}
 
 FederatedAuthRequestImpl::IdentityProviderInfo::~IdentityProviderInfo() =
     default;
@@ -511,6 +518,7 @@
   rp_context = other.rp_context;
   rp_mode = other.rp_mode;
   data = other.data;
+  format = other.format;
 }
 
 FederatedAuthRequestImpl::FederatedAuthRequestImpl(
@@ -1018,9 +1026,11 @@
       blink::mojom::RpContext rp_context = idp_get_params_ptr->context;
       blink::mojom::RpMode rp_mode = idp_get_params_ptr->mode;
       const GURL& idp_config_url = idp_ptr->config->config_url;
+      std::optional<blink::mojom::Format> format =
+          IsFedCmDelegationEnabled() ? idp_ptr->format : std::nullopt;
       token_request_get_infos_.emplace(
-          idp_config_url,
-          IdentityProviderGetInfo(std::move(idp_ptr), rp_context, rp_mode));
+          idp_config_url, IdentityProviderGetInfo(std::move(idp_ptr),
+                                                  rp_context, rp_mode, format));
     }
   }
   if (any_idp_has_parameters || any_idp_has_custom_scopes) {
@@ -1296,7 +1306,8 @@
             get_info_it->second.provider, std::move(fetch_result.endpoints),
             fetch_result.metadata ? std::move(*fetch_result.metadata)
                                   : IdentityProviderMetadata(),
-            get_info_it->second.rp_context, get_info_it->second.rp_mode);
+            get_info_it->second.rp_context, get_info_it->second.rp_mode,
+            get_info_it->second.format);
 
     if (fetch_result.error) {
       const FederatedProviderFetcher::FetchError& fetch_error =
@@ -1535,7 +1546,7 @@
       ClientMetadata{client_metadata.terms_of_service_url,
                      client_metadata.privacy_policy_url,
                      client_metadata.brand_icon_url, rp_brand_icon},
-      idp_info->rp_context, disclosure_fields,
+      idp_info->rp_context, idp_info->format, disclosure_fields,
       /*has_login_status_mismatch=*/false);
   for (auto& account : accounts) {
     account->identity_provider = idp_info->data;
@@ -2016,7 +2027,8 @@
   idp_info->data = base::MakeRefCounted<IdentityProviderData>(
       idp_for_display, idp_info->metadata,
       ClientMetadata{GURL(), GURL(), GURL(), gfx::Image()},
-      idp_info->rp_context, GetDisclosureFields(*idp_info->provider),
+      idp_info->rp_context, idp_info->format,
+      GetDisclosureFields(*idp_info->provider),
       /*has_login_status_mismatch=*/true);
   idp_infos_[idp_config_url] = std::move(idp_info);
 
diff --git a/content/browser/webid/federated_auth_request_impl.h b/content/browser/webid/federated_auth_request_impl.h
index 6b90f6c..688067a 100644
--- a/content/browser/webid/federated_auth_request_impl.h
+++ b/content/browser/webid/federated_auth_request_impl.h
@@ -159,7 +159,8 @@
   struct IdentityProviderGetInfo {
     IdentityProviderGetInfo(blink::mojom::IdentityProviderRequestOptionsPtr,
                             blink::mojom::RpContext rp_context,
-                            blink::mojom::RpMode rp_mode);
+                            blink::mojom::RpMode rp_mode,
+                            std::optional<blink::mojom::Format> format);
     ~IdentityProviderGetInfo();
     IdentityProviderGetInfo(const IdentityProviderGetInfo&);
     IdentityProviderGetInfo& operator=(const IdentityProviderGetInfo& other);
@@ -167,6 +168,7 @@
     blink::mojom::IdentityProviderRequestOptionsPtr provider;
     blink::mojom::RpContext rp_context{blink::mojom::RpContext::kSignIn};
     blink::mojom::RpMode rp_mode{blink::mojom::RpMode::kPassive};
+    std::optional<blink::mojom::Format> format;
   };
 
   struct IdentityProviderInfo {
@@ -174,7 +176,8 @@
                          IdpNetworkRequestManager::Endpoints,
                          IdentityProviderMetadata,
                          blink::mojom::RpContext rp_context,
-                         blink::mojom::RpMode rp_mode);
+                         blink::mojom::RpMode rp_mode,
+                         std::optional<blink::mojom::Format> format);
     ~IdentityProviderInfo();
     IdentityProviderInfo(const IdentityProviderInfo&);
 
@@ -184,6 +187,7 @@
     bool has_failing_idp_signin_status{false};
     blink::mojom::RpContext rp_context{blink::mojom::RpContext::kSignIn};
     blink::mojom::RpMode rp_mode{blink::mojom::RpMode::kPassive};
+    std::optional<blink::mojom::Format> format;
     IdentityProviderDataPtr data;
     gfx::Image decoded_idp_brand_icon;
   };
diff --git a/content/browser/webid/webid_browsertest.cc b/content/browser/webid/webid_browsertest.cc
index 2987d53..58685f59 100644
--- a/content/browser/webid/webid_browsertest.cc
+++ b/content/browser/webid/webid_browsertest.cc
@@ -29,6 +29,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/test/values_test_util.h"
 #include "base/values.h"
+#include "build/build_config.h"
 #include "components/network_session_configurator/common/network_switches.h"
 #include "content/browser/in_memory_federated_permission_context.h"
 #include "content/browser/webid/fake_identity_request_dialog_controller.h"
@@ -2115,6 +2116,9 @@
   EXPECT_EQ(suggestions->size(), 1ul);
 
   auto account = (*suggestions)[0];
+
+  EXPECT_EQ(account->identity_provider->format, blink::mojom::Format::kSdJwt);
+
   source->NotifyAutofillSuggestionAccepted(
       account->identity_provider->idp_metadata.config_url, account->id,
       /*show_modal=*/true, base::NullCallback());
@@ -2139,6 +2143,87 @@
       testing::UnorderedElementsAre("Sam"));
 }
 
+// Flaky on mac, https://crbug.com/415953689
+#if BUILDFLAG(IS_MAC)
+#define MAYBE_ConditionalMediationForMediatedRequest \
+  DISABLED_ConditionalMediationForMediatedRequest
+#else
+#define MAYBE_ConditionalMediationForMediatedRequest \
+  ConditionalMediationForMediatedRequest
+#endif
+IN_PROC_BROWSER_TEST_F(WebIdDelegationBrowserTest,
+                       MAYBE_ConditionalMediationForMediatedRequest) {
+  idp_server()->SetConfigResponseDetails(BuildValidConfigDetails());
+
+  auto mock = std::make_unique<
+      ::testing::NiceMock<MockIdentityRequestDialogController>>();
+  // Keep a copy of the pointer before the std::move.
+  MockIdentityRequestDialogController* controller = mock.get();
+  test_browser_client_->SetIdentityRequestDialogController(std::move(mock));
+
+  auto configURL = BaseIdpUrl();
+
+  base::RunLoop run_loop;
+
+  EXPECT_TRUE(NavigateToURL(shell(), GURL(configURL)));
+
+  std::string script = R"(
+    var token = navigator.credentials.get({
+      mediation: 'conditional',
+      identity: {
+        providers: [{
+          fields: ['name'],
+          configURL: ')" +
+                       configURL + R"(',
+          clientId: 'client_id_1',
+          nonce: '12345',
+        }],
+      },
+    }).then(({token}) => token)
+  )";
+
+  // Await until the accounts are available for autofill.
+  EXPECT_CALL(*controller, NotifyAutofillSourceReadyForTesting)
+      .WillOnce([&run_loop]() { run_loop.Quit(); });
+
+  auto promise = EvalJs(shell(), script, EXECUTE_SCRIPT_NO_RESOLVE_PROMISES);
+
+  run_loop.Run();
+
+  // Gets the pending conditional request.
+  auto* source = FederatedAuthAutofillSource::FromPage(
+      shell()->web_contents()->GetPrimaryPage());
+
+  EXPECT_TRUE(source != nullptr);
+
+  // Gets all the autofill suggestion and selects the first one.
+  auto suggestions = source->GetAutofillSuggestions();
+  EXPECT_TRUE(suggestions);
+  EXPECT_EQ(suggestions->size(), 1ul);
+
+  auto account = (*suggestions)[0];
+
+  // Mediated FedCM has an empty format.
+  EXPECT_EQ(account->identity_provider->format, std::nullopt);
+
+  base::RunLoop callback;
+
+  source->NotifyAutofillSuggestionAccepted(
+      account->identity_provider->idp_metadata.config_url, account->id,
+      /*show_modal=*/false,
+      base::BindLambdaForTesting([&callback](bool accepted) {
+        EXPECT_TRUE(accepted);
+        callback.Quit();
+      }));
+
+  // Wait for the identity provider to return a token.
+  callback.Run();
+
+  // Assert that the conditional mediation request resolved and that
+  // the right token was provided.
+  EXPECT_EQ(std::string(kToken), EvalJs(shell(), "token"));
+}
+
 class WebIdMetricsBrowserTest : public WebIdBrowserTest {
  public:
   void SetUpCommandLine(base::CommandLine* command_line) override {
diff --git a/content/common/content_export.h b/content/common/content_export.h
index 92ad6c7..c4245cfe 100644
--- a/content/common/content_export.h
+++ b/content/common/content_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(CONTENT_IMPLEMENTATION)
 
 #else // defined(WIN32)
-#if defined(CONTENT_IMPLEMENTATION)
 #define CONTENT_EXPORT __attribute__((visibility("default")))
-#else
-#define CONTENT_EXPORT
-#endif
 #endif
 
 #else // defined(COMPONENT_BUILD)
diff --git a/content/public/android/BUILD.gn b/content/public/android/BUILD.gn
index 7743c3f..d96d069 100644
--- a/content/public/android/BUILD.gn
+++ b/content/public/android/BUILD.gn
@@ -129,7 +129,6 @@
     "java/src/org/chromium/content/app/SandboxedProcessService.java",
     "java/src/org/chromium/content/browser/BrowserStartupControllerImpl.java",
     "java/src/org/chromium/content/browser/ChildProcessCreationParamsImpl.java",
-    "java/src/org/chromium/content/browser/ContentChildProcessConstants.java",
     "java/src/org/chromium/content/browser/DeviceUtilsImpl.java",
     "java/src/org/chromium/content/browser/ServicificationStartupUma.java",
     "java/src/org/chromium/content/browser/TracingControllerAndroidImpl.java",
diff --git a/content/public/android/java/src/org/chromium/content/app/ContentChildProcessServiceDelegate.java b/content/public/android/java/src/org/chromium/content/app/ContentChildProcessServiceDelegate.java
index d4d1f86..c34d342 100644
--- a/content/public/android/java/src/org/chromium/content/app/ContentChildProcessServiceDelegate.java
+++ b/content/public/android/java/src/org/chromium/content/app/ContentChildProcessServiceDelegate.java
@@ -29,10 +29,10 @@
 import org.chromium.base.library_loader.LibraryLoader;
 import org.chromium.base.memory.MemoryPressureUma;
 import org.chromium.base.process_launcher.ChildProcessServiceDelegate;
+import org.chromium.base.process_launcher.IChildProcessArgs;
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
 import org.chromium.content.browser.ChildProcessCreationParamsImpl;
-import org.chromium.content.browser.ContentChildProcessConstants;
 import org.chromium.content.common.IGpuProcessCallback;
 import org.chromium.content.common.InputTransferTokenWrapper;
 import org.chromium.content.common.SurfaceWrapper;
@@ -79,18 +79,18 @@
 
     @Override
     public void onConnectionSetup(
-            Bundle connectionBundle, List<IBinder> clientInterfaces, IBinder binderBox) {
+            IChildProcessArgs args, List<IBinder> clientInterfaces, IBinder binderBox) {
         mBinderBox = binderBox;
         mGpuCallback =
                 clientInterfaces != null && !clientInterfaces.isEmpty()
                         ? IGpuProcessCallback.Stub.asInterface(clientInterfaces.get(0))
                         : null;
 
-        mCpuCount = connectionBundle.getInt(ContentChildProcessConstants.EXTRA_CPU_COUNT);
-        mCpuFeatures = connectionBundle.getLong(ContentChildProcessConstants.EXTRA_CPU_FEATURES);
+        mCpuCount = args.cpuCount;
+        mCpuFeatures = args.cpuFeatures;
         assert mCpuCount > 0;
 
-        LibraryLoader.getInstance().getMediator().takeSharedRelrosFromBundle(connectionBundle);
+        LibraryLoader.getInstance().getMediator().takeSharedRelrosFromBundle(args.relroBundle);
     }
 
     @Override
diff --git a/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelperImpl.java b/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelperImpl.java
index 8e53629..a7ecf74 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelperImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelperImpl.java
@@ -17,6 +17,7 @@
 
 import org.jni_zero.CalledByNative;
 import org.jni_zero.JNINamespace;
+import org.jni_zero.JniType;
 import org.jni_zero.NativeMethods;
 
 import org.chromium.base.ApplicationState;
@@ -36,7 +37,8 @@
 import org.chromium.base.process_launcher.ChildProcessConnection;
 import org.chromium.base.process_launcher.ChildProcessConstants;
 import org.chromium.base.process_launcher.ChildProcessLauncher;
-import org.chromium.base.process_launcher.FileDescriptorInfo;
+import org.chromium.base.process_launcher.IChildProcessArgs;
+import org.chromium.base.process_launcher.IFileDescriptorInfo;
 import org.chromium.base.task.PostTask;
 import org.chromium.base.task.TaskTraits;
 import org.chromium.build.annotations.NullMarked;
@@ -174,20 +176,16 @@
                 }
 
                 @Override
-                public void onBeforeConnectionSetup(Bundle connectionBundle) {
+                public void onBeforeConnectionSetup(IChildProcessArgs childProcessArgs) {
                     // Populate the bundle passed to the service setup call with content specific
                     // parameters.
-                    connectionBundle.putInt(
-                            ContentChildProcessConstants.EXTRA_CPU_COUNT, CpuFeatures.getCount());
-                    connectionBundle.putLong(
-                            ContentChildProcessConstants.EXTRA_CPU_FEATURES, CpuFeatures.getMask());
-                    if (sZygoteBundle != null) {
-                        connectionBundle.putAll(sZygoteBundle);
-                    } else {
-                        LibraryLoader.getInstance()
-                                .getMediator()
-                                .putSharedRelrosToBundle(connectionBundle);
+                    childProcessArgs.cpuCount = CpuFeatures.getCount();
+                    childProcessArgs.cpuFeatures = CpuFeatures.getMask();
+                    Bundle relros = sZygoteBundle;
+                    if (relros == null) {
+                        relros = LibraryLoader.getInstance().getMediator().getSharedRelrosBundle();
                     }
+                    childProcessArgs.relroBundle = relros;
                 }
 
                 @Override
@@ -353,29 +351,47 @@
     private boolean mIsSpareRenderer;
 
     @CalledByNative
-    private static @Nullable FileDescriptorInfo makeFdInfo(
-            int id, int fd, boolean autoClose, long offset, long size) {
+    private static IFileDescriptorInfo @Nullable [] makeFdInfos(
+            @JniType("std::vector<int32_t>") int[] ids,
+            @JniType("std::vector<int32_t>") int[] fds,
+            @JniType("std::vector<bool>") boolean[] autoCloses,
+            @JniType("std::vector<int64_t>") long[] offsets,
+            @JniType("std::vector<int64_t>") long[] sizes) {
         assert LauncherThread.runningOnLauncherThread();
-        ParcelFileDescriptor pFd;
-        if (autoClose) {
-            // Adopt the FD, it will be closed when we close the ParcelFileDescriptor.
-            pFd = ParcelFileDescriptor.adoptFd(fd);
-        } else {
-            try {
-                pFd = ParcelFileDescriptor.fromFd(fd);
-            } catch (IOException e) {
-                Log.e(TAG, "Invalid FD provided for process connection, aborting connection.", e);
-                return null;
+        IFileDescriptorInfo[] fileDescriptorInfos = new IFileDescriptorInfo[ids.length];
+        for (int i = 0; i < ids.length; i++) {
+            ParcelFileDescriptor pFd;
+            if (autoCloses[i]) {
+                // Adopt the FD, it will be closed when we close the ParcelFileDescriptor.
+                pFd = ParcelFileDescriptor.adoptFd(fds[i]);
+            } else {
+                try {
+                    pFd = ParcelFileDescriptor.fromFd(fds[i]);
+                } catch (IOException e) {
+                    Log.e(
+                            TAG,
+                            "Invalid FD provided for process connection, id: "
+                                    + ids[i]
+                                    + " fd: "
+                                    + fds[i]);
+                    return null;
+                }
             }
+            IFileDescriptorInfo fileDescriptorInfo = new IFileDescriptorInfo();
+            fileDescriptorInfo.id = ids[i];
+            fileDescriptorInfo.fd = pFd;
+            fileDescriptorInfo.size = sizes[i];
+            fileDescriptorInfo.offset = offsets[i];
+            fileDescriptorInfos[i] = fileDescriptorInfo;
         }
-        return new FileDescriptorInfo(id, pFd, offset, size);
+        return fileDescriptorInfos;
     }
 
     @CalledByNative
     private static ChildProcessLauncherHelperImpl createAndStart(
             long nativePointer,
             String[] commandLine,
-            FileDescriptorInfo[] filesToBeMapped,
+            IFileDescriptorInfo[] filesToBeMapped,
             boolean canUseWarmUpConnection,
             @Nullable IBinder binderBox) {
         assert LauncherThread.runningOnLauncherThread();
@@ -670,7 +686,7 @@
     private ChildProcessLauncherHelperImpl(
             long nativePointer,
             String[] commandLine,
-            FileDescriptorInfo[] filesToBeMapped,
+            IFileDescriptorInfo[] filesToBeMapped,
             boolean sandboxed,
             boolean reducePriorityOnBackground,
             boolean canUseWarmUpConnection,
@@ -1031,7 +1047,7 @@
 
     public static ChildProcessLauncherHelperImpl createAndStartForTesting(
             String[] commandLine,
-            FileDescriptorInfo[] filesToBeMapped,
+            IFileDescriptorInfo[] filesToBeMapped,
             boolean sandboxed,
             boolean reducePriorityOnBackground,
             boolean canUseWarmUpConnection,
diff --git a/content/public/android/java/src/org/chromium/content/browser/ChildProcessRanking.java b/content/public/android/java/src/org/chromium/content/browser/ChildProcessRanking.java
index 16236c1..5706cff 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ChildProcessRanking.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ChildProcessRanking.java
@@ -317,8 +317,10 @@
         if (!mEnableServiceGroupImportance) return;
 
         if (!connection.shouldBeInLowRankGroup()) {
-            if (connection.connection.getGroup() != NO_GROUP) {
-                connection.connection.updateGroupImportance(NO_GROUP, 0);
+            if (connection.connection.getGroup() != NO_GROUP
+                    && connection.connection.updateGroupImportance(NO_GROUP, 0)) {
+                // Rebind a service binding to apply the group importance change.
+                connection.connection.rebind();
             }
             return;
         }
@@ -349,9 +351,15 @@
         // If gap is small, use average.
         // If there is no room left, reshuffle everything.
         if (gap > 2 * FROM_RIGHT) {
-            connection.connection.updateGroupImportance(LOW_RANK_GROUP, right - FROM_RIGHT);
+            if (connection.connection.updateGroupImportance(LOW_RANK_GROUP, right - FROM_RIGHT)) {
+                // Rebind a service binding to apply the group importance change.
+                connection.connection.rebind();
+            }
         } else if (gap > 2) {
-            connection.connection.updateGroupImportance(LOW_RANK_GROUP, left + gap / 2);
+            if (connection.connection.updateGroupImportance(LOW_RANK_GROUP, left + gap / 2)) {
+                // Rebind a service binding to apply the group importance change.
+                connection.connection.rebind();
+            }
         } else {
             reshuffleGroupImportance();
         }
@@ -362,12 +370,24 @@
 
     private void reshuffleGroupImportance() {
         int importance = Integer.MAX_VALUE - FROM_RIGHT;
+        ConnectionWithRank lastUpdatedConnection = null;
         for (int i = mRankings.size() - 1; i >= 0; --i) {
             ConnectionWithRank connection = mRankings.get(i);
             if (!connection.shouldBeInLowRankGroup()) break;
-            connection.connection.updateGroupImportance(LOW_RANK_GROUP, importance);
+            if (connection.connection.updateGroupImportance(LOW_RANK_GROUP, importance)) {
+                if (ContentFeatureList.sGroupRebindingForGroupImportance.isEnabled()) {
+                    lastUpdatedConnection = connection;
+                } else {
+                    // Rebind a service binding to apply the group importance change.
+                    connection.connection.rebind();
+                }
+            }
             importance -= FROM_RIGHT;
         }
+        if (lastUpdatedConnection != null) {
+            // Rebind a service connection in the group to apply the group importance changes.
+            lastUpdatedConnection.connection.rebind();
+        }
     }
 
     private void postRebindHighRankConnectionsIfNeeded() {
diff --git a/content/public/android/java/src/org/chromium/content/browser/ContentChildProcessConstants.java b/content/public/android/java/src/org/chromium/content/browser/ContentChildProcessConstants.java
deleted file mode 100644
index 05cdf99..0000000
--- a/content/public/android/java/src/org/chromium/content/browser/ContentChildProcessConstants.java
+++ /dev/null
@@ -1,25 +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.
-
-package org.chromium.content.browser;
-
-import org.chromium.build.annotations.NullMarked;
-
-/** Constants to be used by child processes. */
-@NullMarked
-public interface ContentChildProcessConstants {
-    // Below are the names for the items placed in the bind or start command intent.
-    // Note that because that intent maybe reused if a service is restarted, none should be process
-    // specific.
-
-    // Below are the names for the items placed in the Bundle passed in the
-    // IChildProcessService.setupConnection call, once the connection has been established.
-
-    // Key for the number of CPU cores.
-    public static final String EXTRA_CPU_COUNT = "com.google.android.apps.chrome.extra.cpu_count";
-
-    // Key for the CPU features mask.
-    public static final String EXTRA_CPU_FEATURES =
-            "com.google.android.apps.chrome.extra.cpu_features";
-}
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/ContentFeatureList.java b/content/public/android/java/src/org/chromium/content_public/browser/ContentFeatureList.java
index 3f3d0b4..d0948fb0 100644
--- a/content/public/android/java/src/org/chromium/content_public/browser/ContentFeatureList.java
+++ b/content/public/android/java/src/org/chromium/content_public/browser/ContentFeatureList.java
@@ -55,6 +55,12 @@
 
     public static final String DIPS_TTL = "DIPSTtl";
 
+    public static final MutableFlagWithSafeDefault sGroupRebindingForGroupImportance =
+            new MutableFlagWithSafeDefault(
+                    ContentFeatureMap.getInstance(),
+                    ContentFeatures.GROUP_REBINDING_FOR_GROUP_IMPORTANCE,
+                    false);
+
     public static final MutableFlagWithSafeDefault sSpareRendererProcessPriority =
             new MutableFlagWithSafeDefault(
                     ContentFeatureMap.getInstance(),
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/ChildProcessLauncherHelperTest.java b/content/public/android/javatests/src/org/chromium/content/browser/ChildProcessLauncherHelperTest.java
index 88782c45..fbdc104 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/ChildProcessLauncherHelperTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/ChildProcessLauncherHelperTest.java
@@ -34,7 +34,7 @@
 import org.chromium.base.library_loader.LibraryProcessType;
 import org.chromium.base.process_launcher.ChildConnectionAllocator;
 import org.chromium.base.process_launcher.ChildProcessConnection;
-import org.chromium.base.process_launcher.FileDescriptorInfo;
+import org.chromium.base.process_launcher.IFileDescriptorInfo;
 import org.chromium.base.test.util.Criteria;
 import org.chromium.base.test.util.CriteriaHelper;
 import org.chromium.base.test.util.DisabledTest;
@@ -498,7 +498,7 @@
                             public ChildProcessLauncherHelperImpl call() {
                                 return ChildProcessLauncherHelperImpl.createAndStartForTesting(
                                         sProcessWaitArguments,
-                                        new FileDescriptorInfo[0],
+                                        new IFileDescriptorInfo[0],
                                         sandboxed,
                                         reducePriorityOnBackground,
                                         canUseWarmUpConnection,
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/ChildProcessLauncherIntegrationTest.java b/content/public/android/javatests/src/org/chromium/content/browser/ChildProcessLauncherIntegrationTest.java
index 62e52f4..946acd08 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/ChildProcessLauncherIntegrationTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/ChildProcessLauncherIntegrationTest.java
@@ -18,6 +18,7 @@
 
 import org.chromium.base.process_launcher.ChildConnectionAllocator;
 import org.chromium.base.process_launcher.ChildProcessConnection;
+import org.chromium.base.process_launcher.IChildProcessArgs;
 import org.chromium.base.test.BaseJUnit4ClassRunner;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.CriteriaHelper;
@@ -241,7 +242,7 @@
         private boolean mCrashServiceCalled;
         private final CountDownLatch mDisconnectedLatch = new CountDownLatch(1);
         // Arguments to setupConnection
-        private Bundle mConnectionBundle;
+        private IChildProcessArgs mChildProcessArgs;
         private List<IBinder> mClientInterfaces;
         private IBinder mBinderBox;
         private ConnectionCallback mConnectionCallback;
@@ -269,14 +270,14 @@
             super.onServiceConnectedOnLauncherThread(service);
             crashServiceForTesting();
             mCrashServiceCalled = true;
-            if (mConnectionBundle != null) {
+            if (mChildProcessArgs != null) {
                 super.setupConnection(
-                        mConnectionBundle,
+                        mChildProcessArgs,
                         mClientInterfaces,
                         mBinderBox,
                         mConnectionCallback,
                         null);
-                mConnectionBundle = null;
+                mChildProcessArgs = null;
                 mClientInterfaces = null;
                 mBinderBox = null;
                 mConnectionCallback = null;
@@ -291,7 +292,7 @@
 
         @Override
         public void setupConnection(
-                Bundle connectionBundle,
+                IChildProcessArgs childProcessArgs,
                 List<IBinder> clientInterfaces,
                 IBinder binderBox,
                 ConnectionCallback connectionCallback,
@@ -300,10 +301,10 @@
             // setupConnection is guaranteed to fail.
             if (mCrashServiceCalled) {
                 super.setupConnection(
-                        connectionBundle, clientInterfaces, binderBox, connectionCallback, null);
+                        childProcessArgs, clientInterfaces, binderBox, connectionCallback, null);
                 return;
             }
-            mConnectionBundle = connectionBundle;
+            mChildProcessArgs = childProcessArgs;
             mClientInterfaces = clientInterfaces;
             mBinderBox = binderBox;
             mConnectionCallback = connectionCallback;
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/ChildProcessLauncherTest.java b/content/public/android/javatests/src/org/chromium/content/browser/ChildProcessLauncherTest.java
index 3659bd7e..490293a 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/ChildProcessLauncherTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/ChildProcessLauncherTest.java
@@ -20,7 +20,8 @@
 import org.chromium.base.process_launcher.ChildConnectionAllocator;
 import org.chromium.base.process_launcher.ChildProcessConnection;
 import org.chromium.base.process_launcher.ChildProcessLauncher;
-import org.chromium.base.process_launcher.FileDescriptorInfo;
+import org.chromium.base.process_launcher.IChildProcessArgs;
+import org.chromium.base.process_launcher.IFileDescriptorInfo;
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.Criteria;
 import org.chromium.base.test.util.CriteriaHelper;
@@ -47,10 +48,6 @@
     private static final String EXTRA_SERVICE_PARAM = "org.chromium.content.browser.SERVICE_EXTRA";
     private static final String EXTRA_SERVICE_PARAM_VALUE = "SERVICE_EXTRA";
 
-    private static final String EXTRA_CONNECTION_PARAM =
-            "org.chromium.content.browser.CONNECTION_EXTRA";
-    private static final String EXTRA_CONNECTION_PARAM_VALUE = "CONNECTION_EXTRA";
-
     private static final int CONNECTION_BLOCK_UNTIL_CONNECTED = 1;
     private static final int CONNECTION_BLOCK_UNTIL_SETUP = 2;
 
@@ -133,7 +130,6 @@
         // Can be accessed after mOnConnectionSetupCalled is signaled.
         private boolean mServiceCreated;
         private Bundle mServiceBundle;
-        private Bundle mConnectionBundle;
 
         // Can be accessed after mOnLoadNativeCalled is signaled.
         private boolean mNativeLibraryLoaded;
@@ -142,11 +138,9 @@
         private String[] mCommandLine;
 
         @Override
-        public void onConnectionSetup(
-                boolean serviceCreatedCalled, Bundle serviceBundle, Bundle connectionBundle) {
+        public void onConnectionSetup(boolean serviceCreatedCalled, Bundle serviceBundle) {
             mServiceCreated = serviceCreatedCalled;
             mServiceBundle = serviceBundle;
-            mConnectionBundle = connectionBundle;
             Assert.assertEquals(0, mOnConnectionSetupHelper.getCallCount());
             mOnConnectionSetupHelper.notifyCalled();
         }
@@ -228,9 +222,7 @@
                     }
 
                     @Override
-                    public void onBeforeConnectionSetup(Bundle connectionBundle) {
-                        connectionBundle.putString(
-                                EXTRA_CONNECTION_PARAM, EXTRA_CONNECTION_PARAM_VALUE);
+                    public void onBeforeConnectionSetup(IChildProcessArgs childProcessArgs) {
                         Assert.assertEquals(0, onBeforeConnectionSetupHelper.getCallCount());
                         onBeforeConnectionSetupHelper.notifyCalled();
                     }
@@ -249,7 +241,7 @@
                 };
 
         final String[] commandLine = new String[] {"--test-param1", "--test-param2"};
-        final FileDescriptorInfo[] filesToBeMapped = new FileDescriptorInfo[0];
+        final IFileDescriptorInfo[] filesToBeMapped = new IFileDescriptorInfo[0];
 
         final IChildProcessBinder childProcessBinder = new IChildProcessBinder();
 
@@ -287,15 +279,11 @@
         childProcessBinder.waitForOnConnectionSetupCalled();
         Assert.assertTrue(childProcessBinder.mServiceCreated);
         Assert.assertNotNull(childProcessBinder.mServiceBundle);
-        Assert.assertNotNull(childProcessBinder.mConnectionBundle);
         if (allocatedConnection) {
             Assert.assertEquals(
                     EXTRA_SERVICE_PARAM_VALUE,
                     childProcessBinder.mServiceBundle.getString(EXTRA_SERVICE_PARAM));
         }
-        Assert.assertEquals(
-                EXTRA_CONNECTION_PARAM_VALUE,
-                childProcessBinder.mConnectionBundle.getString(EXTRA_CONNECTION_PARAM));
 
         // Wait for the client onConnectionEstablished call.
         onConnectionEstablishedHelper.waitForCallback(/* currentCallCount= */ 0);
@@ -519,7 +507,7 @@
                                         LauncherThread.getHandler(),
                                         new ChildProcessLauncher.Delegate() {},
                                         new String[0],
-                                        new FileDescriptorInfo[0],
+                                        new IFileDescriptorInfo[0],
                                         connectionAllocator,
                                         /* clientInterfaces= */ null,
                                         /* binderBox= */ null);
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc
index 1f5468c6f..3c8b332 100644
--- a/content/public/browser/content_browser_client.cc
+++ b/content/public/browser/content_browser_client.cc
@@ -997,11 +997,8 @@
   std::move(callback).Run(nullptr);
 }
 
-std::vector<std::unique_ptr<NavigationThrottle>>
-content::ContentBrowserClient::CreateThrottlesForNavigation(
-    NavigationThrottleRegistry& registry) {
-  return std::vector<std::unique_ptr<NavigationThrottle>>();
-}
+void ContentBrowserClient::CreateThrottlesForNavigation(
+    NavigationThrottleRegistry& registry) {}
 
 std::vector<std::unique_ptr<CommitDeferringCondition>>
 ContentBrowserClient::CreateCommitDeferringConditionsForNavigation(
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
index a0f6c7e..afc93525 100644
--- a/content/public/browser/content_browser_client.h
+++ b/content/public/browser/content_browser_client.h
@@ -1705,12 +1705,8 @@
   // navigations; they are specifically not run for page activating navigations
   // such as prerender activation and back-forward cache restores or for
   // navigations that don't use a URLLoader like same-document navigations.
-  // TODO(https://crbug.com/412524375): New code should add a NavigationThrottle
-  // via the given `registry` instead of returning a NavigationThrottle from
-  // this method. Once all existing NavigationThrottles are migrated, the
-  // returned type will be changed to `void`.
-  virtual std::vector<std::unique_ptr<NavigationThrottle>>
-  CreateThrottlesForNavigation(NavigationThrottleRegistry& registry);
+  virtual void CreateThrottlesForNavigation(
+      NavigationThrottleRegistry& registry);
 
   // Allows the embedder to register one or more CommitDeferringConditions for
   // the navigation indicated by |navigation_handle|. A
diff --git a/content/public/browser/identity_request_dialog_controller.cc b/content/public/browser/identity_request_dialog_controller.cc
index d10efd7..f68ebb575 100644
--- a/content/public/browser/identity_request_dialog_controller.cc
+++ b/content/public/browser/identity_request_dialog_controller.cc
@@ -32,12 +32,14 @@
     const IdentityProviderMetadata& idp_metadata,
     const ClientMetadata& client_metadata,
     blink::mojom::RpContext rp_context,
+    std::optional<blink::mojom::Format> format,
     const std::vector<IdentityRequestDialogDisclosureField>& disclosure_fields,
     bool has_login_status_mismatch)
     : idp_for_display{idp_for_display},
       idp_metadata{idp_metadata},
       client_metadata{client_metadata},
       rp_context(rp_context),
+      format(format),
       disclosure_fields(disclosure_fields),
       has_login_status_mismatch(has_login_status_mismatch) {}
 
diff --git a/content/public/browser/identity_request_dialog_controller.h b/content/public/browser/identity_request_dialog_controller.h
index ed2068e..99bc325 100644
--- a/content/public/browser/identity_request_dialog_controller.h
+++ b/content/public/browser/identity_request_dialog_controller.h
@@ -102,6 +102,7 @@
                        const IdentityProviderMetadata& idp_metadata,
                        const ClientMetadata& client_metadata,
                        blink::mojom::RpContext rp_context,
+                       std::optional<blink::mojom::Format> format,
                        const std::vector<IdentityRequestDialogDisclosureField>&
                            disclosure_fields,
                        bool has_login_status_mismatch);
@@ -110,6 +111,7 @@
   IdentityProviderMetadata idp_metadata;
   ClientMetadata client_metadata;
   blink::mojom::RpContext rp_context;
+  std::optional<blink::mojom::Format> format;
   // For which fields should the dialog request permission for (assuming
   // this is for signup).
   std::vector<IdentityRequestDialogDisclosureField> disclosure_fields;
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 761ea53..2ce80c8 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -1375,6 +1375,13 @@
              "GinJavaBridgeMojoSkipClearObjectsOnMainDocumentReady",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
+// Rebind service binding when consecutive Context.updateServiceGroup() call is
+// done. If this is disabled, it rebinds the service binding on each
+// Context.updateServiceGroup() call.
+BASE_FEATURE(kGroupRebindingForGroupImportance,
+             "GroupRebindingForGroupImportance",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 // Reduce the priority of GPU process when in background so it is more likely
 // to be killed first if the OS needs more memory.
 BASE_FEATURE(kReduceGpuPriorityOnBackground,
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index b11246a..415dae84 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -305,6 +305,7 @@
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kAndroidWebAppLaunchHandler);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(
     kGinJavaBridgeMojoSkipClearObjectsOnMainDocumentReady);
+CONTENT_EXPORT BASE_DECLARE_FEATURE(kGroupRebindingForGroupImportance);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kReduceGpuPriorityOnBackground);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kSmartZoom);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kUserMediaScreenCapturing);
diff --git a/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ChildProcessLauncherTestHelperService.java b/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ChildProcessLauncherTestHelperService.java
index 724f9ff..031c559 100644
--- a/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ChildProcessLauncherTestHelperService.java
+++ b/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ChildProcessLauncherTestHelperService.java
@@ -18,7 +18,7 @@
 import org.chromium.base.library_loader.LibraryLoader;
 import org.chromium.base.library_loader.LibraryProcessType;
 import org.chromium.base.process_launcher.ChildProcessConnection;
-import org.chromium.base.process_launcher.FileDescriptorInfo;
+import org.chromium.base.process_launcher.IFileDescriptorInfo;
 import org.chromium.content.browser.ChildProcessLauncherHelperImpl;
 import org.chromium.content_public.browser.ChildProcessCreationParams;
 
@@ -87,7 +87,7 @@
                 ChildProcessLauncherTestUtils.startForTesting(
                         /* sandboxed= */ true,
                         commandLine,
-                        new FileDescriptorInfo[0],
+                        new IFileDescriptorInfo[0],
                         /* doSetupConnection= */ true);
 
         // Poll the launcher until the connection is set up. The main test in
diff --git a/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ChildProcessLauncherTestUtils.java b/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ChildProcessLauncherTestUtils.java
index 29deab7..ef3e6e2 100644
--- a/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ChildProcessLauncherTestUtils.java
+++ b/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ChildProcessLauncherTestUtils.java
@@ -5,8 +5,8 @@
 package org.chromium.content_shell_apk;
 
 import org.chromium.base.process_launcher.ChildProcessConnection;
-import org.chromium.base.process_launcher.FileDescriptorInfo;
 import org.chromium.base.process_launcher.IChildProcessService;
+import org.chromium.base.process_launcher.IFileDescriptorInfo;
 import org.chromium.content.browser.ChildProcessLauncherHelperImpl;
 import org.chromium.content.browser.LauncherThread;
 
@@ -56,7 +56,7 @@
     public static ChildProcessLauncherHelperImpl startForTesting(
             final boolean sandboxed,
             final String[] commandLine,
-            final FileDescriptorInfo[] filesToBeMapped,
+            final IFileDescriptorInfo[] filesToBeMapped,
             final boolean doSetupConnection) {
         return runOnLauncherAndGetResult(
                 new Callable<ChildProcessLauncherHelperImpl>() {
diff --git a/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/IChildProcessTest.aidl b/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/IChildProcessTest.aidl
index b715d3a..0766e0d 100644
--- a/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/IChildProcessTest.aidl
+++ b/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/IChildProcessTest.aidl
@@ -13,7 +13,7 @@
 interface IChildProcessTest {
   // Called by the service when onConnectionSetup is received. Echos back the parameters received
   // so far.
-  oneway void onConnectionSetup(boolean serviceCreatedCalled, in Bundle serviceBundle, in Bundle connectionBundle);
+  oneway void onConnectionSetup(boolean serviceCreatedCalled, in Bundle serviceBundle);
 
   oneway void onLoadNativeLibrary(boolean loadedSuccessfully);
 
diff --git a/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/TestChildProcessService.java b/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/TestChildProcessService.java
index 709d1fb3..e06273e 100644
--- a/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/TestChildProcessService.java
+++ b/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/TestChildProcessService.java
@@ -19,6 +19,7 @@
 import org.chromium.base.library_loader.LibraryProcessType;
 import org.chromium.base.process_launcher.ChildProcessService;
 import org.chromium.base.process_launcher.ChildProcessServiceDelegate;
+import org.chromium.base.process_launcher.IChildProcessArgs;
 
 import java.util.List;
 
@@ -54,14 +55,13 @@
 
         @Override
         public void onConnectionSetup(
-                Bundle connectionBundle, List<IBinder> clientInterfaces, IBinder binderBox) {
+                IChildProcessArgs args, List<IBinder> clientInterfaces, IBinder binderBox) {
             if (clientInterfaces != null && !clientInterfaces.isEmpty()) {
                 mIChildProcessTest = IChildProcessTest.Stub.asInterface(clientInterfaces.get(0));
             }
             if (mIChildProcessTest != null) {
                 try {
-                    mIChildProcessTest.onConnectionSetup(
-                            mServiceCreated, mServiceBundle, connectionBundle);
+                    mIChildProcessTest.onConnectionSetup(mServiceCreated, mServiceBundle);
                 } catch (RemoteException re) {
                     Log.e(TAG, "Failed to call IChildProcessTest.onConnectionSetup.", re);
                 }
diff --git a/content/shell/browser/shell_content_browser_client.cc b/content/shell/browser/shell_content_browser_client.cc
index e9a17824..cdf869046 100644
--- a/content/shell/browser/shell_content_browser_client.cc
+++ b/content/shell/browser/shell_content_browser_client.cc
@@ -693,16 +693,11 @@
           ->web_contents());
 }
 
-std::vector<std::unique_ptr<NavigationThrottle>>
-ShellContentBrowserClient::CreateThrottlesForNavigation(
+void ShellContentBrowserClient::CreateThrottlesForNavigation(
     NavigationThrottleRegistry& registry) {
-  std::vector<std::unique_ptr<NavigationThrottle>> empty_throttles;
   if (create_throttles_for_navigation_callback_) {
-    // TODO(https://crbug.com/412524375): NavigationThrottleRegistry migration.
-    return create_throttles_for_navigation_callback_.Run(
-        &registry.GetNavigationHandle());
+    create_throttles_for_navigation_callback_.Run(registry);
   }
-  return empty_throttles;
 }
 
 std::unique_ptr<LoginDelegate> ShellContentBrowserClient::CreateLoginDelegate(
diff --git a/content/shell/browser/shell_content_browser_client.h b/content/shell/browser/shell_content_browser_client.h
index 2730cbc..e608835c 100644
--- a/content/shell/browser/shell_content_browser_client.h
+++ b/content/shell/browser/shell_content_browser_client.h
@@ -125,7 +125,7 @@
   void OpenURL(SiteInstance* site_instance,
                const OpenURLParams& params,
                base::OnceCallback<void(WebContents*)> callback) override;
-  std::vector<std::unique_ptr<NavigationThrottle>> CreateThrottlesForNavigation(
+  void CreateThrottlesForNavigation(
       NavigationThrottleRegistry& registry) override;
   std::unique_ptr<LoginDelegate> CreateLoginDelegate(
       const net::AuthChallengeInfo& auth_info,
@@ -218,8 +218,8 @@
         std::move(url_loader_factory_params_callback);
   }
   void set_create_throttles_for_navigation_callback(
-      base::RepeatingCallback<std::vector<std::unique_ptr<NavigationThrottle>>(
-          NavigationHandle*)> create_throttles_for_navigation_callback) {
+      base::RepeatingCallback<void(NavigationThrottleRegistry&)>
+          create_throttles_for_navigation_callback) {
     create_throttles_for_navigation_callback_ =
         create_throttles_for_navigation_callback;
   }
@@ -271,8 +271,7 @@
                                bool is_for_isolated_world,
                                bool is_for_service_worker)>
       url_loader_factory_params_callback_;
-  base::RepeatingCallback<std::vector<std::unique_ptr<NavigationThrottle>>(
-      NavigationHandle*)>
+  base::RepeatingCallback<void(NavigationThrottleRegistry&)>
       create_throttles_for_navigation_callback_;
   base::RepeatingCallback<void(blink::web_pref::WebPreferences*)>
       override_web_preferences_callback_;
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index cc799cd5..30ab9e2 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1461,7 +1461,6 @@
     "../browser/cookie_deprecation_label/cookie_deprecation_label_browsertest.cc",
     "../browser/cross_site_transfer_browsertest.cc",
     "../browser/data_decoder_browsertest.cc",
-    "../browser/device_posture/foldable_apis_origin_trial_browsertest.cc",
     "../browser/device_sensors/device_sensor_browsertest.cc",
     "../browser/devtools/devtools_issue_storage_browsertest.cc",
     "../browser/devtools/devtools_trust_token_browsertest.cc",
diff --git a/content/test/content_test_bundle_data.filelist b/content/test/content_test_bundle_data.filelist
index 3b2cf19..f4530f5 100644
--- a/content/test/content_test_bundle_data.filelist
+++ b/content/test/content_test_bundle_data.filelist
@@ -5234,6 +5234,9 @@
 data/accessibility/html/select-slowly-build-expected-auralinux.txt
 data/accessibility/html/select-slowly-build-expected-blink.txt
 data/accessibility/html/select-slowly-build.html
+data/accessibility/html/select-with-input-expected-auralinux.txt
+data/accessibility/html/select-with-input-expected-blink.txt
+data/accessibility/html/select-with-input.html
 data/accessibility/html/select.html
 data/accessibility/html/selection-container-expected-android-assist-data.txt
 data/accessibility/html/selection-container-expected-android-external.txt
@@ -6764,8 +6767,6 @@
 data/dark_color_scheme_meta_slow.html
 data/data_url_navigations.html
 data/detach_frame_in_copy.html
-data/device_posture/no_token.html
-data/device_posture/valid_token.html
 data/device_sensors/cross_origin_iframe.html
 data/device_sensors/device_inertial_sensor_diagnostics.html
 data/device_sensors/device_motion_null_test.html
diff --git a/content/test/data/accessibility/html/select-with-input-expected-auralinux.txt b/content/test/data/accessibility/html/select-with-input-expected-auralinux.txt
new file mode 100644
index 0000000..f1f30144
--- /dev/null
+++ b/content/test/data/accessibility/html/select-with-input-expected-auralinux.txt
@@ -0,0 +1,32 @@
+[document web]
+++[section]
+++++[combo box]
+++++++[entry] selectable-text controller-for=[menu]
+++++++[menu] controlled-by=[entry]
+++++++++[section]
+++++++++++[section]
+++++++++++[menu item] name='one' selectable selected
+++++++++++[menu item] name='two' selectable
+++++[combo box]
+++++++[entry] selectable-text controller-for=[menu]
+++++++[menu] controlled-by=[entry]
+++++++++[section]
+++++++++++[menu item] name='one' selectable selected
+++++++++++[section]
+++++++++++[menu item] name='two' selectable
+++++[combo box]
+++++++[dialog]
+++++++++[section]
+++++++++++[section]
+++++++++++++[radio button] checkable checkable:true
+++++++++++[menu item] name='one' selectable selected
+++++++++++[menu item] name='two' selectable
+++++[combo box]
+++++++[entry] selectable-text controller-for=[dialog]
+++++++[dialog] controlled-by=[entry,entry]
+++++++++[section]
+++++++++++[section]
+++++++++++[section]
+++++++++++++[entry] selectable-text controller-for=[dialog]
+++++++++++[menu item] name='one' selectable selected
+++++++++++[menu item] name='two' selectable
diff --git a/content/test/data/accessibility/html/select-with-input-expected-blink.txt b/content/test/data/accessibility/html/select-with-input-expected-blink.txt
new file mode 100644
index 0000000..be7a21d8
--- /dev/null
+++ b/content/test/data/accessibility/html/select-with-input-expected-blink.txt
@@ -0,0 +1,37 @@
+rootWebArea
+++genericContainer ignored
+++++genericContainer
+++++++comboBoxSelect collapsed value='one'
+++++++++textField invisible controlsIds=menuListPopup
+++++++++++genericContainer invisible
+++++++++menuListPopup invisible ispopup=auto
+++++++++++genericContainer invisible
+++++++++++++genericContainer invisible
+++++++++++++menuListOption name='one' selected=true
+++++++++++++menuListOption invisible name='two' selected=false
+++++++comboBoxSelect collapsed value='one'
+++++++++textField invisible controlsIds=menuListPopup
+++++++++++genericContainer invisible
+++++++++menuListPopup invisible ispopup=auto
+++++++++++genericContainer invisible
+++++++++++++menuListOption name='one' selected=true
+++++++++++++genericContainer invisible
+++++++++++++menuListOption invisible name='two' selected=false
+++++++comboBoxSelect collapsed value='one'
+++++++++dialog invisible ispopup=auto
+++++++++++genericContainer invisible
+++++++++++++genericContainer invisible
+++++++++++++++radioButton invisible checkedState=false
+++++++++++++menuListOption name='one' selected=true
+++++++++++++menuListOption invisible name='two' selected=false
+++++++comboBoxSelect collapsed value='one'
+++++++++textField invisible controlsIds=dialog
+++++++++++genericContainer invisible
+++++++++dialog invisible ispopup=auto
+++++++++++genericContainer invisible
+++++++++++++genericContainer invisible
+++++++++++++genericContainer invisible
+++++++++++++++textField invisible controlsIds=dialog
+++++++++++++++++genericContainer invisible
+++++++++++++menuListOption name='one' selected=true
+++++++++++++menuListOption invisible name='two' selected=false
diff --git a/content/test/data/accessibility/html/select-with-input.html b/content/test/data/accessibility/html/select-with-input.html
new file mode 100644
index 0000000..4f6b8d1
--- /dev/null
+++ b/content/test/data/accessibility/html/select-with-input.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<style>
+select, ::picker(select) {
+  appearance: base-select;
+}
+</style>
+
+<select id=s1>
+  <span class=input></span>
+  <option>one</option>
+  <option>two</option>
+</select>
+
+<select id=s2>
+  <option>one</option>
+  <span class=input></span>
+  <option>two</option>
+</select>
+
+<select id=s3>
+  <span class=input data-type=radio></span>
+  <option>one</option>
+  <option>two</option>
+</select>
+
+<select id=s4>
+  <span class=input></span>
+  <span class=input></span>
+  <option>one</option>
+  <option>two</option>
+</select>
+
+<script>
+  document.querySelectorAll('.input').forEach(container => {
+    const input = document.createElement('input');
+    input.type = container.getAttribute('data-type');
+    container.appendChild(input);
+  });
+</script>
diff --git a/content/test/data/device_posture/no_token.html b/content/test/data/device_posture/no_token.html
deleted file mode 100644
index 702faa52..0000000
--- a/content/test/data/device_posture/no_token.html
+++ /dev/null
@@ -1,18 +0,0 @@
-<!DOCTYPE html>
-<meta charset="utf-8">
-<title>Device Posture API No Origin Trial Test</title>
-<head>
-  <style>
-    #content {
-      width: 0;
-    }
-    @media (horizontal-viewport-segments: 2) {
-      #content {
-        width: env(viewport-segment-width 0 0);
-      }
-    }
-  </style>
-</head>
-<body>
-  <div id="content"></div>
-</body>
diff --git a/content/test/data/device_posture/valid_token.html b/content/test/data/device_posture/valid_token.html
deleted file mode 100644
index f9b40cd..0000000
--- a/content/test/data/device_posture/valid_token.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<!DOCTYPE html>
-<meta charset="utf-8">
-<!-- The OT token below expires in 2033.
-   Regenerate this token with the command:
-   generate_token.py https://example.test FoldableAPIs --expire-timestamp=2000000000 -->
-<meta http-equiv="origin-trial" content="A7lNZO0Koy9FElYOLjQGgwGDgSdFxS93UMw7Am6gPnaHa+nsKHPYZjj3Z3dllkkmSQCgb+wlKWkLGMXoqEFcBwgAAABXeyJvcmlnaW4iOiAiaHR0cHM6Ly9leGFtcGxlLnRlc3Q6NDQzIiwgImZlYXR1cmUiOiAiRm9sZGFibGVBUElzIiwgImV4cGlyeSI6IDIwMDAwMDAwMDB9">
-<title>Device Posture API Origin Trial Test</title>
-<head>
-  <style>
-    #content {
-      width: 0;
-    }
-    @media (horizontal-viewport-segments: 2) {
-      #content {
-        width: env(viewport-segment-width 0 0);
-      }
-    }
-  </style>
-</head>
-<body>
-  <div id="content"></div>
-</body>
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
index 8193dcd..71b92177 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
@@ -551,8 +551,6 @@
 
 ## Win / Intel ##
 
-crbug.com/384257970 [ angle-d3d11 intel-0x9bc5 win ] conformance/textures/canvas_sub_rectangle/* [ RetryOnFailure ]
-crbug.com/384257970 [ angle-d3d11 intel-0x9bc5 win ] conformance2/textures/canvas_sub_rectangle/* [ RetryOnFailure ]
 
 crbug.com/401515599 [ win11 amd-0x7480 ] deqp/functional/gles3/transformfeedback/interpolation_centroid.html [ Failure ]
 crbug.com/401515599 [ win11 amd-0x7480 ] deqp/functional/gles3/transformfeedback/interpolation_smooth.html [ Failure ]
@@ -846,8 +844,6 @@
 crbug.com/1000354 [ android android-pixel-2 passthrough ] conformance2/reading/read-pixels-from-fbo-test.html [ Failure ]
 crbug.com/1276186 [ android android-pixel-2 ] conformance2/glsl3/array-equality.html [ Failure ]
 crbug.com/angleproject/7421 [ android android-pixel-2 angle-disabled no-passthrough ] conformance2/renderbuffers/invalidate-framebuffer.html [ Failure ]
-# Slow test that sometimes hits the 300s global timeout
-crbug.com/402726806 [ android android-pixel-2 angle-disabled no-passthrough ] deqp/functional/gles3/vertexarrays/multiple_attributes.stride.html [ RetryOnFailure ]
 
 ## Pixel 4 ##
 
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
index 32b90d6..01f66e3 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
@@ -524,7 +524,6 @@
 crbug.com/642822 [ mac amd ] conformance/rendering/clipping-wide-points.html [ Failure ]
 crbug.com/1230781 [ mac amd angle-opengl ] conformance/canvas/to-data-url-test.html [ Failure ]
 crbug.com/328100306 [ amd angle-metal sonoma ] conformance/glsl/bugs/sampler-array-using-loop-index.html [ Failure ]
-crbug.com/328102507 [ amd-0x7340 angle-opengl asan graphite-disabled mac-x86_64 release sonoma ] conformance/renderbuffers/stencil-renderbuffer-initialization.html [ Failure ]
 crbug.com/354627705 [ mac amd-0x7340 angle-opengl ] conformance/context/context-attributes-alpha-depth-stencil-antialias.html [ Failure ]
 crbug.com/354627705 [ mac amd-0x7340 angle-metal ] conformance/extensions/webgl-polygon-mode.html [ Failure ]
 crbug.com/354627705 [ mac amd-0x7340 angle-opengl ] conformance/rendering/color-mask-preserved-during-implicit-clears.html [ Failure ]
@@ -636,23 +635,8 @@
 
 crbug.com/1372149 [ android android-sm-a236b no-passthrough ] conformance/rendering/blending.html [ Failure ]
 crbug.com/40241618 [ android android-sm-s911u1 no-passthrough ] conformance/rendering/blending.html [ Failure ]
-crbug.com/1499000 [ android android-sm-a236b no-passthrough ] conformance/extensions/oes-texture-float-with-video.html [ Failure ]
-crbug.com/1499000 [ android android-sm-a236b no-passthrough ] conformance/extensions/oes-texture-half-float-with-video.html [ Failure ]
-crbug.com/1499000 [ android android-sm-a236b no-passthrough ] conformance/textures/image_bitmap_from_video/tex-2d-luminance-luminance-unsigned_byte.html [ Failure ]
-crbug.com/1499000 [ android android-sm-a236b no-passthrough ] conformance/textures/image_bitmap_from_video/tex-2d-luminance_alpha-luminance_alpha-unsigned_byte.html [ Failure ]
 crbug.com/1499000 [ android android-sm-a236b no-passthrough ] conformance/textures/image_bitmap_from_video/tex-2d-rgb-rgb-unsigned_byte.html [ Failure ]
-crbug.com/1499000 [ android android-sm-a236b no-passthrough ] conformance/textures/image_bitmap_from_video/tex-2d-rgb-rgb-unsigned_short_5_6_5.html [ Failure ]
-crbug.com/1499000 [ android android-sm-a236b no-passthrough ] conformance/textures/image_bitmap_from_video/tex-2d-rgba-rgba-unsigned_byte.html [ Failure ]
-crbug.com/1499000 [ android android-sm-a236b no-passthrough ] conformance/textures/image_bitmap_from_video/tex-2d-rgba-rgba-unsigned_short_4_4_4_4.html [ Failure ]
-crbug.com/1499000 [ android android-sm-a236b no-passthrough ] conformance/textures/image_bitmap_from_video/tex-2d-rgba-rgba-unsigned_short_5_5_5_1.html [ Failure ]
 crbug.com/1499000 [ android android-sm-a236b no-passthrough ] conformance/textures/misc/video-rotation.html [ Failure ]
-crbug.com/1499000 [ android android-sm-a236b no-passthrough ] conformance/textures/video/tex-2d-luminance-luminance-unsigned_byte.html [ Failure ]
-crbug.com/1499000 [ android android-sm-a236b no-passthrough ] conformance/textures/video/tex-2d-luminance_alpha-luminance_alpha-unsigned_byte.html [ Failure ]
-crbug.com/1499000 [ android android-sm-a236b no-passthrough ] conformance/textures/video/tex-2d-rgb-rgb-unsigned_byte.html [ Failure ]
-crbug.com/1499000 [ android android-sm-a236b no-passthrough ] conformance/textures/video/tex-2d-rgb-rgb-unsigned_short_5_6_5.html [ Failure ]
-crbug.com/1499000 [ android android-sm-a236b no-passthrough ] conformance/textures/video/tex-2d-rgba-rgba-unsigned_byte.html [ Failure ]
-crbug.com/1499000 [ android android-sm-a236b no-passthrough ] conformance/textures/video/tex-2d-rgba-rgba-unsigned_short_4_4_4_4.html [ Failure ]
-crbug.com/1499000 [ android android-sm-a236b no-passthrough ] conformance/textures/video/tex-2d-rgba-rgba-unsigned_short_5_5_5_1.html [ Failure ]
 
 crbug.com/331489774 [ android android-sm-s911u1 no-passthrough ] conformance/context/context-attributes-alpha-depth-stencil-antialias.html [ Failure ]
 
diff --git a/content/web_test/browser/web_test_content_browser_client.cc b/content/web_test/browser/web_test_content_browser_client.cc
index e5f7596..7f265db6 100644
--- a/content/web_test/browser/web_test_content_browser_client.cc
+++ b/content/web_test/browser/web_test_content_browser_client.cc
@@ -420,21 +420,14 @@
     WebTestControlHost::Get()->OverrideWebPreferences(prefs);
 }
 
-std::vector<std::unique_ptr<content::NavigationThrottle>>
-WebTestContentBrowserClient::CreateThrottlesForNavigation(
+void WebTestContentBrowserClient::CreateThrottlesForNavigation(
     content::NavigationThrottleRegistry& registry) {
-  content::NavigationHandle* navigation_handle =
-      &registry.GetNavigationHandle();
-  std::vector<std::unique_ptr<content::NavigationThrottle>> throttles =
-      ShellContentBrowserClient::CreateThrottlesForNavigation(registry);
-
-  throttles.push_back(std::make_unique<WebTestOriginTrialThrottle>(
-      navigation_handle, navigation_handle->GetWebContents()
-                             ->GetBrowserContext()
-                             ->GetOriginTrialsControllerDelegate()));
-
-  // TODO(https://crbug.com/412524375): NavigationThrottleRegistry migration.
-  return throttles;
+  ShellContentBrowserClient::CreateThrottlesForNavigation(registry);
+  content::NavigationHandle& navigation_handle = registry.GetNavigationHandle();
+  registry.AddThrottle(std::make_unique<WebTestOriginTrialThrottle>(
+      &navigation_handle, navigation_handle.GetWebContents()
+                              ->GetBrowserContext()
+                              ->GetOriginTrialsControllerDelegate()));
 }
 
 void WebTestContentBrowserClient::AppendExtraCommandLineSwitches(
diff --git a/content/web_test/browser/web_test_content_browser_client.h b/content/web_test/browser/web_test_content_browser_client.h
index e4eaba6..b50d562 100644
--- a/content/web_test/browser/web_test_content_browser_client.h
+++ b/content/web_test/browser/web_test_content_browser_client.h
@@ -75,8 +75,7 @@
   void OverrideWebPreferences(WebContents* web_contents,
                               SiteInstance& main_frame_site,
                               blink::web_pref::WebPreferences* prefs) override;
-  std::vector<std::unique_ptr<content::NavigationThrottle>>
-  CreateThrottlesForNavigation(
+  void CreateThrottlesForNavigation(
       content::NavigationThrottleRegistry& registry) override;
   void AppendExtraCommandLineSwitches(base::CommandLine* command_line,
                                       int child_process_id) override;
diff --git a/crypto/crypto_export.h b/crypto/crypto_export.h
index 66bd53c..186d294be 100644
--- a/crypto/crypto_export.h
+++ b/crypto/crypto_export.h
@@ -18,11 +18,7 @@
 #endif  // defined(CRYPTO_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(CRYPTO_IMPLEMENTATION)
 #define CRYPTO_EXPORT __attribute__((visibility("default")))
-#else
-#define CRYPTO_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/dbus/dbus_export.h b/dbus/dbus_export.h
index 1874aa9c..029b7ec 100644
--- a/dbus/dbus_export.h
+++ b/dbus/dbus_export.h
@@ -15,17 +15,9 @@
 #endif  // defined(WIN32)
 
 #if defined(COMPONENT_BUILD)
-
-#if defined(DBUS_IMPLEMENTATION)
 #define CHROME_DBUS_EXPORT __attribute__((visibility("default")))
-#else
-#define CHROME_DBUS_EXPORT
-#endif
-
 #else  // !defined(COMPONENT_BUILD)
-
 #define CHROME_DBUS_EXPORT
-
 #endif  // defined(COMPONENT_BUILD)
 
 #endif  // DBUS_DBUS_EXPORT_H_
diff --git a/device/base/device_base_export.h b/device/base/device_base_export.h
index 8672b65..f7d74539 100644
--- a/device/base/device_base_export.h
+++ b/device/base/device_base_export.h
@@ -15,11 +15,7 @@
 
 #elif defined(COMPONENT_BUILD) && !defined(WIN32)
 
-#if defined(DEVICE_BASE_IMPLEMENTATION)
 #define DEVICE_BASE_EXPORT __attribute__((visibility("default")))
-#else
-#define DEVICE_BASE_EXPORT
-#endif
 
 #else
 #define DEVICE_BASE_EXPORT
diff --git a/device/base/features.cc b/device/base/features.cc
index 2565670..2af045b 100644
--- a/device/base/features.cc
+++ b/device/base/features.cc
@@ -41,7 +41,7 @@
 // Serial.
 BASE_FEATURE(kBluetoothRfcommAndroid,
              "BluetoothRfcommAndroid",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 #endif  // BUILDFLAG(IS_ANDROID)
 
 #if BUILDFLAG(IS_ANDROID)
diff --git a/device/bluetooth/bluetooth_export.h b/device/bluetooth/bluetooth_export.h
index 7b82a39..69af504b 100644
--- a/device/bluetooth/bluetooth_export.h
+++ b/device/bluetooth/bluetooth_export.h
@@ -15,11 +15,7 @@
 
 #elif defined(COMPONENT_BUILD) && !defined(WIN32)
 
-#if defined(DEVICE_BLUETOOTH_IMPLEMENTATION)
 #define DEVICE_BLUETOOTH_EXPORT __attribute__((visibility("default")))
-#else
-#define DEVICE_BLUETOOTH_EXPORT
-#endif
 
 #else
 #define DEVICE_BLUETOOTH_EXPORT
diff --git a/device/gamepad/gamepad_export.h b/device/gamepad/gamepad_export.h
index 3479e78..1d6cf2d 100644
--- a/device/gamepad/gamepad_export.h
+++ b/device/gamepad/gamepad_export.h
@@ -15,11 +15,7 @@
 
 #elif defined(COMPONENT_BUILD) && !defined(WIN32)
 
-#if defined(DEVICE_GAMEPAD_IMPLEMENTATION)
 #define DEVICE_GAMEPAD_EXPORT __attribute__((visibility("default")))
-#else
-#define DEVICE_GAMEPAD_EXPORT
-#endif
 
 #else
 #define DEVICE_GAMEPAD_EXPORT
diff --git a/device/gamepad/public/cpp/gamepad_features_export.h b/device/gamepad/public/cpp/gamepad_features_export.h
index 0b3a46e..a769952 100644
--- a/device/gamepad/public/cpp/gamepad_features_export.h
+++ b/device/gamepad/public/cpp/gamepad_features_export.h
@@ -17,11 +17,7 @@
 
 #else  // !defined(WIN32)
 
-#if defined(GAMEPAD_FEATURES_IMPLEMENTATION)
 #define GAMEPAD_FEATURES_EXPORT __attribute__((visibility("default")))
-#else
-#define GAMEPAD_FEATURES_EXPORT
-#endif
 
 #endif
 
diff --git a/device/vr/android/arcore/arcore_impl.cc b/device/vr/android/arcore/arcore_impl.cc
index f22fe58..1239e05f 100644
--- a/device/vr/android/arcore/arcore_impl.cc
+++ b/device/vr/android/arcore/arcore_impl.cc
@@ -156,48 +156,41 @@
   out_pixels.copy_from(src_span);
 }
 
-// Helper, copies ARCore image to the passed in vector, discovering the buffer
-// size and resizing the vector first.
-template <typename T>
-void CopyArCoreImage(const ArSession* session,
-                     const ArImage* image,
-                     int32_t plane_index,
-                     std::vector<T>* out_pixels,
-                     uint32_t* out_width,
-                     uint32_t* out_height) {
-  // Get source image information
-  int32_t width = 0, height = 0;
-  ArImage_getWidth(session, image, &width);
-  ArImage_getHeight(session, image, &height);
-
-  *out_width = width;
-  *out_height = height;
-
-  // Allocate memory for the output.
-  out_pixels->resize(width * height);
-
-  CopyArCoreImage(session, image, plane_index,
-                  base::as_writable_byte_span(*out_pixels), sizeof(T), width,
-                  height);
-}
-
 device::mojom::XRLightProbePtr GetLightProbe(
     ArSession* arcore_session,
     ArLightEstimate* arcore_light_estimate) {
   // ArCore hands out 9 sets of RGB spherical harmonics coefficients
   // https://developers.google.com/ar/reference/c/group/light#arlightestimate_getenvironmentalhdrambientsphericalharmonics
   constexpr size_t kNumShCoefficients = 9;
+  constexpr size_t kNumChannels = 3;
+  constexpr size_t kRedChannel = 0;
+  constexpr size_t kGreenChannel = 1;
+  constexpr size_t kBlueChannel = 2;
 
   auto light_probe = device::mojom::XRLightProbe::New();
 
   light_probe->spherical_harmonics = device::mojom::XRSphericalHarmonics::New();
-  light_probe->spherical_harmonics->coefficients =
-      std::vector<device::RgbTupleF32>(kNumShCoefficients,
-                                       device::RgbTupleF32{});
 
+  // Create a temporary array to hold the values from ARCore. We'll need to
+  // transform them into an `RgbTupleF32` to send across mojom.
+  std::array<float, kNumShCoefficients * kNumChannels> coefficient_list;
   ArLightEstimate_getEnvironmentalHdrAmbientSphericalHarmonics(
-      arcore_session, arcore_light_estimate,
-      light_probe->spherical_harmonics->coefficients.data()->components.data());
+      arcore_session, arcore_light_estimate, coefficient_list.data());
+
+  // The returned data is 27 floats (kNumShCoefficients * kNumChannels), in
+  // the repeating order of RGB. We can thus iterate over chunks of 3 to
+  // create our RgbTupleF32s.
+  base::span<const float> coefficients(coefficient_list);
+  light_probe->spherical_harmonics->coefficients.reserve(coefficients.size());
+  while (!coefficients.empty()) {
+    auto [coefficient, rem] = coefficients.split_at<kNumChannels>();
+    // Copy the first set of data.
+    light_probe->spherical_harmonics->coefficients.emplace_back(
+        coefficient[kRedChannel], coefficient[kGreenChannel],
+        coefficient[kBlueChannel]);
+    // Advance the array.
+    coefficients = rem;
+  }
 
   float main_light_direction[3] = {};
   ArLightEstimate_getEnvironmentalHdrMainLightDirection(
@@ -206,6 +199,10 @@
   light_probe->main_light_direction.set_y(main_light_direction[1]);
   light_probe->main_light_direction.set_z(main_light_direction[2]);
 
+  // Intensity is returned as three floats, r, g, then b:
+  // https://developers.google.com/ar/reference/c/group/ar-light-estimate#arlightestimate_getenvironmentalhdrmainlightintensity.
+  // Since this is just a single value, it's okay to have this read directly
+  // into the backing array of the RgbTupleF32.
   ArLightEstimate_getEnvironmentalHdrMainLightIntensity(
       arcore_session, arcore_light_estimate,
       light_probe->main_light_intensity.components.data());
@@ -221,7 +218,7 @@
       arcore_session, arcore_light_estimate, arcore_cube_map);
 
   auto cube_map = device::mojom::XRCubeMap::New();
-  std::array<std::vector<device::RgbaTupleF16>*, 6> const cube_map_faces = {
+  std::array<std::vector<uint16_t>*, 6> const cube_map_faces = {
       &cube_map->positive_x, &cube_map->negative_x, &cube_map->positive_y,
       &cube_map->negative_y, &cube_map->positive_z, &cube_map->negative_z};
 
@@ -244,8 +241,6 @@
       return nullptr;
     }
 
-    auto* cube_map_face = cube_map_faces[i];
-
     // Make sure we only have a single image plane
     int32_t num_planes = 0;
     ArImage_getNumberOfPlanes(arcore_session, arcore_cube_map_face,
@@ -268,9 +263,9 @@
     }
 
     // Copy the cubemap
-    uint32_t face_width = 0, face_height = 0;
-    CopyArCoreImage(arcore_session, arcore_cube_map_face, 0, cube_map_face,
-                    &face_width, &face_height);
+    int32_t face_width = 0, face_height = 0;
+    ArImage_getWidth(arcore_session, arcore_cube_map_face, &face_width);
+    ArImage_getHeight(arcore_session, arcore_cube_map_face, &face_height);
 
     // Make sure the cube map is square
     if (face_width != face_height) {
@@ -279,15 +274,29 @@
       return nullptr;
     }
 
+    const int32_t signed_width_and_height =
+        base::checked_cast<int32_t>(cube_map->width_and_height);
     // Make sure all faces have the same dimensions
     if (i == 0) {
       cube_map->width_and_height = face_width;
-    } else if (face_width != cube_map->width_and_height ||
-               face_height != cube_map->width_and_height) {
+    } else if (face_width != signed_width_and_height ||
+               face_height != signed_width_and_height) {
       DVLOG(1) << "ArCore cube map faces not all of the same dimensions.";
       ReleaseArCoreCubemap(&arcore_cube_map);
       return nullptr;
     }
+
+    // ARCore returns (and mojom expects) to receive the data as r, g, b, a, ...
+    // There are width*height "pixels" of 4 components each, with each component
+    // being a uint16_t.
+    auto* cube_map_face = cube_map_faces[i];
+    cube_map_face->resize(face_width * face_height *
+                          device::mojom::XRCubeMap::kNumComponentsPerPixel);
+    size_t pixel_size =
+        device::mojom::XRCubeMap::kNumComponentsPerPixel * sizeof(uint16_t);
+    CopyArCoreImage(arcore_session, arcore_cube_map_face, 0,
+                    base::as_writable_byte_span(*cube_map_face), pixel_size,
+                    face_width, face_height);
   }
 
   ReleaseArCoreCubemap(&arcore_cube_map);
diff --git a/device/vr/public/mojom/BUILD.gn b/device/vr/public/mojom/BUILD.gn
index b6b8bdd..a969521e 100644
--- a/device/vr/public/mojom/BUILD.gn
+++ b/device/vr/public/mojom/BUILD.gn
@@ -28,10 +28,6 @@
   shared_cpp_typemap = {
     types = [
       {
-        mojom = "device.mojom.RgbaTupleF16"
-        cpp = "::device::RgbaTupleF16"
-      },
-      {
         mojom = "device.mojom.RgbTupleF32"
         cpp = "::device::RgbTupleF32"
       },
diff --git a/device/vr/public/mojom/rgba_tuple_f16.h b/device/vr/public/mojom/rgba_tuple_f16.h
deleted file mode 100644
index e321f2af..0000000
--- a/device/vr/public/mojom/rgba_tuple_f16.h
+++ /dev/null
@@ -1,43 +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.
-
-#ifndef DEVICE_VR_PUBLIC_MOJOM_RGBA_TUPLE_F16_H_
-#define DEVICE_VR_PUBLIC_MOJOM_RGBA_TUPLE_F16_H_
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <array>
-
-namespace device {
-
-struct RgbaTupleF16 {
-  // Because C++ does not have a native 16-bit floating point type,
-  // the components are stored as |uint16_t|s.
-  using Component = uint16_t;
-  static constexpr size_t kNumComponents = 4;
-
-  RgbaTupleF16() : RgbaTupleF16(0, 0, 0, 0) {}
-  RgbaTupleF16(uint16_t red, uint16_t green, uint16_t blue, uint16_t alpha)
-      : components{red, green, blue, alpha} {}
-
-  uint16_t red() const { return components[0]; }
-  void set_red(uint16_t red) { components[0] = red; }
-  uint16_t green() const { return components[1]; }
-  void set_green(uint16_t green) { components[1] = green; }
-  uint16_t blue() const { return components[2]; }
-  void set_blue(uint16_t blue) { components[2] = blue; }
-  uint16_t alpha() const { return components[3]; }
-  void set_alpha(uint16_t alpha) { components[3] = alpha; }
-
-  std::array<Component, kNumComponents> components;
-};
-
-static_assert(sizeof(RgbaTupleF16) == sizeof(RgbaTupleF16::Component) *
-                                          RgbaTupleF16::kNumComponents,
-              "RgbaTupleF16 must be contiguous");
-
-}  // namespace device
-
-#endif  // DEVICE_VR_PUBLIC_MOJOM_RGBA_TUPLE_F16_H_
diff --git a/device/vr/public/mojom/vr_service.mojom b/device/vr/public/mojom/vr_service.mojom
index b1bd3e7..47e5a1b 100644
--- a/device/vr/public/mojom/vr_service.mojom
+++ b/device/vr/public/mojom/vr_service.mojom
@@ -551,24 +551,12 @@
 // Contains the coefficient data for L2 spherical harmonics generated by ArCore.
 struct XRSphericalHarmonics {
   // L2 (3rd order) spherical harmonics coefficients.
-  // This array has 9 RGB vector elements, representing the 27 coefficients
-  // given by ArCore.
+  // This array has 9 RGB vector elements, representing the 27 required
+  // coefficients for 3rd order spherical harmonics.
   // Units are expressed in nits:
   // https://github.com/immersive-web/lighting-estimation/blob/master/lighting-estimation-explainer.md#physically-based-units
   array<RgbTupleF32, 9> coefficients;
 };
-
-// Tuple of half-precision floating point RGBA data.
-// Since there isn't a native way to represent half-precision floats, they are
-// stored a uint16s.
-// This is the pixel format used by ArCore for cubemap textures.
-struct RgbaTupleF16 {
-  uint16 red;
-  uint16 green;
-  uint16 blue;
-  uint16 alpha;
-};
-
 // Contains the pixel data for all six faces of an HDR cubemap generated by
 // ArCore. Pixel data is stored in RGBA16F format.
 struct XRCubeMap {
@@ -578,19 +566,23 @@
   // The width and height (in pixels) of each cube map face.
   // Since each face is equal and square, this is only given as a single value.
   // Additionally, the dimensions of each face will be a power of two.
-  // Note that each pixel is a contiguous RGBA16F vector, so the length of each
-  // array will be this times |kNumComponentsPerPixel|.
   uint32 width_and_height;
 
-  // Each cube map face, stored in RGBA row-major order. Units are in nits.
-  // Each array will be |width_and_height ** 2| in length.
+  // Each cube map face, stored in RGBA row-major order.
+  // Each array will be |kNumComponentsPerPixel| * |width_and_height ** 2| in
+  // length, since each RGBA component is represented separately, but in a
+  // repeating order.
+  // Since there isn't a native way to represent half-precision floats, they are
+  // stored as uint16s.
+  // This matches the pixel format used by ArCore for cubemap textures.
+  // Units are expressed in nits:
   // https://github.com/immersive-web/lighting-estimation/blob/master/lighting-estimation-explainer.md#physically-based-units
-  array<RgbaTupleF16> positive_x;
-  array<RgbaTupleF16> negative_x;
-  array<RgbaTupleF16> positive_y;
-  array<RgbaTupleF16> negative_y;
-  array<RgbaTupleF16> positive_z;
-  array<RgbaTupleF16> negative_z;
+  array<uint16> positive_x;
+  array<uint16> negative_x;
+  array<uint16> positive_y;
+  array<uint16> negative_y;
+  array<uint16> positive_z;
+  array<uint16> negative_z;
 };
 
 // Contains lighting information generated from ArCore for use in diffuse
diff --git a/device/vr/public/mojom/vr_service_mojom_traits.h b/device/vr/public/mojom/vr_service_mojom_traits.h
index dcd7ebc..b2ffddc 100644
--- a/device/vr/public/mojom/vr_service_mojom_traits.h
+++ b/device/vr/public/mojom/vr_service_mojom_traits.h
@@ -7,7 +7,6 @@
 
 #include "device/vr/public/mojom/pose.h"
 #include "device/vr/public/mojom/rgb_tuple_f32.h"
-#include "device/vr/public/mojom/rgba_tuple_f16.h"
 #include "device/vr/public/mojom/vr_service.mojom-shared.h"
 #include "mojo/public/cpp/bindings/struct_traits.h"
 #include "ui/gfx/geometry/mojom/geometry_mojom_traits.h"
@@ -17,26 +16,6 @@
 namespace mojo {
 
 template <>
-struct StructTraits<device::mojom::RgbaTupleF16DataView, device::RgbaTupleF16> {
-  static uint16_t red(const device::RgbaTupleF16& rgba) { return rgba.red(); }
-  static uint16_t green(const device::RgbaTupleF16& rgba) {
-    return rgba.green();
-  }
-  static uint16_t blue(const device::RgbaTupleF16& rgba) { return rgba.blue(); }
-  static uint16_t alpha(const device::RgbaTupleF16& rgba) {
-    return rgba.alpha();
-  }
-  static bool Read(device::mojom::RgbaTupleF16DataView data,
-                   device::RgbaTupleF16* out) {
-    out->set_red(data.red());
-    out->set_green(data.green());
-    out->set_blue(data.blue());
-    out->set_alpha(data.alpha());
-    return true;
-  }
-};
-
-template <>
 struct StructTraits<device::mojom::RgbTupleF32DataView, device::RgbTupleF32> {
   static float red(const device::RgbTupleF32& rgba) { return rgba.red(); }
   static float green(const device::RgbTupleF32& rgba) { return rgba.green(); }
diff --git a/device/vr/vr_export.h b/device/vr/vr_export.h
index 43390e1..adc9426 100644
--- a/device/vr/vr_export.h
+++ b/device/vr/vr_export.h
@@ -15,11 +15,7 @@
 
 #elif defined(COMPONENT_BUILD) && !defined(WIN32)
 
-#if defined(DEVICE_VR_IMPLEMENTATION)
 #define DEVICE_VR_EXPORT __attribute__((visibility("default")))
-#else
-#define DEVICE_VR_EXPORT
-#endif
 
 #else
 #define DEVICE_VR_EXPORT
diff --git a/docs/mac/debugging.md b/docs/mac/debugging.md
index 95fc9e5..d56867b 100644
--- a/docs/mac/debugging.md
+++ b/docs/mac/debugging.md
@@ -220,11 +220,11 @@
 ### (2) Use *gn*
 
 1. Tell `gn` to generate an Xcode project for your out directory:
-   `gn gen --ide=xcode out/debug`
+   `gn gen --ide=xcode out/debug --ninja-executable=autoninja`
 2. Open *out/debug/all.xcodeproj*
 3. Have it automatically generate schemes for you
 4. You can now build targets from within Xcode, which will simply call out to
-   `ninja` via an Xcode script. But the resulting binaries are available as
+   `autoninja` via an Xcode script. But the resulting binaries are available as
    debuggable targets in Xcode.
 
 Note that any changes to the .xcodeproj will be overwritten; all changes to the
diff --git a/docs/website b/docs/website
index e157e12..b2558d3 160000
--- a/docs/website
+++ b/docs/website
@@ -1 +1 @@
-Subproject commit e157e12d99cfc729a970b474344673c44e2d2c9c
+Subproject commit b2558d301fc88f8e4671024c47960ff1ee82cf34
diff --git a/extensions/browser/api/serial/BUILD.gn b/extensions/browser/api/serial/BUILD.gn
index adf7c5b..ba6d5a0 100644
--- a/extensions/browser/api/serial/BUILD.gn
+++ b/extensions/browser/api/serial/BUILD.gn
@@ -19,17 +19,15 @@
 
   configs += [ "//build/config/compiler:wexit_time_destructors" ]
 
-  deps = [
-    "//content/public/common",
+  public_deps = [
+    "//base",
+    "//content/public/browser",
+    "//extensions/browser:browser_sources",
     "//extensions/common",
     "//extensions/common/api",
     "//mojo/public/cpp/bindings",
+    "//mojo/public/cpp/system",
+    "//net",
     "//services/device/public/mojom",
-    "//services/service_manager/public/cpp",
-  ]
-
-  public_deps = [
-    "//content/public/browser",
-    "//extensions/browser:browser_sources",
   ]
 }
diff --git a/extensions/browser/api/socket/BUILD.gn b/extensions/browser/api/socket/BUILD.gn
index c4be578..75d3b54 100644
--- a/extensions/browser/api/socket/BUILD.gn
+++ b/extensions/browser/api/socket/BUILD.gn
@@ -28,25 +28,33 @@
 
   configs += [ "//build/config/compiler:wexit_time_destructors" ]
 
-  deps = [
+  public_deps = [
+    "//base",
     "//build:chromeos_buildflags",
     "//content/public/browser",
     "//content/public/common",
+    "//extensions/browser:browser_sources",
     "//extensions/common",
     "//extensions/common/api",
+    "//mojo/public/cpp/bindings",
+    "//mojo/public/cpp/system",
+    "//net",
+    "//net/traffic_annotation",
+    "//services/network/public/cpp",
+    "//services/network/public/mojom",
+    "//services/network/public/mojom:mojom_host_resolver",
   ]
 
+  deps = [ "//url" ]
+
   if (is_chromeos) {
     sources += [
       "app_firewall_hole_manager.cc",
       "app_firewall_hole_manager.h",
     ]
 
-    deps += [
-      "//chromeos/components/firewall_hole",
-      "//components/keyed_service/content",
-    ]
-  }
+    public_deps += [ "//chromeos/components/firewall_hole" ]
 
-  public_deps = [ "//extensions/browser:browser_sources" ]
+    deps += [ "//components/keyed_service/content" ]
+  }
 }
diff --git a/extensions/browser/api/sockets_udp/BUILD.gn b/extensions/browser/api/sockets_udp/BUILD.gn
index 9678c8c..1df2633 100644
--- a/extensions/browser/api/sockets_udp/BUILD.gn
+++ b/extensions/browser/api/sockets_udp/BUILD.gn
@@ -17,14 +17,19 @@
 
   configs += [ "//build/config/compiler:wexit_time_destructors" ]
 
-  deps = [
+  public_deps = [
+    "//base",
+    "//content/public/browser",
+    "//extensions/browser:browser_sources",
     "//extensions/browser/api/socket",
     "//extensions/common",
     "//extensions/common/api",
+    "//mojo/public/cpp/bindings",
   ]
 
-  public_deps = [
-    "//content/public/browser",
-    "//extensions/browser:browser_sources",
+  deps = [
+    "//content/public/common",
+    "//net",
+    "//services/network/public/mojom",
   ]
 }
diff --git a/extensions/browser/guest_view/web_view/web_view_permission_helper_delegate.h b/extensions/browser/guest_view/web_view/web_view_permission_helper_delegate.h
index a1fe26d8..8e2e25dc 100644
--- a/extensions/browser/guest_view/web_view/web_view_permission_helper_delegate.h
+++ b/extensions/browser/guest_view/web_view/web_view_permission_helper_delegate.h
@@ -98,6 +98,8 @@
   virtual bool ForwardEmbeddedMediaPermissionChecksAsEmbedder(
       const url::Origin& embedder_origin);
 
+  // Allows the delegate to override the results of permission requests; useful
+  // when custom handling is needed for specific webviews.
   virtual std::optional<content::PermissionResult> OverridePermissionResult(
       ContentSettingsType type);
 
diff --git a/extensions/common/extensions_export.h b/extensions/common/extensions_export.h
index 5f05f358..1f2cb671e 100644
--- a/extensions/common/extensions_export.h
+++ b/extensions/common/extensions_export.h
@@ -16,11 +16,7 @@
 #endif  // defined(EXTENSIONS_COMPONENT_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(EXTENSIONS_COMPONENT_IMPLEMENTATION)
 #define EXTENSIONS_EXPORT __attribute__((visibility("default")))
-#else
-#define EXTENSIONS_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/extensions/shell/browser/shell_content_browser_client.cc b/extensions/shell/browser/shell_content_browser_client.cc
index 28e12bf5..fab3a40 100644
--- a/extensions/shell/browser/shell_content_browser_client.cc
+++ b/extensions/shell/browser/shell_content_browser_client.cc
@@ -252,8 +252,7 @@
                           render_frame_host.GetGlobalId()));
 }
 
-std::vector<std::unique_ptr<content::NavigationThrottle>>
-ShellContentBrowserClient::CreateThrottlesForNavigation(
+void ShellContentBrowserClient::CreateThrottlesForNavigation(
     content::NavigationThrottleRegistry& registry) {
   content::NavigationHandle& navigation_handle =
       registry.GetNavigationHandle();
@@ -265,7 +264,6 @@
   }
   registry.MaybeAddThrottle(
       WebViewGuest::MaybeCreateNavigationThrottle(&navigation_handle));
-  return {};
 }
 
 std::unique_ptr<content::NavigationUIData>
diff --git a/extensions/shell/browser/shell_content_browser_client.h b/extensions/shell/browser/shell_content_browser_client.h
index 8da3baa..368f9a48 100644
--- a/extensions/shell/browser/shell_content_browser_client.h
+++ b/extensions/shell/browser/shell_content_browser_client.h
@@ -89,8 +89,7 @@
   void RegisterAssociatedInterfaceBindersForRenderFrameHost(
       content::RenderFrameHost& render_frame_host,
       blink::AssociatedInterfaceRegistry& associated_registry) override;
-  std::vector<std::unique_ptr<content::NavigationThrottle>>
-  CreateThrottlesForNavigation(
+  void CreateThrottlesForNavigation(
       content::NavigationThrottleRegistry& registry) override;
   std::unique_ptr<content::NavigationUIData> GetNavigationUIData(
       content::NavigationHandle* navigation_handle) override;
diff --git a/fuchsia_web/webengine/browser/web_engine_content_browser_client.cc b/fuchsia_web/webengine/browser/web_engine_content_browser_client.cc
index 80729d4..c0bfdc62 100644
--- a/fuchsia_web/webengine/browser/web_engine_content_browser_client.cc
+++ b/fuchsia_web/webengine/browser/web_engine_content_browser_client.cc
@@ -310,8 +310,7 @@
   return base::OnceClosure();
 }
 
-std::vector<std::unique_ptr<content::NavigationThrottle>>
-WebEngineContentBrowserClient::CreateThrottlesForNavigation(
+void WebEngineContentBrowserClient::CreateThrottlesForNavigation(
     content::NavigationThrottleRegistry& registry) {
   auto& navigation_handle = registry.GetNavigationHandle();
   auto* frame_impl =
@@ -334,8 +333,6 @@
         navigation_handle.GetWebContents()->GetBrowserContext(),
         *explicit_sites_filter_error_page));
   }
-
-  return {};
 }
 
 std::vector<std::unique_ptr<blink::URLLoaderThrottle>>
diff --git a/fuchsia_web/webengine/browser/web_engine_content_browser_client.h b/fuchsia_web/webengine/browser/web_engine_content_browser_client.h
index e887887..057785c 100644
--- a/fuchsia_web/webengine/browser/web_engine_content_browser_client.h
+++ b/fuchsia_web/webengine/browser/web_engine_content_browser_client.h
@@ -62,8 +62,7 @@
       net::SSLCertRequestInfo* cert_request_info,
       net::ClientCertIdentityList client_certs,
       std::unique_ptr<content::ClientCertificateDelegate> delegate) override;
-  std::vector<std::unique_ptr<content::NavigationThrottle>>
-  CreateThrottlesForNavigation(
+  void CreateThrottlesForNavigation(
       content::NavigationThrottleRegistry& registry) override;
   std::vector<std::unique_ptr<blink::URLLoaderThrottle>>
   CreateURLLoaderThrottles(
diff --git a/fuchsia_web/webengine/web_engine_export.h b/fuchsia_web/webengine/web_engine_export.h
index f96cb77..e219ead 100644
--- a/fuchsia_web/webengine/web_engine_export.h
+++ b/fuchsia_web/webengine/web_engine_export.h
@@ -7,11 +7,7 @@
 
 #if defined(COMPONENT_BUILD)
 
-#if defined(WEB_ENGINE_IMPLEMENTATION)
 #define WEB_ENGINE_EXPORT __attribute__((visibility("default")))
-#else
-#define WEB_ENGINE_EXPORT
-#endif
 
 #else  // defined(COMPONENT_BUILD)
 #define WEB_ENGINE_EXPORT
diff --git a/gin/gin_export.h b/gin/gin_export.h
index 83986e6..9aa36fe 100644
--- a/gin/gin_export.h
+++ b/gin/gin_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(GIN_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(GIN_IMPLEMENTATION)
 #define GIN_EXPORT __attribute__((visibility("default")))
-#else
-#define GIN_EXPORT
-#endif  // defined(GIN_IMPLEMENTATION)
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/google_apis/gcm/base/gcm_export.h b/google_apis/gcm/base/gcm_export.h
index 7e29ee7..82e0f33 100644
--- a/google_apis/gcm/base/gcm_export.h
+++ b/google_apis/gcm/base/gcm_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(GCM_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(GCM_IMPLEMENTATION)
 #define GCM_EXPORT __attribute__((visibility("default")))
-#else
-#define GCM_EXPORT
-#endif  // defined(GCM_IMPLEMENTATION)
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/gpu/command_buffer/client/gles2_c_lib_export.h b/gpu/command_buffer/client/gles2_c_lib_export.h
index b9d3fb1..3d6449a 100644
--- a/gpu/command_buffer/client/gles2_c_lib_export.h
+++ b/gpu/command_buffer/client/gles2_c_lib_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(GLES2_C_LIB_IMPLEMENTATION)
 
 #else // defined(WIN32)
-#if defined(GLES2_C_LIB_IMPLEMENTATION)
 #define GLES2_C_LIB_EXPORT __attribute__((visibility("default")))
-#else
-#define GLES2_C_LIB_EXPORT
-#endif
 #endif
 
 #else // defined(COMPONENT_BUILD)
diff --git a/gpu/command_buffer/client/gles2_impl_export.h b/gpu/command_buffer/client/gles2_impl_export.h
index 1a5168c..0f6ee0d 100644
--- a/gpu/command_buffer/client/gles2_impl_export.h
+++ b/gpu/command_buffer/client/gles2_impl_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(GLES2_IMPL_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(GLES2_IMPL_IMPLEMENTATION)
 #define GLES2_IMPL_EXPORT __attribute__((visibility("default")))
-#else
-#define GLES2_IMPL_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/gpu/command_buffer/client/webgpu_export.h b/gpu/command_buffer/client/webgpu_export.h
index b362730..9483a945 100644
--- a/gpu/command_buffer/client/webgpu_export.h
+++ b/gpu/command_buffer/client/webgpu_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(WEBGPU_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(WEBGPU_IMPLEMENTATION)
 #define WEBGPU_EXPORT __attribute__((visibility("default")))
-#else
-#define WEBGPU_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/gpu/command_buffer/common/gles2_utils_export.h b/gpu/command_buffer/common/gles2_utils_export.h
index fcecbb7..bcbe73f 100644
--- a/gpu/command_buffer/common/gles2_utils_export.h
+++ b/gpu/command_buffer/common/gles2_utils_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(GLES2_UTILS_IMPLEMENTATION)
 
 #else // defined(WIN32)
-#if defined(GLES2_UTILS_IMPLEMENTATION)
 #define GLES2_UTILS_EXPORT __attribute__((visibility("default")))
-#else
-#define GLES2_UTILS_EXPORT
-#endif
 #endif
 
 #else // defined(COMPONENT_BUILD)
diff --git a/gpu/command_buffer/service/webgpu_decoder_impl.cc b/gpu/command_buffer/service/webgpu_decoder_impl.cc
index 16cd766..245adb7 100644
--- a/gpu/command_buffer/service/webgpu_decoder_impl.cc
+++ b/gpu/command_buffer/service/webgpu_decoder_impl.cc
@@ -1277,6 +1277,7 @@
   switch (feature) {
     case wgpu::FeatureName::ChromiumExperimentalTimestampQueryInsidePasses:
     case wgpu::FeatureName::MultiDrawIndirect:
+    case wgpu::FeatureName::TextureCompressionASTCSliced3D:
     case wgpu::FeatureName::TextureCompressionBCSliced3D:
     case wgpu::FeatureName::Unorm16TextureFormats:
     case wgpu::FeatureName::Snorm16TextureFormats:
diff --git a/gpu/gpu_export.h b/gpu/gpu_export.h
index 4bfc01b..8072ba0 100644
--- a/gpu/gpu_export.h
+++ b/gpu/gpu_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(GPU_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(GPU_IMPLEMENTATION)
 #define GPU_EXPORT __attribute__((visibility("default")))
-#else
-#define GPU_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/gpu/gpu_gles2_export.h b/gpu/gpu_gles2_export.h
index f0bd00f..f5cfa470 100644
--- a/gpu/gpu_gles2_export.h
+++ b/gpu/gpu_gles2_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(GPU_GLES2_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(GPU_GLES2_IMPLEMENTATION)
 #define GPU_GLES2_EXPORT __attribute__((visibility("default")))
-#else
-#define GPU_GLES2_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/gpu/gpu_util_export.h b/gpu/gpu_util_export.h
index d6f5b356..fa22faa 100644
--- a/gpu/gpu_util_export.h
+++ b/gpu/gpu_util_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(GPU_UTIL_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(GPU_UTIL_IMPLEMENTATION)
 #define GPU_UTIL_EXPORT __attribute__((visibility("default")))
-#else
-#define GPU_UTIL_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/gpu/ipc/gl_in_process_context_export.h b/gpu/ipc/gl_in_process_context_export.h
index 78cf97b..db9ea37 100644
--- a/gpu/ipc/gl_in_process_context_export.h
+++ b/gpu/ipc/gl_in_process_context_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(GL_IN_PROCESS_CONTEXT_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(GL_IN_PROCESS_CONTEXT_IMPLEMENTATION)
 #define GL_IN_PROCESS_CONTEXT_EXPORT __attribute__((visibility("default")))
-#else
-#define GL_IN_PROCESS_CONTEXT_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/gpu/ipc/service/gpu_ipc_service_export.h b/gpu/ipc/service/gpu_ipc_service_export.h
index 04f8eed..65bcd55 100644
--- a/gpu/ipc/service/gpu_ipc_service_export.h
+++ b/gpu/ipc/service/gpu_ipc_service_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(GPU_IPC_SERVICE_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(GPU_IPC_SERVICE_IMPLEMENTATION)
 #define GPU_IPC_SERVICE_EXPORT __attribute__((visibility("default")))
-#else
-#define GPU_IPC_SERVICE_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/gpu/raster_export.h b/gpu/raster_export.h
index 3671d1f..a6649cf 100644
--- a/gpu/raster_export.h
+++ b/gpu/raster_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(RASTER_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(RASTER_IMPLEMENTATION)
 #define RASTER_EXPORT __attribute__((visibility("default")))
-#else
-#define RASTER_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/headless/lib/browser/headless_content_browser_client.cc b/headless/lib/browser/headless_content_browser_client.cc
index 12252b4..0865a115 100644
--- a/headless/lib/browser/headless_content_browser_client.cc
+++ b/headless/lib/browser/headless_content_browser_client.cc
@@ -453,21 +453,15 @@
 #endif
 
 #if defined(HEADLESS_USE_POLICY)
-std::vector<std::unique_ptr<content::NavigationThrottle>>
-HeadlessContentBrowserClient::CreateThrottlesForNavigation(
+void HeadlessContentBrowserClient::CreateThrottlesForNavigation(
     content::NavigationThrottleRegistry& registry) {
-  std::vector<std::unique_ptr<content::NavigationThrottle>> throttles;
-
   // Avoid creating naviagtion throttle if preferences are not available
   // (happens in tests).
   content::NavigationHandle& handle = registry.GetNavigationHandle();
   if (browser_->GetPrefs()) {
-    throttles.push_back(std::make_unique<PolicyBlocklistNavigationThrottle>(
+    registry.AddThrottle(std::make_unique<PolicyBlocklistNavigationThrottle>(
         &handle, handle.GetWebContents()->GetBrowserContext()));
   }
-
-  // TODO(https://crbug.com/412524375): NavigationThrottleRegistry migration.
-  return throttles;
 }
 #endif  // defined(HEADLESS_USE_POLICY)
 
diff --git a/headless/lib/browser/headless_content_browser_client.h b/headless/lib/browser/headless_content_browser_client.h
index c41428c..9a23173 100644
--- a/headless/lib/browser/headless_content_browser_client.h
+++ b/headless/lib/browser/headless_content_browser_client.h
@@ -133,8 +133,7 @@
 #endif
 
 #if defined(HEADLESS_USE_POLICY)
-  std::vector<std::unique_ptr<content::NavigationThrottle>>
-  CreateThrottlesForNavigation(
+  void CreateThrottlesForNavigation(
       content::NavigationThrottleRegistry& registry) override;
 #endif
 
diff --git a/headless/public/headless_export.h b/headless/public/headless_export.h
index 65c6c7e3..36ba5c26 100644
--- a/headless/public/headless_export.h
+++ b/headless/public/headless_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(HEADLESS_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(HEADLESS_IMPLEMENTATION)
 #define HEADLESS_EXPORT __attribute__((visibility("default")))
-#else
-#define HEADLESS_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/headless/test/headless_protocol_browsertest.cc b/headless/test/headless_protocol_browsertest.cc
index 7344a1d..2b675b67 100644
--- a/headless/test/headless_protocol_browsertest.cc
+++ b/headless/test/headless_protocol_browsertest.cc
@@ -290,12 +290,24 @@
                        "emulation/virtual-time-history-navigation-same-doc.js")
 HEADLESS_PROTOCOL_TEST(VirtualTimeSVG, "emulation/virtual-time-svg.js")
 
-HEADLESS_PROTOCOL_TEST(VirtualTimeWorkerBasic,
+// Flaky on Mac. TODO(crbug.com/352304682): Re-enable.
+#if BUILDFLAG(IS_MAC)
+#define MAYBE_VirtualTimeWorkerBasic DISABLED_VirtualTimeWorkerBasic
+#else
+#define MAYBE_VirtualTimeWorkerBasic VirtualTimeWorkerBasic
+#endif
+HEADLESS_PROTOCOL_TEST(MAYBE_VirtualTimeWorkerBasic,
                        "emulation/virtual-time-worker-basic.js")
 HEADLESS_PROTOCOL_TEST(VirtualTimeWorkerLockstep,
                        "emulation/virtual-time-worker-lockstep.js")
 
-HEADLESS_PROTOCOL_TEST(VirtualTimeWorkerFetch,
+// Flaky on Mac. TODO(crbug.com/352304682): Re-enable.
+#if BUILDFLAG(IS_MAC)
+#define MAYBE_VirtualTimeWorkerFetch DISABLED_VirtualTimeWorkerFetch
+#else
+#define MAYBE_VirtualTimeWorkerFetch VirtualTimeWorkerFetch
+#endif
+HEADLESS_PROTOCOL_TEST(MAYBE_VirtualTimeWorkerFetch,
                        "emulation/virtual-time-worker-fetch.js")
 HEADLESS_PROTOCOL_TEST(VirtualTimeWorkerTerminate,
                        "emulation/virtual-time-worker-terminate.js")
diff --git a/infra/config/console-header.star b/infra/config/console-header.star
index f4d0772..a25e179 100644
--- a/infra/config/console-header.star
+++ b/infra/config/console-header.star
@@ -167,6 +167,16 @@
                     alt = "Checks console",
                 ),
                 headers.link(
+                    text = "chromium",
+                    branch_selector = [
+                        branches.selector.ANDROID_BRANCHES,
+                        branches.selector.DESKTOP_BRANCHES,
+                        branches.selector.FUCHSIA_BRANCHES,
+                    ],
+                    url = "/p/{}/g/chromium".format(settings.project),
+                    alt = "Main Chromium archive console",
+                ),
+                headers.link(
                     text = "chromiumos",
                     branch_selector = branches.selector.CROS_LTS_BRANCHES,
                     url = "/p/{}/g/chromium.chromiumos".format(settings.project),
diff --git a/infra/config/generated/builders/ci/GPU FYI Win x64 Builder/targets/chromium.gpu.fyi.json b/infra/config/generated/builders/ci/GPU FYI Win x64 Builder/targets/chromium.gpu.fyi.json
index a4231e2..78e7c40 100644
--- a/infra/config/generated/builders/ci/GPU FYI Win x64 Builder/targets/chromium.gpu.fyi.json
+++ b/infra/config/generated/builders/ci/GPU FYI Win x64 Builder/targets/chromium.gpu.fyi.json
@@ -3044,6 +3044,30 @@
       },
       {
         "args": [
+          "--gtest_filter=MediaFoundationEncryptedMediaTest*",
+          "--use-gpu-in-tests"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "media_foundation_browser_tests",
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "1002:7480-32.0.12033.1030",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "browser_tests",
+        "test_id_prefix": "ninja://chrome/test:browser_tests/"
+      },
+      {
+        "args": [
           "--gtest_filter=WebNN*",
           "--use-gpu-in-tests"
         ],
diff --git "a/infra/config/generated/builders/ci/Win11 FYI x64 Release \050AMD RX 7600\051/targets/chromium.gpu.fyi.json" "b/infra/config/generated/builders/ci/Win11 FYI x64 Release \050AMD RX 7600\051/targets/chromium.gpu.fyi.json"
index f531e42..21bfc51 100644
--- "a/infra/config/generated/builders/ci/Win11 FYI x64 Release \050AMD RX 7600\051/targets/chromium.gpu.fyi.json"
+++ "b/infra/config/generated/builders/ci/Win11 FYI x64 Release \050AMD RX 7600\051/targets/chromium.gpu.fyi.json"
@@ -100,6 +100,30 @@
       },
       {
         "args": [
+          "--gtest_filter=MediaFoundationEncryptedMediaTest*",
+          "--use-gpu-in-tests"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "media_foundation_browser_tests",
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "1002:7480-32.0.12033.1030",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "browser_tests",
+        "test_id_prefix": "ninja://chrome/test:browser_tests/"
+      },
+      {
+        "args": [
           "--gtest_filter=WebNN*",
           "--use-gpu-in-tests"
         ],
diff --git a/infra/config/generated/builders/try/gpu-fyi-try-win11-amd-rel-64/targets/chromium.gpu.fyi.json b/infra/config/generated/builders/try/gpu-fyi-try-win11-amd-rel-64/targets/chromium.gpu.fyi.json
index e8d8046c..6a05ab0 100644
--- a/infra/config/generated/builders/try/gpu-fyi-try-win11-amd-rel-64/targets/chromium.gpu.fyi.json
+++ b/infra/config/generated/builders/try/gpu-fyi-try-win11-amd-rel-64/targets/chromium.gpu.fyi.json
@@ -101,6 +101,30 @@
       },
       {
         "args": [
+          "--gtest_filter=MediaFoundationEncryptedMediaTest*",
+          "--use-gpu-in-tests"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "media_foundation_browser_tests",
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "1002:7480-32.0.12033.1030",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "browser_tests",
+        "test_id_prefix": "ninja://chrome/test:browser_tests/"
+      },
+      {
+        "args": [
           "--gtest_filter=WebNN*",
           "--use-gpu-in-tests"
         ],
diff --git a/infra/config/generated/luci/luci-milo.cfg b/infra/config/generated/luci/luci-milo.cfg
index d8ad7229..0b1bb08 100644
--- a/infra/config/generated/luci/luci-milo.cfg
+++ b/infra/config/generated/luci/luci-milo.cfg
@@ -240,6 +240,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -1181,6 +1186,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -1940,6 +1950,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -2499,6 +2514,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -2876,6 +2896,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -3744,6 +3769,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -4211,6 +4241,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -4643,6 +4678,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -5012,6 +5052,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -5384,6 +5429,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -5756,6 +5806,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -6193,6 +6248,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -7189,6 +7249,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -7951,6 +8016,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -8318,6 +8388,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -8999,6 +9074,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -9381,6 +9461,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -9743,6 +9828,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -10194,6 +10284,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -10626,6 +10721,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -11114,6 +11214,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -11541,6 +11646,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -12143,6 +12253,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -12715,6 +12830,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -13337,6 +13457,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -13824,6 +13949,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -14213,6 +14343,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -14744,6 +14879,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -15373,6 +15513,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -15796,6 +15941,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -16554,6 +16704,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -16949,6 +17104,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -17692,6 +17852,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -18098,6 +18263,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -18544,6 +18714,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -19016,6 +19191,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -19483,6 +19663,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -19850,6 +20035,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -20337,6 +20527,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -20735,6 +20930,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -21137,6 +21337,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -21704,6 +21909,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -22101,6 +22311,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -22539,6 +22754,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -22971,6 +23191,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -23343,6 +23568,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -23715,6 +23945,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -24181,6 +24416,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -24783,6 +25023,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -25176,6 +25421,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -25580,6 +25830,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
@@ -25962,6 +26217,11 @@
         alt: "Checks console"
       }
       links {
+        text: "chromium"
+        url: "/p/chromium/g/chromium"
+        alt: "Main Chromium archive console"
+      }
+      links {
         text: "chromiumos"
         url: "/p/chromium/g/chromium.chromiumos"
         alt: "ChromiumOS console"
diff --git a/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star b/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star
index 8b854af..00a7674 100644
--- a/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star
+++ b/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star
@@ -3045,12 +3045,6 @@
                     "--test-launcher-filter-file=../../testing/buildbot/filters/win.amd.7600.gl_tests_passthrough.filter",
                 ],
             ),
-            "media_foundation_browser_tests": targets.remove(
-                reason = [
-                    "TODO(crbug.com/40912267): Enable Media Foundation browser tests on ",
-                    "gpu bots once the Windows OS supports HW secure decryption.",
-                ],
-            ),
         },
     ),
     targets_settings = targets.settings(
diff --git a/internal b/internal
index 2331975..20b9f62 160000
--- a/internal
+++ b/internal
@@ -1 +1 @@
-Subproject commit 23319755d0789a1e63cc4c1dfb6f38403c9bfa63
+Subproject commit 20b9f62811d76f8f5de0eba491b161f8bb948c6e
diff --git a/ios/chrome/browser/authentication/ui_bundled/authentication_flow/authentication_flow_unittest.mm b/ios/chrome/browser/authentication/ui_bundled/authentication_flow/authentication_flow_unittest.mm
index ebc69da..ebdb485 100644
--- a/ios/chrome/browser/authentication/ui_bundled/authentication_flow/authentication_flow_unittest.mm
+++ b/ios/chrome/browser/authentication/ui_bundled/authentication_flow/authentication_flow_unittest.mm
@@ -113,34 +113,6 @@
     }
 
     run_loop_ = std::make_unique<base::RunLoop>();
-    sign_in_completion_ = ^(SigninCoordinatorResult result) {
-      run_loop_->Quit();
-      switch (result) {
-        case SigninCoordinatorResult::SigninCoordinatorResultSuccess:
-          signin_result_ = signin::Tribool::kTrue;
-          break;
-        case SigninCoordinatorResult::SigninCoordinatorResultInterrupted:
-        case SigninCoordinatorResult::SigninCoordinatorResultCanceledByUser:
-        case SigninCoordinatorResult::SigninCoordinatorResultDisabled:
-        case SigninCoordinatorResult::SigninCoordinatorUINotAvailable:
-        case SigninCoordinatorResult::SigninCoordinatorProfileSwitch:
-          signin_result_ = signin::Tribool::kFalse;
-          break;
-      }
-    };
-    continuation_provider_ = base::BindRepeating(
-        [](signin_ui::SigninCompletionCallback sign_in_completion) {
-          ChangeProfileContinuation continuation = base::BindOnce(
-              [](signin_ui::SigninCompletionCallback sign_in_completion,
-                 SceneState* sceneState, base::OnceClosure closure) {
-                sign_in_completion(
-                    SigninCoordinatorResult::SigninCoordinatorResultSuccess);
-                std::move(closure).Run();
-              },
-              sign_in_completion);
-          return continuation;
-        },
-        sign_in_completion_);
   }
 
   void TearDown() override {
@@ -148,17 +120,6 @@
     EXPECT_OCMOCK_VERIFY((id)view_controller_mock_);
     EXPECT_OCMOCK_VERIFY((id)performer_mock_);
   }
-  // Reset the authentication_flow_’s request helper.
-  // Must be call before each `startSignIn`
-  void ResetAuthenticationFlowRequestHelper() {
-    // Each mock expect its methods to be called at most once.
-    test_authentication_flow_request_helper_ =
-        [[TestAuthenticationFlowRequest alloc]
-             initWithSigninCompletionCallback:sign_in_completion_
-            changeProfileContinuationProvider:continuation_provider_];
-    authentication_flow_.requestHelper =
-        test_authentication_flow_request_helper_;
-  }
 
   TestProfileIOS* CreateProfile(
       std::optional<std::string> name = std::nullopt) {
@@ -197,6 +158,7 @@
                                 signin_metrics::AccessPoint accessPoint,
                                 BOOL shouldHandOverToFlowInProfile) {
     view_controller_mock_ = OCMClassMock([UIViewController class]);
+    CHECK(!authentication_flow_);
     authentication_flow_ =
         [[AuthenticationFlow alloc] initWithBrowser:personal_browser_.get()
                                            identity:identity
@@ -230,6 +192,47 @@
           })
           .andReturn(performer_mock_);
     }
+
+    signin_ui::SigninCompletionCallback sign_in_completion =
+        ^(SigninCoordinatorResult result) {
+          run_loop_->Quit();
+          switch (result) {
+            case SigninCoordinatorResult::SigninCoordinatorResultSuccess:
+              signin_result_ = signin::Tribool::kTrue;
+              break;
+            case SigninCoordinatorResult::SigninCoordinatorResultInterrupted:
+            case SigninCoordinatorResult::SigninCoordinatorResultCanceledByUser:
+            case SigninCoordinatorResult::SigninCoordinatorResultDisabled:
+            case SigninCoordinatorResult::SigninCoordinatorUINotAvailable:
+            case SigninCoordinatorResult::SigninCoordinatorProfileSwitch:
+              signin_result_ = signin::Tribool::kFalse;
+              break;
+          }
+          authentication_flow_ = nil;
+        };
+    // Runs the sign_in_completion with Success and the closure.
+    ChangeProfileContinuationProvider continuation_provider =
+        base::BindRepeating(
+            [](signin_ui::SigninCompletionCallback sign_in_completion) {
+              ChangeProfileContinuation continuation = base::BindOnce(
+                  [](signin_ui::SigninCompletionCallback sign_in_completion,
+                     SceneState* sceneState, base::OnceClosure closure) {
+                    sign_in_completion(SigninCoordinatorResult::
+                                           SigninCoordinatorResultSuccess);
+                    std::move(closure).Run();
+                  },
+                  sign_in_completion);
+              return continuation;
+            },
+            sign_in_completion);
+
+    // Each mock expect its methods to be called at most once.
+    test_authentication_flow_request_helper_ =
+        [[TestAuthenticationFlowRequest alloc]
+             initWithSigninCompletionCallback:sign_in_completion
+            changeProfileContinuationProvider:continuation_provider];
+    authentication_flow_.requestHelper =
+        test_authentication_flow_request_helper_;
   }
 
   // Checks if the AuthenticationFlow operation has completed, and whether it
@@ -393,7 +396,6 @@
                                                  browser:final_browser
                                              accessPoint:access_point]);
 
-    ResetAuthenticationFlowRequestHelper();
     [authentication_flow_ startSignIn];
     // The completion block should not be called synchronously.
     EXPECT_EQ(signin::Tribool::kUnknown, signin_result_);
@@ -435,8 +437,6 @@
   AuthenticationFlowInProfile<AuthenticationFlowPerformerDelegate>*
       authentication_flow_in_profile_ = nil;
   AuthenticationFlowPerformer* performer_mock_ = nil;
-  signin_ui::SigninCompletionCallback sign_in_completion_;
-  ChangeProfileContinuationProvider continuation_provider_;
   UIViewController* view_controller_mock_;
   // Used to verify histogram logging.
   base::HistogramTester histogram_tester_;
@@ -480,7 +480,6 @@
         [invocation getArgument:&completionBlock atIndex:3];
         completionBlock();
       });
-  ResetAuthenticationFlowRequestHelper();
   [authentication_flow_ startSignIn];
 
   CheckSignInCompletion(/*expected_signed_in=*/false);
@@ -580,7 +579,6 @@
         run_loop_->Quit();
       });
 
-  ResetAuthenticationFlowRequestHelper();
   [authentication_flow_ startSignIn];
   run_loop_->Run();
 }
@@ -623,7 +621,6 @@
         run_loop_->Quit();
       });
 
-  ResetAuthenticationFlowRequestHelper();
   [authentication_flow_ startSignIn];
   run_loop_->Run();
 }
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_mediator.mm b/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_mediator.mm
index e4bbaaf..fb1771d 100644
--- a/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_mediator.mm
+++ b/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_mediator.mm
@@ -460,6 +460,8 @@
     case AccountMenuAccessPoint::kSettings:
       return CreateChangeProfileSettingsContinuation();
     case AccountMenuAccessPoint::kWeb: {
+      GetApplicationContext()->GetLocalState()->SetBoolean(
+          prefs::kHasSwitchedAccountsViaWebFlow, true);
       if (_prepareChangeProfile) {
         _prepareChangeProfile();
       };
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/signin_history_sync/signin_and_history_sync_coordinator.mm b/ios/chrome/browser/authentication/ui_bundled/signin/signin_history_sync/signin_and_history_sync_coordinator.mm
index 2ba6d5d..863c38f 100644
--- a/ios/chrome/browser/authentication/ui_bundled/signin/signin_history_sync/signin_and_history_sync_coordinator.mm
+++ b/ios/chrome/browser/authentication/ui_bundled/signin/signin_history_sync/signin_and_history_sync_coordinator.mm
@@ -54,8 +54,10 @@
 @end
 
 @implementation SignInAndHistorySyncCoordinator {
-  // Sign-in or history sync coordinator, according to `_currentStep`.
-  AnimatedCoordinator* _childCoordinator;
+  // Sign-in coordinator, according to `_currentStep`.
+  SigninCoordinator* _signinCoordinator;
+  // HistorySyncPopupCoordinator for SignInHistorySyncStep::kHistorySync.
+  HistorySyncPopupCoordinator* _historySyncPopupCoordinator;
   // The current step.
   SignInHistorySyncStep _currentStep;
   // Promo button used to trigger the sign-in.
@@ -95,7 +97,10 @@
 }
 
 - (void)dealloc {
-  DCHECK(!_childCoordinator) << base::SysNSStringToUTF8([self description]);
+  CHECK(!_signinCoordinator, base::NotFatalUntil::M145)
+      << base::SysNSStringToUTF8([self description]);
+  CHECK(!_historySyncPopupCoordinator, base::NotFatalUntil::M145)
+      << base::SysNSStringToUTF8([self description]);
 }
 
 #pragma mark - ChromeCoordinator
@@ -111,8 +116,8 @@
 #pragma mark - AnimatedCoordinator
 
 - (void)stopAnimated:(BOOL)animated {
-  [_childCoordinator stopAnimated:animated];
-  _childCoordinator = nil;
+  [self stopSigninCoordinatorAnimated:animated];
+  [self stopHistorySyncPopupCoordinatorAnimated:animated];
   _syncService = nullptr;
   _authenticationService = nullptr;
   [super stopAnimated:animated];
@@ -122,20 +127,31 @@
 
 - (void)historySyncPopupCoordinator:(HistorySyncPopupCoordinator*)coordinator
                 didFinishWithResult:(SigninCoordinatorResult)result {
-  [self currentStepDidFinishWithResult:result];
+  CHECK_EQ(coordinator, _historySyncPopupCoordinator,
+           base::NotFatalUntil::M145);
+  [self stopHistorySyncPopupCoordinatorAnimated:YES];
+  [self presentNextStepWithPreviousResult:result];
 }
 
 #pragma mark - Private
 
-- (void)stopChildCoordinator {
-  [_childCoordinator stop];
-  _childCoordinator = nil;
+- (void)stopHistorySyncPopupCoordinatorAnimated:(BOOL)animated {
+  [_historySyncPopupCoordinator stopAnimated:animated];
+  _historySyncPopupCoordinator.delegate = nil;
+  _historySyncPopupCoordinator = nil;
+}
+
+- (void)stopSigninCoordinatorAnimated:(BOOL)animated {
+  [_signinCoordinator stop];
+  _signinCoordinator = nil;
 }
 
 // Moves to the next step and presents the coordinator of that next step.
 - (void)presentNextStepWithPreviousResult:
     (SigninCoordinatorResult)previousResult {
-  CHECK(!_childCoordinator) << base::SysNSStringToUTF8([self description]);
+  CHECK(!_signinCoordinator) << base::SysNSStringToUTF8([self description]);
+  CHECK(!_historySyncPopupCoordinator)
+      << base::SysNSStringToUTF8([self description]);
   switch (previousResult) {
     case SigninCoordinatorResultSuccess:
     case SigninCoordinatorResultDisabled:
@@ -154,8 +170,7 @@
       NOTREACHED();
   }
   if (_currentStep != SignInHistorySyncStep::kCompleted) {
-    _childCoordinator = [self createPresentStepChildCoordinator];
-    [_childCoordinator start];
+    [self createAndPresentStepChildCoordinator];
     return;
   }
   // If there are no steps remaining, call delegate to stop presenting
@@ -181,11 +196,11 @@
 }
 
 // Creates the current step coordinator according to `_currentStep`.
-- (AnimatedCoordinator*)createPresentStepChildCoordinator {
+- (void)createAndPresentStepChildCoordinator {
   switch (_currentStep) {
     case SignInHistorySyncStep::kFullscreenSignin: {
       // TODO(crbug.com/375605572) Sends an actual continuation.
-      SigninCoordinator* coordinator = [[FullscreenSigninCoordinator alloc]
+      _signinCoordinator = [[FullscreenSigninCoordinator alloc]
                  initWithBaseViewController:self.baseViewController
                                     browser:self.browser
                              screenProvider:[[SigninScreenProvider alloc] init]
@@ -193,31 +208,32 @@
                                 accessPoint:self.accessPoint
           changeProfileContinuationProvider:_continuationProvider];
       __weak __typeof(self) weakSelf = self;
-      coordinator.signinCompletion =
+      _signinCoordinator.signinCompletion =
           ^(SigninCoordinatorResult result, id<SystemIdentity>) {
-            [weakSelf currentStepDidFinishWithResult:result];
+            [weakSelf currentSigninStepDidFinishWithResult:result];
           };
-      return coordinator;
+      [_signinCoordinator start];
+      return;
     }
     case SignInHistorySyncStep::kBottomSheetSignin: {
-      SigninCoordinator* coordinator =
-          [[ConsistencyPromoSigninCoordinator alloc]
-              initWithBaseViewController:self.baseViewController
-                                 browser:self.browser
-                            contextStyle:self.contextStyle
-                             accessPoint:self.accessPoint
-                    prepareChangeProfile:nil
-                    continuationProvider:_continuationProvider];
+      _signinCoordinator = [[ConsistencyPromoSigninCoordinator alloc]
+          initWithBaseViewController:self.baseViewController
+                             browser:self.browser
+                        contextStyle:self.contextStyle
+                         accessPoint:self.accessPoint
+                prepareChangeProfile:nil
+                continuationProvider:_continuationProvider];
       __weak __typeof(self) weakSelf = self;
-      coordinator.signinCompletion =
+      _signinCoordinator.signinCompletion =
           ^(SigninCoordinatorResult result, id<SystemIdentity>) {
-            [weakSelf currentStepDidFinishWithResult:result];
+            [weakSelf currentSigninStepDidFinishWithResult:result];
           };
-      return coordinator;
+      [_signinCoordinator start];
+      return;
     }
     case SignInHistorySyncStep::kInstantSignin: {
       // TODO(crbug.com/375605572) Sends an actual continuation.
-      SigninCoordinator* coordinator = [[InstantSigninCoordinator alloc]
+      _signinCoordinator = [[InstantSigninCoordinator alloc]
           initWithBaseViewController:self.baseViewController
                              browser:self.browser
                             identity:nil
@@ -226,11 +242,12 @@
                          promoAction:_promoAction
                 continuationProvider:_continuationProvider];
       __weak __typeof(self) weakSelf = self;
-      coordinator.signinCompletion =
+      _signinCoordinator.signinCompletion =
           ^(SigninCoordinatorResult result, id<SystemIdentity>) {
-            [weakSelf currentStepDidFinishWithResult:result];
+            [weakSelf currentSigninStepDidFinishWithResult:result];
           };
-      return coordinator;
+      [_signinCoordinator start];
+      return;
     }
     case SignInHistorySyncStep::kHistorySync: {
       if (history_sync::GetSkipReason(_syncService, _authenticationService,
@@ -239,20 +256,19 @@
           history_sync::HistorySyncSkipReason::kNone) {
         [self
             presentNextStepWithPreviousResult:SigninCoordinatorResultDisabled];
-        return nil;
       } else {
-        HistorySyncPopupCoordinator* coordinator =
-            [[HistorySyncPopupCoordinator alloc]
-                initWithBaseViewController:self.baseViewController
-                                   browser:self.browser
-                             showUserEmail:NO
-                         signOutIfDeclined:NO
-                                isOptional:_optionalHistorySync
-                              contextStyle:self.contextStyle
-                               accessPoint:self.accessPoint];
-        coordinator.delegate = self;
-        return coordinator;
+        _historySyncPopupCoordinator = [[HistorySyncPopupCoordinator alloc]
+            initWithBaseViewController:self.baseViewController
+                               browser:self.browser
+                         showUserEmail:NO
+                     signOutIfDeclined:NO
+                            isOptional:_optionalHistorySync
+                          contextStyle:self.contextStyle
+                           accessPoint:self.accessPoint];
+        _historySyncPopupCoordinator.delegate = self;
+        [_historySyncPopupCoordinator start];
       }
+      return;
     }
     case SignInHistorySyncStep::kStart:
     case SignInHistorySyncStep::kCompleted:
@@ -262,11 +278,13 @@
 }
 
 // Stops the child coordinator and prepares the next step to present.
-- (void)currentStepDidFinishWithResult:(SigninCoordinatorResult)result {
+- (void)currentSigninStepDidFinishWithResult:(SigninCoordinatorResult)result {
   // TODO(crbug.com/40929259): Turn into CHECK.
-  DUMP_WILL_BE_CHECK(_childCoordinator)
+  DUMP_WILL_BE_CHECK(_signinCoordinator)
       << base::SysNSStringToUTF8([self description]);
-  [self stopChildCoordinator];
+  DUMP_WILL_BE_CHECK(!_historySyncPopupCoordinator)
+      << base::SysNSStringToUTF8([self description]);
+  [self stopSigninCoordinatorAnimated:YES];
   [self presentNextStepWithPreviousResult:result];
 }
 
@@ -301,9 +319,11 @@
 
 - (NSString*)description {
   return [NSString
-      stringWithFormat:@"<%@: %p, childcoordinator: %@, currentStep: %d, "
+      stringWithFormat:@"<%@: %p, signinCoordinator: %@, "
+                        "historySyncPopupCoordinator: %@, currentStep: %d, "
                        @"accessPoint %d, promoAction %d>",
-                       self.class.description, self, _childCoordinator,
+                       self.class.description, self, _signinCoordinator,
+                       _historySyncPopupCoordinator,
                        static_cast<int>(_currentStep),
                        static_cast<int>(self.accessPoint),
                        static_cast<int>(_promoAction)];
diff --git a/ios/chrome/browser/intelligence/glic/ui/BUILD.gn b/ios/chrome/browser/intelligence/glic/ui/BUILD.gn
index d2dd9a9..2df0074e 100644
--- a/ios/chrome/browser/intelligence/glic/ui/BUILD.gn
+++ b/ios/chrome/browser/intelligence/glic/ui/BUILD.gn
@@ -11,11 +11,21 @@
     "glic_promo_display_handler.mm",
   ]
   deps = [
+    ":constants",
     "//base",
     "//components/feature_engagement/public:feature_constants",
     "//ios/chrome/browser/promos_manager/model:types",
     "//ios/chrome/browser/promos_manager/ui_bundled:promos",
+    "//ios/chrome/browser/shared/ui/symbols",
+    "//ios/chrome/common/ui/colors",
     "//ios/chrome/common/ui/promo_style",
   ]
   frameworks = [ "UIKit.framework" ]
 }
+
+source_set("constants") {
+  sources = [
+    "glic_constants.h",
+    "glic_constants.mm",
+  ]
+}
diff --git a/ios/chrome/browser/intelligence/glic/ui/glic_consent_view_controller.mm b/ios/chrome/browser/intelligence/glic/ui/glic_consent_view_controller.mm
index f6a9db3..0e30183e 100644
--- a/ios/chrome/browser/intelligence/glic/ui/glic_consent_view_controller.mm
+++ b/ios/chrome/browser/intelligence/glic/ui/glic_consent_view_controller.mm
@@ -5,43 +5,108 @@
 #import "ios/chrome/browser/intelligence/glic/ui/glic_consent_view_controller.h"
 
 #import "ios/chrome/browser/intelligence/glic/ui/glic_consent_mutator.h"
+#import "ios/chrome/browser/intelligence/glic/ui/glic_constants.h"
+#import "ios/chrome/browser/shared/ui/symbols/symbols.h"
+#import "ios/chrome/common/ui/colors/semantic_color_names.h"
 #import "ios/chrome/common/ui/promo_style/promo_style_view_controller_delegate.h"
 
 @interface GLICConsentViewController () <PromoStyleViewControllerDelegate>
-
 @end
 
-@implementation GLICConsentViewController
+@implementation GLICConsentViewController {
+  UIStackView* _mainStackView;
+}
 
 #pragma mark - UIViewController
 
 // TODO(crbug.com/414777915): Implement a basic UI.
 - (void)viewDidLoad {
   self.delegate = self;
+  [self configureSheetPresentation];
+  [self configurePromoStyleProperties];
+  [super viewDidLoad];
+  // The stackview should be added after `viewDidLoad`, so that the
+  // `UIScrollView` is not placed on top of the stackview.
+  [self setupStackView];
+}
+
+#pragma mark - Private
+
+// Configure all the stacks.
+- (void)setupStackView {
+  [self configureMainStackView];
+}
+
+// Configure the differents detents for the expanded and collapsed view.
+- (void)configureSheetPresentation {
+  self.modalPresentationStyle = UIModalPresentationPageSheet;
+
+  self.sheetPresentationController.detents = @[
+    [self customHeightDetentWithIdentifier:kGLICConsentPartialDetentIdentifier
+                                    height:kGLICConsentPartialDetentHeight],
+    [self customHeightDetentWithIdentifier:kGLICConsentFullDetentIdentifier
+                                    height:kGLICConsentFullDetentHeight]
+  ];
+
+  self.sheetPresentationController.selectedDetentIdentifier =
+      kGLICConsentPartialDetentIdentifier;
+
+  self.sheetPresentationController.preferredCornerRadius =
+      kGLICConsentPreferredCornerRadius;
+  self.sheetPresentationController.prefersScrollingExpandsWhenScrolledToEdge =
+      NO;
+}
+
+// Create a custom sheet presentation controller detent with a fixed height.
+- (UISheetPresentationControllerDetent*)
+    customHeightDetentWithIdentifier:(NSString*)identifier
+                              height:(CGFloat)height {
+  auto resolver = ^CGFloat(
+      id<UISheetPresentationControllerDetentResolutionContext> context) {
+    return height;
+  };
+
+  return
+      [UISheetPresentationControllerDetent customDetentWithIdentifier:identifier
+                                                             resolver:resolver];
+}
+
+// Configure promo style properties to add buttons. Ignores header image type.
+- (void)configurePromoStyleProperties {
   self.layoutBehindNavigationBar = YES;
   self.shouldHideBanner = YES;
   self.headerImageType = PromoStyleImageType::kNone;
 
-  self.modalPresentationStyle = UIModalPresentationPageSheet;
+  self.primaryActionString = kGLICConsentPrimaryAction;
+  self.secondaryActionString = kGLICConsentSecondaryAction;
+}
 
-  // TODO(crbug.com/414777890): Use a custom detent.
-  self.sheetPresentationController.detents = @[
-    [UISheetPresentationControllerDetent mediumDetent],
-    [UISheetPresentationControllerDetent largeDetent]
-  ];
+// Configure the main stack view.
+- (void)configureMainStackView {
+  _mainStackView = [[UIStackView alloc] init];
+  _mainStackView.axis = UILayoutConstraintAxisVertical;
+  _mainStackView.distribution = UIStackViewDistributionFill;
+  _mainStackView.alignment = UIStackViewAlignmentFill;
+  _mainStackView.spacing = kGLICConsentMainStackSpacing;
 
-  self.sheetPresentationController.preferredCornerRadius = 16.0;
+  _mainStackView.translatesAutoresizingMaskIntoConstraints = NO;
 
-  UIView* rootView = [[UIView alloc] initWithFrame:UIScreen.mainScreen.bounds];
-  rootView.backgroundColor = [UIColor systemBackgroundColor];
-  [self.view addSubview:rootView];
+  [self.view addSubview:_mainStackView];
 
-  // TODO(crbug.com/414778685): Add strings.
-  self.primaryActionString = @"Yes, I'm in";
-  self.secondaryActionString = @"No thanks";
-
-  self.bannerSize = BannerImageSizeType::kStandard;
-  [super viewDidLoad];
+  [NSLayoutConstraint activateConstraints:@[
+    [_mainStackView.leadingAnchor
+        constraintEqualToAnchor:self.view.leadingAnchor
+                       constant:kGLICConsentMainStackHorizontalInset],
+    [_mainStackView.trailingAnchor
+        constraintEqualToAnchor:self.view.trailingAnchor
+                       constant:-kGLICConsentMainStackHorizontalInset],
+    [_mainStackView.topAnchor
+        constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor
+                       constant:kGLICConsentMainStackTopInset],
+    [_mainStackView.bottomAnchor
+        constraintLessThanOrEqualToAnchor:self.view.safeAreaLayoutGuide
+                                              .bottomAnchor]
+  ]];
 }
 
 #pragma mark - PromoStyleViewControllerDelegate
diff --git a/ios/chrome/browser/intelligence/glic/ui/glic_constants.h b/ios/chrome/browser/intelligence/glic/ui/glic_constants.h
new file mode 100644
index 0000000..de0642a
--- /dev/null
+++ b/ios/chrome/browser/intelligence/glic/ui/glic_constants.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 IOS_CHROME_BROWSER_INTELLIGENCE_GLIC_UI_GLIC_CONSTANTS_H_
+#define IOS_CHROME_BROWSER_INTELLIGENCE_GLIC_UI_GLIC_CONSTANTS_H_
+
+#import <Foundation/Foundation.h>
+#import <UIKit/UIKit.h>
+
+// Sheet detents.
+extern NSString* const kGLICConsentPartialDetentIdentifier;
+extern NSString* const kGLICConsentFullDetentIdentifier;
+extern const CGFloat kGLICConsentPartialDetentHeight;
+extern const CGFloat kGLICConsentFullDetentHeight;
+extern const CGFloat kGLICConsentPreferredCornerRadius;
+
+// Stack view insets and spacing.
+extern const CGFloat kGLICConsentMainStackHorizontalInset;
+extern const CGFloat kGLICConsentMainStackTopInset;
+extern const CGFloat kGLICConsentMainStackSpacing;
+
+// Promo style strings.
+extern NSString* const kGLICConsentPrimaryAction;
+extern NSString* const kGLICConsentSecondaryAction;
+
+#endif  // IOS_CHROME_BROWSER_INTELLIGENCE_GLIC_UI_GLIC_CONSTANTS_H_
diff --git a/ios/chrome/browser/intelligence/glic/ui/glic_constants.mm b/ios/chrome/browser/intelligence/glic/ui/glic_constants.mm
new file mode 100644
index 0000000..17cbd85
--- /dev/null
+++ b/ios/chrome/browser/intelligence/glic/ui/glic_constants.mm
@@ -0,0 +1,24 @@
+// 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/intelligence/glic/ui/glic_constants.h"
+
+// Sheet detents.
+NSString* const kGLICConsentPartialDetentIdentifier =
+    @"GLICConsentPartialDetentIdentifier";
+NSString* const kGLICConsentFullDetentIdentifier =
+    @"GLICConsentFullDetentIdentifier";
+const CGFloat kGLICConsentPartialDetentHeight = 300.0;
+const CGFloat kGLICConsentFullDetentHeight = 500.0;
+const CGFloat kGLICConsentPreferredCornerRadius = 10.0;
+
+// Stack view insets and spacing.
+const CGFloat kGLICConsentMainStackHorizontalInset = 20.0;
+const CGFloat kGLICConsentMainStackTopInset = 24.0;
+const CGFloat kGLICConsentMainStackSpacing = 16.0;
+
+// Promo style strings.
+// TODO(crbug.com/414778685): Add strings.
+NSString* const kGLICConsentPrimaryAction = @"Yes, I'm in";
+NSString* const kGLICConsentSecondaryAction = @"No thanks";
diff --git a/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_coordinator.mm b/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_coordinator.mm
index 4e88298..0345f7b 100644
--- a/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_coordinator.mm
+++ b/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_coordinator.mm
@@ -301,7 +301,14 @@
 - (void)searchWithLensImageMetadata:(id<LensImageMetadata>)metadata
                          entrypoint:(LensOverlayEntrypoint)entrypoint
                          completion:(void (^)(BOOL))completion {
-  [self prepareOverlayWithEntrypoint:entrypoint];
+  BOOL success = [self prepareOverlayWithEntrypoint:entrypoint];
+  if (!success) {
+    if (completion) {
+      completion(NO);
+    }
+
+    return;
+  }
   // Even if the image is already prepared at this point, the snapshotting
   // infrastructure still needs to be built to allow the restoration window to
   // be displayed when exiting and re-entering the experience.
@@ -316,7 +323,13 @@
 - (void)searchImageWithLens:(UIImage*)image
                  entrypoint:(LensOverlayEntrypoint)entrypoint
                  completion:(void (^)(BOOL))completion {
-  [self prepareOverlayWithEntrypoint:entrypoint];
+  BOOL success = [self prepareOverlayWithEntrypoint:entrypoint];
+  if (!success) {
+    if (completion) {
+      completion(NO);
+    }
+    return;
+  }
   // Even if the image is already prepared at this point, the snapshotting
   // infrastructure still needs to be built to allow the restoration window to
   // be displayed when exiting and re-entering the experience.
@@ -331,7 +344,13 @@
 - (void)createAndShowLensUI:(BOOL)animated
                  entrypoint:(LensOverlayEntrypoint)entrypoint
                  completion:(void (^)(BOOL))completion {
-  [self prepareOverlayWithEntrypoint:entrypoint];
+  BOOL success = [self prepareOverlayWithEntrypoint:entrypoint];
+  if (!success) {
+    if (completion) {
+      completion(NO);
+    }
+    return;
+  }
   __weak __typeof(self) weakSelf = self;
   [self captureSnapshotWithCompletion:^(UIImage* snapshot) {
     LensImageSource* imageSource =
@@ -945,7 +964,7 @@
 #pragma mark - private
 
 // Prepares the lens overlay for display from the given entrypoint.
-- (void)prepareOverlayWithEntrypoint:(LensOverlayEntrypoint)entrypoint {
+- (BOOL)prepareOverlayWithEntrypoint:(LensOverlayEntrypoint)entrypoint {
   if (self.isUICreated) {
     // The UI is probably associated with the non-active tab. Destroy it with no
     // animation.
@@ -960,7 +979,12 @@
            object:nil];
 
   _entrypoint = entrypoint;
-  _associatedTabHelper = self.activeTabHelper->GetWeakPtr();
+
+  LensOverlayTabHelper* tabHelper = self.activeTabHelper;
+  if (!tabHelper) {
+    return NO;
+  }
+  _associatedTabHelper = tabHelper->GetWeakPtr();
 
   _metricsRecorder = [[LensOverlayMetricsRecorder alloc]
       initWithEntrypoint:entrypoint
@@ -970,6 +994,8 @@
   // handler for the associated tab.
   _associatedTabHelper->SetLensOverlayCommandsHandler(self);
   _associatedTabHelper->SetLensOverlayUIAttachedAndAlive(true);
+
+  return YES;
 }
 
 // Opens a given URL in a new tab.
diff --git a/ios/chrome/browser/omnibox/model/omnibox_controller_ios.h b/ios/chrome/browser/omnibox/model/omnibox_controller_ios.h
index 1802767f..3d11428 100644
--- a/ios/chrome/browser/omnibox/model/omnibox_controller_ios.h
+++ b/ios/chrome/browser/omnibox/model/omnibox_controller_ios.h
@@ -93,12 +93,6 @@
                                 bool hidden) const;
 
  private:
-  // Stores the bitmap, using `icon_url` as the key in
-  // `edit_model_->icon_bitmaps_` if provided, or `result_index` in
-  // `edit_model_->rich_suggestion_bitmaps_` otherwise.
-  void SetRichSuggestionBitmap(int result_index,
-                               const GURL& icon_url,
-                               const SkBitmap& bitmap);
 
   // Called when the prefs for the visibility of groups changes.
   void OnSuggestionGroupVisibilityPrefChanged();
diff --git a/ios/chrome/browser/omnibox/model/omnibox_controller_ios.mm b/ios/chrome/browser/omnibox/model/omnibox_controller_ios.mm
index 36fcb09..f70913b 100644
--- a/ios/chrome/browser/omnibox/model/omnibox_controller_ios.mm
+++ b/ios/chrome/browser/omnibox/model/omnibox_controller_ios.mm
@@ -5,6 +5,7 @@
 #import "ios/chrome/browser/omnibox/model/omnibox_controller_ios.h"
 
 #import "base/functional/bind.h"
+#import "base/functional/callback_helpers.h"
 #import "base/metrics/histogram.h"
 #import "base/strings/utf_string_conversions.h"
 #import "base/trace_event/trace_event.h"
@@ -144,11 +145,10 @@
   // passed in to eliminate the potential for crashes on shutdown.
   // `should_preload` is set to `controller->done()` as prerender may only want
   // to start preloading a result after all Autocomplete results are ready.
-  client_->OnResultChanged(
-      autocomplete_controller_->result(), default_match_changed,
-      /*should_preload=*/controller->done(),
-      base::BindRepeating(&OmniboxControllerIOS::SetRichSuggestionBitmap,
-                          weak_ptr_factory_.GetWeakPtr()));
+  client_->OnResultChanged(autocomplete_controller_->result(),
+                           default_match_changed,
+                           /*should_preload=*/controller->done(),
+                           /*on_bitmap_fetched=*/base::DoNothing());
 }
 
 void OmniboxControllerIOS::ClearPopupKeywordMode() const {
@@ -198,16 +198,6 @@
   }
 }
 
-void OmniboxControllerIOS::SetRichSuggestionBitmap(int result_index,
-                                                   const GURL& icon_url,
-                                                   const SkBitmap& bitmap) {
-  if (!icon_url.is_empty()) {
-    edit_model_->SetIconBitmap(icon_url, bitmap);
-  } else {
-    edit_model_->SetPopupRichSuggestionBitmap(result_index, bitmap);
-  }
-}
-
 void OmniboxControllerIOS::OnSuggestionGroupVisibilityPrefChanged() {
   for (size_t i = 0; i < autocomplete_controller_->result().size(); ++i) {
     const AutocompleteMatch& match =
diff --git a/ios/chrome/browser/omnibox/model/omnibox_edit_model_ios.h b/ios/chrome/browser/omnibox/model/omnibox_edit_model_ios.h
index 790774e81..bc0c264 100644
--- a/ios/chrome/browser/omnibox/model/omnibox_edit_model_ios.h
+++ b/ios/chrome/browser/omnibox/model/omnibox_edit_model_ios.h
@@ -39,31 +39,6 @@
 
 class OmniboxEditModelIOS {
  public:
-  struct State {
-    State(bool user_input_in_progress,
-          const std::u16string& user_text,
-          const std::u16string& keyword,
-          const std::u16string& keyword_placeholder,
-          bool is_keyword_hint,
-          metrics::OmniboxEventProto::KeywordModeEntryMethod
-              keyword_mode_entry_method,
-          OmniboxFocusState focus_state,
-          const AutocompleteInput& autocomplete_input);
-    State(const State& other);
-    ~State();
-    State& operator=(const State&) = delete;
-
-    bool user_input_in_progress;
-    const std::u16string user_text;
-    const std::u16string keyword;
-    const std::u16string keyword_placeholder;
-    const bool is_keyword_hint;
-    metrics::OmniboxEventProto::KeywordModeEntryMethod
-        keyword_mode_entry_method;
-    OmniboxFocusState focus_state;
-    const AutocompleteInput autocomplete_input;
-  };
-
   OmniboxEditModelIOS(OmniboxControllerIOS* controller, OmniboxViewBase* view);
   virtual ~OmniboxEditModelIOS();
   OmniboxEditModelIOS(const OmniboxEditModelIOS&) = delete;
@@ -75,14 +50,6 @@
 
   metrics::OmniboxEventProto::PageClassification GetPageClassification() const;
 
-  // Returns the current state.  This assumes we are switching tabs, and changes
-  // the internal state appropriately.
-  State GetStateForTabSwitch() const;
-
-  // Resets the tab state, then restores local state from `state`. `state` may
-  // be nullptr if there is no saved state.
-  void RestoreState(const State* state);
-
   // Returns the match for the current text. If the user has not edited the text
   // this is the match corresponding to the permanent text. Returns the
   // alternate nav URL, if `alternate_nav_url` is non-NULL and there is such a
@@ -127,16 +94,6 @@
 
   bool user_input_in_progress() const { return user_input_in_progress_; }
 
-  // Encapsulates all the varied conditions for whether to override the
-  // permanent page icon (associated with the currently displayed page),
-  // with a temporary icon (associated with the current match or user text).
-  bool ShouldShowCurrentPageIcon() const;
-
-  // Returns the SuperGIcon for chrome builds. Otherwise return an empty
-  // ImageModel. If `dark_mode` is enabled, return the monochrome version of the
-  // icon.
-  ui::ImageModel GetSuperGIcon(int image_size, bool dark_mode) const;
-
   // Sets the state of user_input_in_progress_, and notifies the observer if
   // that state has changed.
   void SetInputInProgress(bool in_progress);
@@ -387,15 +344,6 @@
   // Returns true if the destination URL of the match is bookmarked.
   bool IsStarredMatch(const AutocompleteMatch& match) const;
 
-#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
-  // Gets the icon for the given `match`.
-  gfx::Image GetMatchIcon(const AutocompleteMatch& match,
-                          SkColor vector_icon_color) const;
-  // Gets the icon for the given `match` if the match was provided by an omnibox
-  // API extension, otherwise returns empty image.
-  gfx::Image GetMatchIconIfExtension(const AutocompleteMatch& match) const;
-#endif
-
   // Returns true if the popup exists and is open. Virtual for testing.
   virtual bool PopupIsOpen() const;
 
@@ -451,32 +399,6 @@
   // preserved here only to prevent possible behavior change while refactoring.
   void OnPopupResultChanged();
 
-  // Lookup the bitmap for `result_index`. Returns nullptr if not found.
-  const SkBitmap* GetPopupRichSuggestionBitmap(int result_index) const;
-
-  // Lookup the bitmap for the first `match` in
-  // `autocomplete_controller()->result()` that has `keyword` as its
-  // `associated_keyword`. Used to fetch bitmap where the `result_index` is
-  // unknown.  Returns nullptr if not found.
-  const SkBitmap* GetPopupRichSuggestionBitmap(
-      const std::u16string& keyword) const;
-
-  // Lookup the bitmap based on the image URL.  Similar to above,
-  // but used to fetch bitmap where the `result_index` is unknown and where
-  // there are possibly multiple suggestions with the same `keyword` but not the
-  // `image_url` being looked up. Returns nullptr if not found.
-  const SkBitmap* GetPopupRichSuggestionBitmap(const GURL& image_url) const;
-
-  // Lookup the icon bitmap based on the icon URL. Returns nullptr
-  // if not found.
-  const SkBitmap* GetIconBitmap(const GURL& icon_url) const;
-
-  // Stores the image in a local data member and schedules a repaint.
-  void SetPopupRichSuggestionBitmap(int result_index, const SkBitmap& bitmap);
-
-  // Stores the icon in a local data member and schedules a repaint.
-  void SetIconBitmap(const GURL& icon_url, const SkBitmap& bitmap);
-
   // Updates the popup view when the visibility of a group changes.
   void SetPopupSuggestionGroupVisibility(size_t match_index,
                                          bool suggestion_group_hidden);
@@ -513,12 +435,6 @@
   FRIEND_TEST_ALL_PREFIXES(OmniboxEditModelIOSTest,
                            ConsumeCtrlKeyOnRequestFocus);
   FRIEND_TEST_ALL_PREFIXES(OmniboxEditModelIOSTest, ConsumeCtrlKeyOnCtrlAction);
-  FRIEND_TEST_ALL_PREFIXES(
-      OmniboxEditModelIOSPopupTest,
-      GetPopupRichSuggestionBitmapForMatchWithoutAssociatedKeyword);
-  FRIEND_TEST_ALL_PREFIXES(
-      OmniboxEditModelIOSPopupTest,
-      GetPopupRichSuggestionBitmapForMatchWithAssociatedKeyword);
 
   enum PasteState {
     NONE,     // Most recent edit was not a paste.
@@ -643,10 +559,6 @@
   // the view.
   void SetFocusState(OmniboxFocusState state, OmniboxFocusChangeReason reason);
 
-  // This is an event handler that notifies the popup view of match icon
-  // changes.
-  void OnFaviconFetched(const GURL& page_url, const gfx::Image& icon) const;
-
   // Returns view text if there is a view. Until the model is made the
   // primary data source, this should not be called when there's no view.
   std::u16string GetText() const;
@@ -680,12 +592,11 @@
   std::u16string url_for_editing_;
 
   // This flag is true when the user has modified the contents of the edit, but
-  // not yet accepted them.  We use this to determine when we need to save
-  // state (on switching tabs) and whether changes to the page URL should be
-  // immediately displayed.
-  // This flag *should* be true in a superset of the cases where the popup is
-  // open. Except (crbug.com/1340378) for zero suggestions when the popup was
-  // opened with ctrl+L or a mouse click (as opposed to the down arrow).
+  // not yet accepted them.  We use this to determine whether changes to the
+  // page URL should be immediately displayed. This flag *should* be true in a
+  // superset of the cases where the popup is open. Except (crbug.com/1340378)
+  // for zero suggestions when the popup was opened with ctrl+L or a mouse click
+  // (as opposed to the down arrow).
   bool user_input_in_progress_;
 
   // The text that the user has entered.  This does not include inline
@@ -815,18 +726,6 @@
   // `input_` to differ from the one currently stored in AutocompleteController.
   AutocompleteInput input_;
 
-  // Rich suggestion bitmaps for popup keyed by `result_index`. These are
-  // cleared when `OmniboxPopupViewViews` is initialized and destroyed, and on
-  // `OnPopupResultChanged()`.
-  std::map<int, SkBitmap> rich_suggestion_bitmaps_;
-
-  // Icon bitmaps for popup keyed by `icon_url`. These are cleared when
-  // `OmniboxPopupViewViews` is initialized and destroyed. This differs from
-  // `rich_suggestion_bitmaps_` since they are not cleared on
-  // `OnPopupResultChanged()`, which allows for fetching the icon even when the
-  // popup is closed.
-  std::map<GURL, SkBitmap> icon_bitmaps_;
-
   // The popup view is nullptr when there's no popup, and is non-null when
   // a popup view exists (i.e. between calls to `set_popup_view`).
   raw_ptr<OmniboxPopupViewBase> popup_view_ = nullptr;
diff --git a/ios/chrome/browser/omnibox/model/omnibox_edit_model_ios.mm b/ios/chrome/browser/omnibox/model/omnibox_edit_model_ios.mm
index 2c693dd..6292a54 100644
--- a/ios/chrome/browser/omnibox/model/omnibox_edit_model_ios.mm
+++ b/ios/chrome/browser/omnibox/model/omnibox_edit_model_ios.mm
@@ -71,7 +71,6 @@
 #import "third_party/icu/source/common/unicode/ubidi.h"
 #import "third_party/metrics_proto/omnibox_event.pb.h"
 #import "third_party/metrics_proto/omnibox_focus_type.pb.h"
-#import "third_party/skia/include/core/SkBitmap.h"
 #import "ui/base/l10n/l10n_util.h"
 #import "ui/gfx/color_palette.h"
 #import "ui/gfx/geometry/rect.h"
@@ -79,11 +78,6 @@
 #import "url/third_party/mozilla/url_parse.h"
 #import "url/url_util.h"
 
-#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
-#import "ui/gfx/paint_vector_icon.h"
-#import "ui/gfx/vector_icon_types.h"
-#endif
-
 constexpr bool kIsDesktop = !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS);
 
 using bookmarks::BookmarkModel;
@@ -312,31 +306,6 @@
 
 }  // namespace
 
-// OmniboxEditModelIOS::State
-// ----------------------------------------------------
-
-OmniboxEditModelIOS::State::State(
-    bool user_input_in_progress,
-    const std::u16string& user_text,
-    const std::u16string& keyword,
-    const std::u16string& keyword_placeholder,
-    bool is_keyword_hint,
-    OmniboxEventProto::KeywordModeEntryMethod keyword_mode_entry_method,
-    OmniboxFocusState focus_state,
-    const AutocompleteInput& autocomplete_input)
-    : user_input_in_progress(user_input_in_progress),
-      user_text(user_text),
-      keyword(keyword),
-      keyword_placeholder(keyword_placeholder),
-      is_keyword_hint(is_keyword_hint),
-      keyword_mode_entry_method(keyword_mode_entry_method),
-      focus_state(focus_state),
-      autocomplete_input(autocomplete_input) {}
-
-OmniboxEditModelIOS::State::State(const State& other) = default;
-
-OmniboxEditModelIOS::State::~State() = default;
-
 // OmniboxEditModelIOS
 // -----------------------------------------------------------
 
@@ -362,8 +331,6 @@
   popup_view_ = popup_view;
 
   // Clear/reset popup-related state.
-  rich_suggestion_bitmaps_.clear();
-  icon_bitmaps_.clear();
   old_focused_url_ = GURL();
   popup_selection_ = OmniboxPopupSelection(OmniboxPopupSelection::kNoMatch,
                                            OmniboxPopupSelection::NORMAL);
@@ -374,93 +341,6 @@
   return controller_->client()->GetPageClassification(/*is_prefetch=*/false);
 }
 
-OmniboxEditModelIOS::State OmniboxEditModelIOS::GetStateForTabSwitch() const {
-  // NOTE: it's important this doesn't attempt to access any state that
-  // may come from the active WebContents. At the time this is called, the
-  // active WebContents has already changed.
-
-  // Like typing, switching tabs "accepts" the temporary text as the user
-  // text, because it makes little sense to have temporary text when the
-  // popup is closed.
-  std::u16string user_text;
-  if (user_input_in_progress_) {
-    const std::u16string display_text = GetText();
-    if (!MaybePrependKeyword(display_text).empty()) {
-      user_text = display_text;
-    }
-    // Else case is user deleted all the text. The expectation (which matches
-    // other browsers) is when the user restores the state a revert happens as
-    // well as a select all. The revert shouldn't be done here, as at the time
-    // this is called a revert would revert to the url of the newly activated
-    // tab (because at the time this is called, the WebContents has already
-    // changed). By leaving the `user_text` empty downstream code is able to
-    // detect this and select all.
-  } else {
-    user_text = user_text_;
-  }
-  return State(user_input_in_progress_, user_text, keyword_,
-               keyword_placeholder_, is_keyword_hint_,
-               keyword_mode_entry_method_, focus_state_, input_);
-}
-
-void OmniboxEditModelIOS::RestoreState(const State* state) {
-  // We need to update the permanent display texts correctly and revert the
-  // view regardless of whether there is saved state.
-  ResetDisplayTexts();
-
-  if (view_) {
-    view_->RevertAll();
-  }
-  // Restore the autocomplete controller's input, or clear it if this is a new
-  // tab.
-  input_ = state ? state->autocomplete_input : AutocompleteInput();
-  if (!state) {
-    return;
-  }
-
-  // The tab-management system saves the last-focused control for each tab and
-  // restores it. That operation also updates this edit model's focus_state_
-  // if necessary. This occurs before we reach this point in the code.
-  //
-  // The only reason we need to separately save and restore our focus state is
-  // to preserve our special "invisible focus" state used for the fakebox.
-  //
-  // However, in some circumstances (if the last-focused control was destroyed),
-  // the Omnibox will be focused by default, and the edit model's saved state
-  // may be invalid. We make a check to guard against that.
-  bool saved_focus_state_invalid = focus_state_ == OMNIBOX_FOCUS_VISIBLE &&
-                                   state->focus_state == OMNIBOX_FOCUS_NONE;
-  if (!saved_focus_state_invalid) {
-    SetFocusState(state->focus_state, OMNIBOX_FOCUS_CHANGE_TAB_SWITCH);
-  }
-
-  // Restore any user editing.
-  if (state->user_input_in_progress) {
-    // NOTE: Be sure to set keyword-related state AFTER invoking
-    // SetUserText(), as SetUserText() clears the keyword state.
-    if ((!state->user_text.empty() || !state->keyword.empty()) && view_) {
-      view_->SetUserText(state->user_text, false);
-    }
-    SetKeyword(state->keyword);
-    SetKeywordPlaceholder(state->keyword_placeholder);
-    is_keyword_hint_ = state->is_keyword_hint;
-    keyword_mode_entry_method_ = state->keyword_mode_entry_method;
-    if (view_) {
-      view_->OnKeywordPlaceholderTextChange();
-    }
-  } else if (!state->user_text.empty()) {
-    // If the `user_input_in_progress` is false but we have `user_text`,
-    // restore the `user_text` to the model and the view. It's likely unelided
-    // text that the user has not made any modifications to.
-    InternalSetUserText(state->user_text);
-
-    // We let the View manage restoring the cursor position afterwards.
-    if (view_) {
-      view_->SetWindowTextAndCaretPos(state->user_text, 0, false, false);
-    }
-  }
-}
-
 AutocompleteMatch OmniboxEditModelIOS::CurrentMatch(
     GURL* alternate_nav_url) const {
   // If we have a valid match use it. Otherwise get one for the current text.
@@ -678,34 +558,6 @@
   }
 }
 
-bool OmniboxEditModelIOS::ShouldShowCurrentPageIcon() const {
-  // If the popup is open, don't show the current page's icon. The caller is
-  // instead expected to show the current match's icon.
-  if (PopupIsOpen()) {
-    return false;
-  }
-
-  // On the New Tab Page, the omnibox textfield is empty. We want to display
-  // the default search provider favicon instead of the NTP security icon.
-  if (GetText().empty()) {
-    return false;
-  }
-
-  // If user input is not in progress, always show the current page's icon.
-  if (!user_input_in_progress()) {
-    return true;
-  }
-
-  // If user input is in progress, keep showing the current page's icon so long
-  // as the text matches the current page's URL, elided or unelided.
-  return GetText() == display_text_ || GetText() == url_for_editing_;
-}
-
-ui::ImageModel OmniboxEditModelIOS::GetSuperGIcon(int image_size,
-                                                  bool dark_mode) const {
-  return ui::ImageModel();
-}
-
 void OmniboxEditModelIOS::UpdateInput(bool has_selected_text,
                                       bool prevent_inline_autocomplete) {
   bool changed_to_user_input_in_progress = SetInputInProgressNoNotify(true);
@@ -1810,103 +1662,6 @@
   return bookmark_model && bookmark_model->IsBookmarked(match.destination_url);
 }
 
-// Android and iOS have their own platform-specific icon logic.
-#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
-gfx::Image OmniboxEditModelIOS::GetMatchIcon(const AutocompleteMatch& match,
-                                             SkColor vector_icon_color) const {
-  if (!match.icon_url.is_empty()) {
-    const SkBitmap* bitmap = GetIconBitmap(match.icon_url);
-    if (bitmap) {
-      return controller_->client()->GetSizedIcon(bitmap);
-    }
-  }
-
-  gfx::Image extension_icon = GetMatchIconIfExtension(match);
-  if (!extension_icon.IsEmpty()) {
-    return extension_icon;
-  }
-
-  const TemplateURL* turl =
-      match.associated_keyword
-          ? controller_->client()
-                ->GetTemplateURLService()
-                ->GetTemplateURLForKeyword(match.associated_keyword->keyword)
-          : nullptr;
-
-  // Get the favicon for navigational suggestions.
-  //
-  // The starter pack suggestions are a unique case. These suggestions
-  // normally use a favicon image that cannot be styled further by client
-  // code. In order to apply custom styling to the icon (e.g. colors), we ignore
-  // this favicon in favor of using a vector icon which has better styling
-  // support.
-  if (!AutocompleteMatch::IsSearchType(match.type) &&
-      match.type != AutocompleteMatchType::DOCUMENT_SUGGESTION &&
-      match.type != AutocompleteMatchType::HISTORY_CLUSTER &&
-      match.type != AutocompleteMatchType::HISTORY_EMBEDDINGS_ANSWER &&
-      !AutocompleteMatch::IsStarterPackType(match.type)) {
-    // Because the Views UI code calls GetMatchIcon in both the layout and
-    // painting code, we may generate multiple `OnFaviconFetched` callbacks,
-    // all run one after another. This seems to be harmless as the callback
-    // just flips a flag to schedule a repaint. However, if it turns out to be
-    // costly, we can optimize away the redundant extra callbacks.
-    gfx::Image favicon;
-    auto on_icon_fetched =
-        base::BindOnce(&OmniboxEditModelIOS::OnFaviconFetched,
-                       weak_factory_.GetWeakPtr(), match.destination_url);
-    favicon =
-        (turl && AutocompleteMatch::IsFeaturedEnterpriseSearchType(match.type))
-            ? controller_->client()->GetFaviconForKeywordSearchProvider(
-                  turl, std::move(on_icon_fetched))
-            : controller_->client()->GetFaviconForPageUrl(
-                  match.destination_url, std::move(on_icon_fetched));
-
-    // Extension icons are the correct size for non-touch UI but need to be
-    // adjusted to be the correct size for touch mode.
-    if (!favicon.IsEmpty()) {
-      return controller_->client()->GetSizedIcon(favicon);
-    }
-  }
-
-  bool is_starred_match = IsStarredMatch(match);
-  const auto& vector_icon_type = match.GetVectorIcon(is_starred_match, turl);
-
-  return controller_->client()->GetSizedIcon(vector_icon_type,
-                                             vector_icon_color);
-}
-
-gfx::Image OmniboxEditModelIOS::GetMatchIconIfExtension(
-    const AutocompleteMatch& match) const {
-  // Return an empty image if not an extension match.
-  TemplateURLService* service = controller_->client()->GetTemplateURLService();
-  const TemplateURL* template_url = match.GetTemplateURL(service, false);
-  if (!template_url ||
-      template_url->type() != TemplateURL::OMNIBOX_API_EXTENSION) {
-    return gfx::Image();
-  }
-
-  // Return the image specified in the suggestion match if set by looking it up
-  // in the rich suggestions bitmaps. Fall back to the extension icon if empty
-  // or not found.
-  if (match.provider &&
-      match.provider->type() == AutocompleteProvider::TYPE_UNSCOPED_EXTENSION &&
-      !match.ImageUrl().is_empty()) {
-    const SkBitmap* bitmap = GetPopupRichSuggestionBitmap(match.image_url);
-    if (bitmap) {
-      return controller_->client()->GetSizedIcon(bitmap);
-    }
-  }
-
-  gfx::Image extension_icon =
-      controller_->client()->GetExtensionIcon(template_url);
-  // Extension icons are the correct size for non-touch UI but need to be
-  // adjusted to be the correct size for touch mode
-  return extension_icon.IsEmpty()
-             ? extension_icon
-             : controller_->client()->GetSizedIcon(extension_icon);
-}
-#endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
-
 bool OmniboxEditModelIOS::PopupIsOpen() const {
   return popup_view_ && popup_view_->IsOpen();
 }
@@ -2270,7 +2025,6 @@
   if (!popup_view_) {
     return;
   }
-  rich_suggestion_bitmaps_.clear();
   const AutocompleteResult& result = autocomplete_controller()->result();
   size_t old_selected_line = GetPopupSelection().line;
 
@@ -2296,71 +2050,6 @@
   popup_view_->UpdatePopupAppearance();
 }
 
-const SkBitmap* OmniboxEditModelIOS::GetPopupRichSuggestionBitmap(
-    int result_index) const {
-  DCHECK(popup_view_);
-
-  const auto iter = rich_suggestion_bitmaps_.find(result_index);
-  if (iter == rich_suggestion_bitmaps_.end()) {
-    return nullptr;
-  }
-  return &iter->second;
-}
-
-const SkBitmap* OmniboxEditModelIOS::GetPopupRichSuggestionBitmap(
-    const std::u16string& keyword) const {
-  DCHECK(popup_view_);
-
-  auto it = std::ranges::find_if(autocomplete_controller()->result(),
-                                 [&keyword](const AutocompleteMatch& match) {
-                                   return match.associated_keyword &&
-                                          match.associated_keyword->keyword ==
-                                              keyword;
-                                 });
-  return it == autocomplete_controller()->result().end()
-             ? nullptr
-             : GetPopupRichSuggestionBitmap(std::distance(
-                   autocomplete_controller()->result().begin(), it));
-}
-
-const SkBitmap* OmniboxEditModelIOS::GetPopupRichSuggestionBitmap(
-    const GURL& image_url) const {
-  DCHECK(popup_view_);
-  auto iter =
-      std::ranges::find_if(autocomplete_controller()->result(),
-                           [&image_url](const AutocompleteMatch& result_match) {
-                             return (!result_match.ImageUrl().is_empty() &&
-                                     result_match.ImageUrl() == image_url);
-                           });
-  return iter == autocomplete_controller()->result().end()
-             ? nullptr
-             : GetPopupRichSuggestionBitmap(std::distance(
-                   autocomplete_controller()->result().begin(), iter));
-}
-
-const SkBitmap* OmniboxEditModelIOS::GetIconBitmap(const GURL& icon_url) const {
-  DCHECK(popup_view_);
-  auto iter = icon_bitmaps_.find(icon_url);
-  if (iter == icon_bitmaps_.end()) {
-    return nullptr;
-  }
-  return &iter->second;
-}
-
-void OmniboxEditModelIOS::SetPopupRichSuggestionBitmap(int result_index,
-                                                       const SkBitmap& bitmap) {
-  DCHECK(popup_view_);
-  rich_suggestion_bitmaps_[result_index] = bitmap;
-  popup_view_->UpdatePopupAppearance();
-}
-
-void OmniboxEditModelIOS::SetIconBitmap(const GURL& icon_url,
-                                        const SkBitmap& bitmap) {
-  DCHECK(popup_view_ && !icon_url.is_empty());
-  icon_bitmaps_[icon_url] = bitmap;
-  popup_view_->UpdatePopupAppearance();
-}
-
 void OmniboxEditModelIOS::SetPopupSuggestionGroupVisibility(
     size_t match_index,
     bool suggestion_group_hidden) {
@@ -3075,22 +2764,6 @@
   controller_->client()->OnFocusChanged(focus_state_, reason);
 }
 
-void OmniboxEditModelIOS::OnFaviconFetched(const GURL& page_url,
-                                           const gfx::Image& icon) const {
-  if (icon.IsEmpty() || !PopupIsOpen()) {
-    return;
-  }
-
-  // Notify all affected matches.
-  for (size_t i = 0; i < autocomplete_controller()->result().size(); ++i) {
-    auto& match = autocomplete_controller()->result().match_at(i);
-    if (!AutocompleteMatch::IsSearchType(match.type) &&
-        match.destination_url == page_url) {
-      popup_view_->OnMatchIconUpdated(i);
-    }
-  }
-}
-
 std::u16string OmniboxEditModelIOS::GetText() const {
   // Once the model owns primary text, the check for `view_` won't be needed.
   if (view_) {
diff --git a/ios/chrome/browser/omnibox/model/omnibox_edit_model_ios_unittest.mm b/ios/chrome/browser/omnibox/model/omnibox_edit_model_ios_unittest.mm
index 4163e9f9..2876ea0a 100644
--- a/ios/chrome/browser/omnibox/model/omnibox_edit_model_ios_unittest.mm
+++ b/ios/chrome/browser/omnibox/model/omnibox_edit_model_ios_unittest.mm
@@ -42,7 +42,6 @@
 #import "testing/platform_test.h"
 #import "third_party/metrics_proto/omnibox_event.pb.h"
 #import "third_party/omnibox_proto/answer_type.pb.h"
-#import "third_party/skia/include/core/SkBitmap.h"
 #import "ui/base/l10n/l10n_util.h"
 #import "ui/base/window_open_disposition.h"
 
@@ -454,11 +453,6 @@
 
   EXPECT_EQ(u"https://www.example.com/", view()->GetText());
   EXPECT_TRUE(model()->CurrentTextIsURL());
-
-  // We should still show the current page's icon until the URL is modified.
-  EXPECT_TRUE(model()->ShouldShowCurrentPageIcon());
-  view()->SetUserText(u"something else");
-  EXPECT_FALSE(model()->ShouldShowCurrentPageIcon());
 }
 
 TEST_F(OmniboxEditModelIOSTest, UnelideDoesNothingWhenFullURLAlreadyShown) {
@@ -477,24 +471,6 @@
   EXPECT_FALSE(model()->user_input_in_progress());
   EXPECT_FALSE(view()->IsSelectAll());
   EXPECT_TRUE(model()->CurrentTextIsURL());
-  EXPECT_TRUE(model()->ShouldShowCurrentPageIcon());
-}
-
-// The tab-switching system sometimes focuses the Omnibox even if it was not
-// previously focused. In those cases, ignore the saved focus state.
-TEST_F(OmniboxEditModelIOSTest, IgnoreInvalidSavedFocusStates) {
-  // The Omnibox starts out unfocused. Save that state.
-  ASSERT_FALSE(model()->has_focus());
-  OmniboxEditModelIOS::State state = model()->GetStateForTabSwitch();
-  ASSERT_EQ(OMNIBOX_FOCUS_NONE, state.focus_state);
-
-  // Simulate the tab-switching system focusing the Omnibox.
-  model()->OnSetFocus(false);
-
-  // Restoring the old saved state should not clobber the model's focus state.
-  model()->RestoreState(&state);
-  EXPECT_TRUE(model()->has_focus());
-  EXPECT_TRUE(model()->is_caret_visible());
 }
 
 // Tests ConsumeCtrlKey() consumes ctrl key when down, but does not affect ctrl
@@ -585,9 +561,8 @@
 
   model()->OnControlKeyChanged(true);
   model()->OpenSelection();
-  OmniboxEditModelIOS::State state = model()->GetStateForTabSwitch();
   EXPECT_EQ(GURL("http://www.foo.com/"),
-            state.autocomplete_input.canonicalized_url());
+            model()->GetInputForTesting().canonicalized_url());
 }
 
 TEST_F(OmniboxEditModelIOSTest, CtrlEnterNavigatesToDesiredTLDTemporaryText) {
@@ -601,9 +576,8 @@
 
   model()->OnControlKeyChanged(true);
   model()->OpenSelection();
-  OmniboxEditModelIOS::State state = model()->GetStateForTabSwitch();
   EXPECT_EQ(GURL("http://www.foobar.com/"),
-            state.autocomplete_input.canonicalized_url());
+            model()->GetInputForTesting().canonicalized_url());
 }
 
 TEST_F(OmniboxEditModelIOSTest,
@@ -616,9 +590,8 @@
 
   model()->OnControlKeyChanged(true);
   model()->OpenSelection();
-  OmniboxEditModelIOS::State state = model()->GetStateForTabSwitch();
   EXPECT_EQ(GURL("https://www.example.com/"),
-            state.autocomplete_input.canonicalized_url());
+            model()->GetInputForTesting().canonicalized_url());
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -1501,22 +1474,3 @@
                                       8, 1);
 }
 
-// Tests `GetPopupRichSuggestionBitmap()` method, verifying that no bitmap is
-// fetched when there is no match with an `associated_keyword`.
-TEST_F(OmniboxEditModelIOSPopupTest,
-       GetPopupRichSuggestionBitmapForMatchWithoutAssociatedKeyword) {
-  // Setup match with no bitmap.
-  ACMatches matches;
-  AutocompleteMatch match_without_associated_keyword(
-      nullptr, 1000, false, AutocompleteMatchType::URL_WHAT_YOU_TYPED);
-  match_without_associated_keyword.keyword =
-      u"match_without_associated_keyword";
-  matches.push_back(match_without_associated_keyword);
-  AutocompleteResult* result = published_result();
-  result->AppendMatches(matches);
-
-  const SkBitmap* actual_bitmap = model()->GetPopupRichSuggestionBitmap(
-      u"match_without_associated_keyword");
-
-  EXPECT_FALSE(actual_bitmap);
-}
diff --git a/ios/chrome/browser/omnibox/model/omnibox_view_base.h b/ios/chrome/browser/omnibox/model/omnibox_view_base.h
index c332334..a6a1755 100644
--- a/ios/chrome/browser/omnibox/model/omnibox_view_base.h
+++ b/ios/chrome/browser/omnibox/model/omnibox_view_base.h
@@ -28,8 +28,6 @@
 
 class OmniboxViewBase {
  public:
-  using IconFetchedCallback = base::OnceCallback<void(const gfx::Image& icon)>;
-
   // Represents the changes between two State objects.  This is used by the
   // model to determine how its internal state should be updated after the view
   // state changes.  See OmniboxEditModelIOS::OnAfterPossibleChange().
@@ -67,27 +65,6 @@
   // the field is empty.
   bool IsEditingOrEmpty() const;
 
-  // Returns the icon to display as the location icon. If a favicon is
-  // available, `on_icon_fetched` may be called later asynchronously.
-  // `color_current_page_icon` is used for the page icon (i.e. when the popup is
-  // closed, there is no input in progress, and there's a URL displayed) (e.g.
-  // the secure page lock). `color_vectors` is used for vector icons e.g. the
-  // history clock or bookmark star. `color_bright_vectors` is used for special
-  // vector icons e.g. the history cluster squiggle.
-  // `color_vectors_with_background` is used for vector icons that are drawn
-  // atop a background e.g. action suggestions. Favicons aren't custom-colored.
-  // `dark_mode` returns the dark_mode version of an icon. This should usually
-  // be handled by `color_current_page_icon` but in cases where the icon has
-  // hardcoded colors this can be used to return a different icon. E.g., the
-  // SuperGIcon will return different icons in dark and light modes.
-  ui::ImageModel GetIcon(int dip_size,
-                         SkColor color_current_page_icon,
-                         SkColor color_vectors,
-                         SkColor color_bright_vectors,
-                         SkColor color_vectors_with_background,
-                         IconFetchedCallback on_icon_fetched,
-                         bool dark_mode) const;
-
   // The user text is the text the user has manually keyed in.  When present,
   // this is shown in preference to the permanent text; hitting escape will
   // revert to the permanent text.
diff --git a/ios/chrome/browser/omnibox/model/omnibox_view_base.mm b/ios/chrome/browser/omnibox/model/omnibox_view_base.mm
index 447d165..667161e 100644
--- a/ios/chrome/browser/omnibox/model/omnibox_view_base.mm
+++ b/ios/chrome/browser/omnibox/model/omnibox_view_base.mm
@@ -159,22 +159,6 @@
           model()->PopupIsOpen());
 }
 
-// OmniboxViewBase::GetIcon is very similar to
-// OmniboxPopupModel::GetMatchIcon. They contain certain inconsistencies
-// concerning what flags are required to display url favicons and bookmark star
-// icons. OmniboxPopupModel::GetMatchIcon also doesn't display default search
-// provider icons. It's possible they have other inconsistencies as well. We may
-// want to consider reusing the same code for both the popup and omnibox icons.
-ui::ImageModel OmniboxViewBase::GetIcon(int dip_size,
-                                        SkColor color_current_page_icon,
-                                        SkColor color_vectors,
-                                        SkColor color_bright_vectors,
-                                        SkColor color_vectors_with_background,
-                                        IconFetchedCallback on_icon_fetched,
-                                        bool dark_mode) const {
-  NOTREACHED();
-}
-
 void OmniboxViewBase::SetUserText(const std::u16string& text) {
   SetUserText(text, true);
 }
diff --git a/ios/chrome/browser/shared/model/browser/browser_user_data.h b/ios/chrome/browser/shared/model/browser/browser_user_data.h
index a6491dc..4e2c112 100644
--- a/ios/chrome/browser/shared/model/browser/browser_user_data.h
+++ b/ios/chrome/browser/shared/model/browser/browser_user_data.h
@@ -58,6 +58,10 @@
     return &kId;
   }
 
+ protected:
+  BrowserUserData() {}
+  explicit BrowserUserData(Browser* browser) {}
+
  private:
   // Default factory for T that invoke T's constructor. Can be overloaded
   // by sub-class if they want to create a sub-class of T instead.
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/BUILD.gn b/ios/chrome/browser/tab_switcher/ui_bundled/BUILD.gn
index 76a4e2d..b699b4a 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/BUILD.gn
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/BUILD.gn
@@ -27,6 +27,7 @@
   deps = [
     "//base",
     "//components/favicon/ios",
+    "//ios/chrome/browser/favicon/model",
     "//ios/chrome/browser/shared/model/url",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/chrome/browser/shared/ui/symbols",
@@ -76,6 +77,7 @@
     "//base",
     "//components/favicon/ios",
     "//components/tab_groups",
+    "//ios/chrome/browser/favicon/model",
     "//ios/chrome/browser/shared/model/browser",
     "//ios/chrome/browser/shared/model/url",
     "//ios/chrome/browser/shared/model/web_state_list",
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/BUILD.gn b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/BUILD.gn
index 5613059..f091c87 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/BUILD.gn
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/BUILD.gn
@@ -70,9 +70,11 @@
     "//ios/chrome/browser/bookmarks/model",
     "//ios/chrome/browser/bubble/model",
     "//ios/chrome/browser/collaboration/model",
+    "//ios/chrome/browser/collaboration/model:features",
     "//ios/chrome/browser/commerce/model",
     "//ios/chrome/browser/default_browser/model:utils",
     "//ios/chrome/browser/drag_and_drop/model",
+    "//ios/chrome/browser/favicon/model",
     "//ios/chrome/browser/main/model",
     "//ios/chrome/browser/menu/ui_bundled",
     "//ios/chrome/browser/reading_list/model",
@@ -276,6 +278,8 @@
 
 source_set("grid_mediator_item_provider") {
   sources = [ "base_grid_mediator_items_provider.h" ]
+
+  deps = [ "//ios/chrome/browser/tab_switcher/ui_bundled:items" ]
 }
 
 source_set("grid_toolbars_configuration_provider") {
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/base_grid_mediator.mm b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/base_grid_mediator.mm
index 65dd677..ba8e69d 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/base_grid_mediator.mm
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/base_grid_mediator.mm
@@ -2007,4 +2007,9 @@
   return nil;
 }
 
+- (void)fetchTabGroupItemInfo:(TabGroupItem*)tabGroupItem
+                   completion:(GroupTabInfosFetchingCompletionBlock)completion {
+  [tabGroupItem fetchGroupTabInfos:completion faviconLoader:nil];
+}
+
 @end
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/base_grid_mediator_items_provider.h b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/base_grid_mediator_items_provider.h
index 8640ad7..c0b2655 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/base_grid_mediator_items_provider.h
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/base_grid_mediator_items_provider.h
@@ -5,9 +5,10 @@
 #ifndef IOS_CHROME_BROWSER_TAB_SWITCHER_UI_BUNDLED_TAB_GRID_GRID_BASE_GRID_MEDIATOR_ITEMS_PROVIDER_H_
 #define IOS_CHROME_BROWSER_TAB_SWITCHER_UI_BUNDLED_TAB_GRID_GRID_BASE_GRID_MEDIATOR_ITEMS_PROVIDER_H_
 
-@class GridItemIdentifier;
+#import "ios/chrome/browser/tab_switcher/ui_bundled/tab_group_item.h"
 
 @class ActivityLabelData;
+@class GridItemIdentifier;
 namespace web {
 class WebStateID;
 }  // namespace web
@@ -25,6 +26,10 @@
 // Returns the facePile view associated with the `itemID`.
 - (UIView*)facePileViewForItem:(GridItemIdentifier*)itemID;
 
+// Fetches the `tabGroupItem` info and executes the given `completion` block.
+- (void)fetchTabGroupItemInfo:(TabGroupItem*)tabGroupItem
+                   completion:(GroupTabInfosFetchingCompletionBlock)completion;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_TAB_SWITCHER_UI_BUNDLED_TAB_GRID_GRID_BASE_GRID_MEDIATOR_ITEMS_PROVIDER_H_
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/base_grid_mediator_unittest.mm b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/base_grid_mediator_unittest.mm
index 3c9358f..d12f864 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/base_grid_mediator_unittest.mm
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/base_grid_mediator_unittest.mm
@@ -95,10 +95,11 @@
           nullptr, nullptr, nullptr, tab_group_service_);
 
       mediator_ = [[RegularGridMediator alloc]
-            initWithModeHolder:mode_holder_
-           tabGroupSyncService:tab_group_sync_service_.get()
-               shareKitService:share_kit_service_.get()
-              messagingService:nil];
+           initWithModeHolder:mode_holder_
+          tabGroupSyncService:tab_group_sync_service_.get()
+              shareKitService:share_kit_service_.get()
+             messagingService:nil
+                faviconLoader:nil];
     }
     mediator_.consumer = consumer_;
     mediator_.browser = browser_.get();
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/base_grid_view_controller.mm b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/base_grid_view_controller.mm
index 9c30183..e1e91cc 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/base_grid_view_controller.mm
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/base_grid_view_controller.mm
@@ -1682,13 +1682,14 @@
   cell.activityLabelData =
       [self.gridProvider activityLabelDataForItem:groupItemIdentifier];
 
-  [item fetchGroupTabInfos:^(TabGroupItem* innerItem,
-                             NSArray<GroupTabInfo*>* groupTabInfos) {
+  auto completionBlock = ^(TabGroupItem* innerItem,
+                           NSArray<GroupTabInfo*>* groupTabInfos) {
     if ([cell.itemIdentifier.tabGroupItem isEqual:innerItem]) {
       [cell configureWithGroupTabInfos:groupTabInfos
                         totalTabsCount:innerItem.numberOfTabsInGroup];
     }
-  }];
+  };
+  [self.gridProvider fetchTabGroupItemInfo:item completion:completionBlock];
 }
 
 // Configures `cell`'s identifier and title synchronously, and favicon and
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/regular/BUILD.gn b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/regular/BUILD.gn
index 17b0b1b..57a7be3e 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/regular/BUILD.gn
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/regular/BUILD.gn
@@ -19,6 +19,7 @@
     "//ios/chrome/browser/collaboration/model",
     "//ios/chrome/browser/collaboration/model:features",
     "//ios/chrome/browser/collaboration/model/messaging",
+    "//ios/chrome/browser/favicon/model",
     "//ios/chrome/browser/policy/model:policy_util",
     "//ios/chrome/browser/saved_tab_groups/model",
     "//ios/chrome/browser/share_kit/model",
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/regular/regular_grid_coordinator.mm b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/regular/regular_grid_coordinator.mm
index cca5105..894b082 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/regular/regular_grid_coordinator.mm
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/regular/regular_grid_coordinator.mm
@@ -4,7 +4,10 @@
 
 #import "ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/regular/regular_grid_coordinator.h"
 
+#import "ios/chrome/browser/collaboration/model/collaboration_service_factory.h"
+#import "ios/chrome/browser/collaboration/model/features.h"
 #import "ios/chrome/browser/collaboration/model/messaging/messaging_backend_service_factory.h"
+#import "ios/chrome/browser/favicon/model/ios_chrome_favicon_loader_factory.h"
 #import "ios/chrome/browser/policy/model/policy_util.h"
 #import "ios/chrome/browser/saved_tab_groups/model/tab_group_sync_service_factory.h"
 #import "ios/chrome/browser/share_kit/model/share_kit_service_factory.h"
@@ -139,14 +142,23 @@
 
   self.gridViewController = gridViewController;
 
+  FaviconLoader* faviconLoader = nil;
+  collaboration::CollaborationService* collaborationService =
+      collaboration::CollaborationServiceFactory::GetForProfile(profile);
+  // Fetch favicons if shared tab groups is enabled.
+  if (IsSharedTabGroupsJoinEnabled(collaborationService)) {
+    faviconLoader = IOSChromeFaviconLoaderFactory::GetForProfile(profile);
+  }
+
   _mediator = [[RegularGridMediator alloc]
-        initWithModeHolder:self.modeHolder
-       tabGroupSyncService:tab_groups::TabGroupSyncServiceFactory::
-                               GetForProfile(profile)
-           shareKitService:ShareKitServiceFactory::GetForProfile(profile)
-          messagingService:collaboration::messaging::
-                               MessagingBackendServiceFactory::GetForProfile(
-                                   profile)];
+       initWithModeHolder:self.modeHolder
+      tabGroupSyncService:tab_groups::TabGroupSyncServiceFactory::GetForProfile(
+                              profile)
+          shareKitService:ShareKitServiceFactory::GetForProfile(profile)
+         messagingService:collaboration::messaging::
+                              MessagingBackendServiceFactory::GetForProfile(
+                                  profile)
+            faviconLoader:faviconLoader];
   _mediator.consumer = gridViewController;
 
   gridViewController.dragDropHandler = _mediator;
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/regular/regular_grid_mediator.h b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/regular/regular_grid_mediator.h
index 9c399d4..a1c2b74a 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/regular/regular_grid_mediator.h
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/regular/regular_grid_mediator.h
@@ -15,6 +15,7 @@
 class TabGroupSyncService;
 }  // namespace tab_groups
 
+class FaviconLoader;
 class ShareKitService;
 
 // Mediates between model layer and regular grid UI layer.
@@ -27,14 +28,15 @@
 @property(nonatomic, weak) id<GridCommands> inactiveTabsGridCommands;
 
 // Designated initialized. `tabGroupSyncService`, `shareKitService` and
-// `messagingService` can be nullptr.
+// `messagingService`: can be `nullptr`.
+// `faviconLoader`: used to fetch favicons on Google server, can be `nullptr`.
 - (instancetype)
-      initWithModeHolder:(TabGridModeHolder*)modeHolder
-     tabGroupSyncService:(tab_groups::TabGroupSyncService*)tabGroupSyncService
-         shareKitService:(ShareKitService*)shareKitService
-        messagingService:
-            (collaboration::messaging::MessagingBackendService*)messagingService
-    NS_DESIGNATED_INITIALIZER;
+     initWithModeHolder:(TabGridModeHolder*)modeHolder
+    tabGroupSyncService:(tab_groups::TabGroupSyncService*)tabGroupSyncService
+        shareKitService:(ShareKitService*)shareKitService
+       messagingService:
+           (collaboration::messaging::MessagingBackendService*)messagingService
+          faviconLoader:(FaviconLoader*)faviconLoader NS_DESIGNATED_INITIALIZER;
 - (instancetype)initWithModeHolder:(TabGridModeHolder*)modeHolder
     NS_UNAVAILABLE;
 - (instancetype)init NS_UNAVAILABLE;
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/regular/regular_grid_mediator.mm b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/regular/regular_grid_mediator.mm
index dcf62c71..06354fc 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/regular/regular_grid_mediator.mm
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/regular/regular_grid_mediator.mm
@@ -83,17 +83,21 @@
   // The bridge between the C++ MessagingBackendService observer and this
   // Objective-C class.
   std::unique_ptr<MessagingBackendServiceBridge> _messagingBackendServiceBridge;
+  // FaviconLoader used to fetch favicons on Google server.
+  raw_ptr<FaviconLoader> _faviconLoader;
 }
 
 - (instancetype)
-      initWithModeHolder:(TabGridModeHolder*)modeHolder
-     tabGroupSyncService:(tab_groups::TabGroupSyncService*)tabGroupSyncService
-         shareKitService:(ShareKitService*)shareKitService
-        messagingService:(collaboration::messaging::MessagingBackendService*)
-                             messagingService {
+     initWithModeHolder:(TabGridModeHolder*)modeHolder
+    tabGroupSyncService:(tab_groups::TabGroupSyncService*)tabGroupSyncService
+        shareKitService:(ShareKitService*)shareKitService
+       messagingService:
+           (collaboration::messaging::MessagingBackendService*)messagingService
+          faviconLoader:(FaviconLoader*)faviconLoader {
   if ((self = [super initWithModeHolder:modeHolder])) {
     _tabGroupSyncService = tabGroupSyncService;
     _shareKitService = shareKitService;
+    _faviconLoader = faviconLoader;
     _syncServiceObserver =
         std::make_unique<TabGroupSyncServiceObserverBridge>(self);
 
@@ -503,6 +507,11 @@
   return _shareKitService->FacePileView(config);
 }
 
+- (void)fetchTabGroupItemInfo:(TabGroupItem*)tabGroupItem
+                   completion:(GroupTabInfosFetchingCompletionBlock)completion {
+  [tabGroupItem fetchGroupTabInfos:completion faviconLoader:_faviconLoader];
+}
+
 #pragma mark - MessagingBackendServiceObserving
 
 - (void)onMessagingBackendServiceInitialized {
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/regular/regular_grid_mediator_unittest.mm b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/regular/regular_grid_mediator_unittest.mm
index 4f50f54c..d1fd147 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/regular/regular_grid_mediator_unittest.mm
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/regular/regular_grid_mediator_unittest.mm
@@ -68,10 +68,11 @@
         nullptr, nullptr, nullptr, tab_group_service_);
 
     mediator_ = [[TestRegularGridMediator alloc]
-          initWithModeHolder:mode_holder_
-         tabGroupSyncService:tab_group_sync_service_.get()
-             shareKitService:share_kit_service_.get()
-            messagingService:&messaging_backend_];
+         initWithModeHolder:mode_holder_
+        tabGroupSyncService:tab_group_sync_service_.get()
+            shareKitService:share_kit_service_.get()
+           messagingService:&messaging_backend_
+              faviconLoader:nil];
     mediator_.consumer = consumer_;
     mediator_.browser = browser_.get();
     mediator_.toolbarsMutator = fake_toolbars_mediator_;
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/BUILD.gn b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/BUILD.gn
index cff672b..27a17f88 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/BUILD.gn
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/BUILD.gn
@@ -107,6 +107,9 @@
     ":tab_groups_ui",
     "//base",
     "//components/tab_groups",
+    "//ios/chrome/browser/collaboration/model",
+    "//ios/chrome/browser/collaboration/model:features",
+    "//ios/chrome/browser/favicon/model",
     "//ios/chrome/browser/saved_tab_groups/ui:utils",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/browser",
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/create_tab_group_coordinator.mm b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/create_tab_group_coordinator.mm
index af71537..a364b6e 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/create_tab_group_coordinator.mm
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/create_tab_group_coordinator.mm
@@ -9,6 +9,9 @@
 #import "components/sync/base/user_selectable_type.h"
 #import "components/sync/service/sync_service.h"
 #import "components/sync/service/sync_user_settings.h"
+#import "ios/chrome/browser/collaboration/model/collaboration_service_factory.h"
+#import "ios/chrome/browser/collaboration/model/features.h"
+#import "ios/chrome/browser/favicon/model/ios_chrome_favicon_loader_factory.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/model/web_state_list/tab_group.h"
@@ -106,17 +109,27 @@
       [[CreateTabGroupViewController alloc] initWithEditMode:editMode
                                                    tabSynced:tabSynced];
 
+  FaviconLoader* faviconLoader = nil;
+  collaboration::CollaborationService* collaborationService =
+      collaboration::CollaborationServiceFactory::GetForProfile(profile);
+  // Fetch favicons if shared tab groups is enabled.
+  if (IsSharedTabGroupsJoinEnabled(collaborationService)) {
+    faviconLoader = IOSChromeFaviconLoaderFactory::GetForProfile(profile);
+  }
+
   if (_tabGroup) {
     _mediator = [[CreateTabGroupMediator alloc]
         initTabGroupEditionWithConsumer:_viewController
                                tabGroup:_tabGroup
-                           webStateList:browser->GetWebStateList()];
+                           webStateList:browser->GetWebStateList()
+                          faviconLoader:faviconLoader];
     _mediator.delegate = self;
   } else {
     _mediator = [[CreateTabGroupMediator alloc]
         initTabGroupCreationWithConsumer:_viewController
                             selectedTabs:_identifiers
-                                 browser:browser];
+                                 browser:browser
+                           faviconLoader:faviconLoader];
   }
   _viewController.mutator = _mediator;
   _viewController.delegate = self;
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/create_tab_group_mediator.h b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/create_tab_group_mediator.h
index ea20391b..805efacd 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/create_tab_group_mediator.h
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/create_tab_group_mediator.h
@@ -13,6 +13,7 @@
 
 class Browser;
 @protocol CreateTabGroupMediatorDelegate;
+class FaviconLoader;
 class TabGroup;
 @protocol TabGroupCreationConsumer;
 class WebStateList;
@@ -31,19 +32,23 @@
 // - `consumer` the UI that will receive updates.
 // - `identifiers` the list of selected tabs ID
 // - `browser` the browser containing the selected tabs.
+// `faviconLoader`: used to fetch favicons on Google server, can be `nullptr`.
 - (instancetype)
     initTabGroupCreationWithConsumer:(id<TabGroupCreationConsumer>)consumer
                         selectedTabs:(std::set<web::WebStateID>&)identifiers
-                             browser:(Browser*)browser;
+                             browser:(Browser*)browser
+                       faviconLoader:(FaviconLoader*)faviconLoader;
 
 // Init the tab group creation mediator with:
 // - `consumer` the UI that will receive updates.
 // - `tabGroup` the group to edit.
 // - `webStateList` the web state list containing `tabGroup`.
+// `faviconLoader`: used to fetch favicons on Google server, can be `nullptr`.
 - (instancetype)initTabGroupEditionWithConsumer:
                     (id<TabGroupCreationConsumer>)consumer
                                        tabGroup:(const TabGroup*)tabGroup
-                                   webStateList:(WebStateList*)webStateList;
+                                   webStateList:(WebStateList*)webStateList
+                                  faviconLoader:(FaviconLoader*)faviconLoader;
 
 // Disconnects the mediator's dependencies.
 - (void)disconnect;
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/create_tab_group_mediator.mm b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/create_tab_group_mediator.mm
index b050773..eec78ed 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/create_tab_group_mediator.mm
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/create_tab_group_mediator.mm
@@ -13,6 +13,7 @@
 #import "base/scoped_multi_source_observation.h"
 #import "base/strings/sys_string_conversions.h"
 #import "components/tab_groups/tab_group_color.h"
+#import "ios/chrome/browser/favicon/model/favicon_loader.h"
 #import "ios/chrome/browser/saved_tab_groups/ui/tab_group_utils.h"
 #import "ios/chrome/browser/shared/model/browser/browser.h"
 #import "ios/chrome/browser/shared/model/browser/browser_list_factory.h"
@@ -63,7 +64,8 @@
 - (instancetype)
     initTabGroupCreationWithConsumer:(id<TabGroupCreationConsumer>)consumer
                         selectedTabs:(std::set<web::WebStateID>&)identifiers
-                             browser:(Browser*)browser {
+                             browser:(Browser*)browser
+                       faviconLoader:(FaviconLoader*)faviconLoader {
   CHECK(IsTabGroupInGridEnabled())
       << "You should not be able to create a tab group outside the Tab Groups "
          "experiment.";
@@ -114,6 +116,7 @@
       [TabGroupUtils
           fetchTabGroupInfoFromWebState:currentWebStateList->GetWebStateAt(
                                             index)
+                          faviconLoader:faviconLoader
                              completion:^(GroupTabInfo* info) {
                                [weakSelf addInfo:info];
                                [weakSelf updateConsumer];
@@ -127,7 +130,8 @@
 - (instancetype)initTabGroupEditionWithConsumer:
                     (id<TabGroupCreationConsumer>)consumer
                                        tabGroup:(const TabGroup*)tabGroup
-                                   webStateList:(WebStateList*)webStateList {
+                                   webStateList:(WebStateList*)webStateList
+                                  faviconLoader:(FaviconLoader*)faviconLoader {
   CHECK(IsTabGroupInGridEnabled())
       << "You should not be able to create a tab group outside the Tab Groups "
          "experiment.";
@@ -149,11 +153,13 @@
     _groupItem = [[TabGroupItem alloc] initWithTabGroup:_tabGroup
                                            webStateList:_webStateList];
     __weak CreateTabGroupMediator* weakSelf = self;
-    [_groupItem fetchGroupTabInfos:^(TabGroupItem* item,
-                                     NSArray<GroupTabInfo*>* groupTabInfos) {
-      [weakSelf setGroupTabInfos:groupTabInfos];
-      [weakSelf updateConsumer];
-    }];
+    [_groupItem
+        fetchGroupTabInfos:^(TabGroupItem* item,
+                             NSArray<GroupTabInfo*>* groupTabInfos) {
+          [weakSelf setGroupTabInfos:groupTabInfos];
+          [weakSelf updateConsumer];
+        }
+             faviconLoader:faviconLoader];
 
     // Do not use the helper to get the following values as the title helper do
     // not return nil but the number of tabs. In this case, we want nil so it do
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/create_tab_group_mediator_unittest.mm b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/create_tab_group_mediator_unittest.mm
index 309b845..c45353ce 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/create_tab_group_mediator_unittest.mm
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/create_tab_group_mediator_unittest.mm
@@ -91,7 +91,8 @@
   CreateTabGroupMediator* mediator = [[CreateTabGroupMediator alloc]
       initTabGroupEditionWithConsumer:consumer_
                              tabGroup:tab_group
-                         webStateList:&web_state_list_];
+                         webStateList:&web_state_list_
+                        faviconLoader:nil];
   mediator.delegate = delegate_;
   EXPECT_EQ(delegate_.editedGroupWasExternallyMutatedCallCount, 0);
 
@@ -115,7 +116,8 @@
   CreateTabGroupMediator* mediator = [[CreateTabGroupMediator alloc]
       initTabGroupEditionWithConsumer:consumer_
                              tabGroup:tab_group_0
-                         webStateList:&web_state_list_];
+                         webStateList:&web_state_list_
+                        faviconLoader:nil];
   mediator.delegate = delegate_;
   EXPECT_EQ(delegate_.editedGroupWasExternallyMutatedCallCount, 0);
 
@@ -138,7 +140,8 @@
   CreateTabGroupMediator* mediator = [[CreateTabGroupMediator alloc]
       initTabGroupEditionWithConsumer:consumer_
                              tabGroup:tab_group
-                         webStateList:&web_state_list_];
+                         webStateList:&web_state_list_
+                        faviconLoader:nil];
   mediator.delegate = delegate_;
   EXPECT_EQ(delegate_.editedGroupWasExternallyMutatedCallCount, 0);
   tab_groups::TabGroupVisualData visual_data(u"Updated Group Name",
@@ -164,7 +167,8 @@
   CreateTabGroupMediator* mediator = [[CreateTabGroupMediator alloc]
       initTabGroupEditionWithConsumer:consumer_
                              tabGroup:tab_group_0
-                         webStateList:&web_state_list_];
+                         webStateList:&web_state_list_
+                        faviconLoader:nil];
   mediator.delegate = delegate_;
   EXPECT_EQ(delegate_.editedGroupWasExternallyMutatedCallCount, 0);
   tab_groups::TabGroupVisualData visual_data(u"Updated Group Name",
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_group_item.h b/ios/chrome/browser/tab_switcher/ui_bundled/tab_group_item.h
index eac479fd..a26f2d0 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_group_item.h
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_group_item.h
@@ -7,10 +7,10 @@
 
 #import <UIKit/UIKit.h>
 
-@class TabGroupItem;
-
 @class GroupTabInfo;
+@class TabGroupItem;
 #ifdef __cplusplus
+class FaviconLoader;
 class TabGroup;
 class WebStateList;
 #endif
@@ -44,10 +44,14 @@
 @property(nonatomic, readonly) NSInteger numberOfTabsInGroup;
 @property(nonatomic, readonly) BOOL collapsed;
 
+#ifdef __cplusplus
 // Fetches the groupTabInfos (pair of snapshots and favicons), calling
 // `completion` on the calling sequence when the operation completes.
+// `faviconLoader`: used to fetch favicons on Google server, can be `nullptr`.
 - (void)fetchGroupTabInfos:
-    (nonnull GroupTabInfosFetchingCompletionBlock)completion;
+            (GroupTabInfosFetchingCompletionBlock _Nonnull)completion
+             faviconLoader:(FaviconLoader* _Nullable)faviconLoader;
+#endif
 
 @end
 
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_group_item.mm b/ios/chrome/browser/tab_switcher/ui_bundled/tab_group_item.mm
index 1ec1d4f5..29d08a9 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_group_item.mm
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_group_item.mm
@@ -6,6 +6,7 @@
 
 #import "base/memory/raw_ptr.h"
 #import "base/task/sequenced_task_runner.h"
+#import "ios/chrome/browser/favicon/model/favicon_loader.h"
 #import "ios/chrome/browser/shared/model/web_state_list/tab_group.h"
 #import "ios/chrome/browser/shared/model/web_state_list/tab_group_range.h"
 #import "ios/chrome/browser/shared/model/web_state_list/web_state_list.h"
@@ -78,7 +79,8 @@
   return _tabGroup->visual_data().is_collapsed();
 }
 
-- (void)fetchGroupTabInfos:(GroupTabInfosFetchingCompletionBlock)completion {
+- (void)fetchGroupTabInfos:(GroupTabInfosFetchingCompletionBlock)completion
+             faviconLoader:(FaviconLoader*)faviconLoader {
   if (!_tabGroup) {
     __weak TabGroupItem* weakSelf = self;
     base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
@@ -101,6 +103,7 @@
     CHECK(webState);
     __weak TabGroupItem* weakSelf = self;
     [TabGroupUtils fetchTabGroupInfoFromWebState:webState
+                                   faviconLoader:faviconLoader
                                       completion:^(GroupTabInfo* info) {
                                         [weakSelf
                                             groupTabInfoFetched:info
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_group_utils.h b/ios/chrome/browser/tab_switcher/ui_bundled/tab_group_utils.h
index 635c529..c67f7e71 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_group_utils.h
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_group_utils.h
@@ -7,6 +7,7 @@
 
 #import <Foundation/Foundation.h>
 
+class FaviconLoader;
 @class GroupTabInfo;
 namespace web {
 class WebState;
@@ -15,7 +16,10 @@
 @interface TabGroupUtils : NSObject
 
 // Retrieves GroupTabInfo from the given `webState`.
+// `faviconLoader`: used to fetch favicons on Google server, can be `nullptr`.
+// `completion`: the block is executed with the fetched GroupTabInfo.
 + (void)fetchTabGroupInfoFromWebState:(web::WebState*)webState
+                        faviconLoader:(FaviconLoader*)faviconLoader
                            completion:(void (^)(GroupTabInfo*))completion;
 
 @end
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_group_utils.mm b/ios/chrome/browser/tab_switcher/ui_bundled/tab_group_utils.mm
index fdee0822..7c55ff5e 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_group_utils.mm
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_group_utils.mm
@@ -8,6 +8,7 @@
 
 #import "base/memory/weak_ptr.h"
 #import "components/favicon/ios/web_favicon_driver.h"
+#import "ios/chrome/browser/favicon/model/favicon_loader.h"
 #import "ios/chrome/browser/shared/model/url/url_util.h"
 #import "ios/chrome/browser/shared/ui/symbols/symbols.h"
 #import "ios/chrome/browser/snapshots/model/snapshot_tab_helper.h"
@@ -21,24 +22,32 @@
 @implementation TabGroupUtils
 
 + (void)fetchTabGroupInfoFromWebState:(web::WebState*)webState
+                        faviconLoader:(FaviconLoader*)faviconLoader
                            completion:(void (^)(GroupTabInfo*))completion {
   CHECK(webState);
   base::WeakPtr<web::WebState> weakWebState = webState->GetWeakPtr();
   SnapshotTabHelper::FromWebState(webState)->RetrieveColorSnapshot(
       ^(UIImage* snapshot) {
-        GroupTabInfo* info = [[GroupTabInfo alloc] init];
-        info.snapshot = snapshot;
-        info.favicon = [TabGroupUtils faviconFromWebState:weakWebState];
-        completion(info);
+        GroupTabInfo* groupTabInfo = [[GroupTabInfo alloc] init];
+        groupTabInfo.snapshot = snapshot;
+        [TabGroupUtils fetchFaviconFromWebState:weakWebState
+                                   groupTabInfo:groupTabInfo
+                                  faviconLoader:faviconLoader
+                                     completion:completion];
       });
 }
 
 #pragma mark - Private helpers
 
-// Returns the favicon for the given `webState` or nil otherwise.
-+ (UIImage*)faviconFromWebState:(base::WeakPtr<web::WebState>)webState {
+// Fetches the favicon for the given `webState` and executes the `completion`
+// block.
++ (void)fetchFaviconFromWebState:(base::WeakPtr<web::WebState>)webState
+                    groupTabInfo:(GroupTabInfo*)groupTabInfo
+                   faviconLoader:(FaviconLoader*)faviconLoader
+                      completion:(void (^)(GroupTabInfo*))completion {
   if (!webState) {
-    return nil;
+    completion(groupTabInfo);
+    return;
   }
 
   UIImageConfiguration* configuration = [UIImageSymbolConfiguration
@@ -47,7 +56,10 @@
                            scale:UIImageSymbolScaleMedium];
 
   if (IsUrlNtp(webState->GetVisibleURL())) {
-    return CustomSymbolWithConfiguration(kChromeProductSymbol, configuration);
+    groupTabInfo.favicon =
+        CustomSymbolWithConfiguration(kChromeProductSymbol, configuration);
+    completion(groupTabInfo);
+    return;
   }
 
   // Use the page favicon.
@@ -57,12 +69,21 @@
   if (faviconDriver) {
     gfx::Image favicon = faviconDriver->GetFavicon();
     if (!favicon.IsEmpty()) {
-      return favicon.ToUIImage();
+      groupTabInfo.favicon = favicon.ToUIImage();
+      completion(groupTabInfo);
+      return;
     }
   }
 
-  // Return the default favicon.
-  return DefaultSymbolWithConfiguration(kGlobeAmericasSymbol, configuration);
+  // Use the default favicon.
+  groupTabInfo.favicon =
+      DefaultSymbolWithConfiguration(kGlobeAmericasSymbol, configuration);
+  if (!faviconLoader) {
+    completion(groupTabInfo);
+    return;
+  }
+  // TODO(crbug.com/400966281): Fetch favicon on Google server.
+  completion(groupTabInfo);
 }
 
 @end
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_strip/ui/tab_strip_tab_cell.mm b/ios/chrome/browser/tab_switcher/ui_bundled/tab_strip/ui/tab_strip_tab_cell.mm
index 0ed31b70..d084203 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_strip/ui/tab_strip_tab_cell.mm
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_strip/ui/tab_strip_tab_cell.mm
@@ -238,10 +238,23 @@
       NSArray<UITrait>* traits = TraitCollectionSetForTraits(nil);
       [self registerForTraitChanges:traits withAction:@selector(updateColors)];
     }
+
+    [[NSNotificationCenter defaultCenter]
+        addObserver:self
+           selector:@selector(handleFocusUpdate:)
+               name:UIFocusDidUpdateNotification
+             object:nil];
   }
   return self;
 }
 
+- (void)dealloc {
+  [[NSNotificationCenter defaultCenter]
+      removeObserver:self
+                name:UIFocusDidUpdateNotification
+              object:nil];
+}
+
 - (void)setFaviconImage:(UIImage*)image {
   if (!image) {
     _faviconView.image = DefaultFavicon();
@@ -593,9 +606,11 @@
 // Updates view colors.
 - (void)updateColors {
   BOOL isSelected = self.isSelected;
-
-  if (self.isHighlighted || self.configurationState.cellDragState !=
-                                UICellConfigurationDragStateNone) {
+  if (self.focused) {
+    _selectedBackground.backgroundColor = [UIColor clearColor];
+    _accessibilityContainerView.backgroundColor = [UIColor clearColor];
+  } else if (self.highlighted || self.configurationState.cellDragState !=
+                                     UICellConfigurationDragStateNone) {
     // Before a cell is dragged, it is highlighted.
     // The cell's background color must be updated at this moment, otherwise it
     // will not be applied correctly.
@@ -1232,4 +1247,13 @@
   _blueDotView.hidden = YES;
 }
 
+- (void)handleFocusUpdate:(NSNotification*)notification {
+  UIFocusUpdateContext* context =
+      notification.userInfo[UIFocusUpdateContextKey];
+  if (context.nextFocusedView == self ||
+      context.previouslyFocusedView == self) {
+    [self updateColors];
+  }
+}
+
 @end
diff --git a/ios/chrome/browser/tips_notifications/model/tips_notification_client_unittest.mm b/ios/chrome/browser/tips_notifications/model/tips_notification_client_unittest.mm
index 97ee06d..3a0050c 100644
--- a/ios/chrome/browser/tips_notifications/model/tips_notification_client_unittest.mm
+++ b/ios/chrome/browser/tips_notifications/model/tips_notification_client_unittest.mm
@@ -757,4 +757,16 @@
   EXPECT_EQ(order[0], TipsNotificationType::kEnhancedSafeBrowsing);
   EXPECT_EQ(order[1], TipsNotificationType::kLens);
   EXPECT_EQ(order[2], TipsNotificationType::kWhatsNew);
+
+  // Test Expanded Tips order param.
+  feature_list.Reset();
+  feature_list.InitAndEnableFeatureWithParameters(
+      kIOSExpandedTips, {
+                            {kIOSExpandedTipsOrderParam, "4,5,6"},
+                        });
+  order = TipsNotificationsTypesOrder(false);
+  EXPECT_EQ(order.size(), 3u);
+  EXPECT_EQ(order[0], TipsNotificationType::kSetUpListContinuation);
+  EXPECT_EQ(order[1], TipsNotificationType::kDocking);
+  EXPECT_EQ(order[2], TipsNotificationType::kOmniboxPosition);
 }
diff --git a/ios/chrome/browser/tips_notifications/model/utils.mm b/ios/chrome/browser/tips_notifications/model/utils.mm
index 4577e25..c6cecee 100644
--- a/ios/chrome/browser/tips_notifications/model/utils.mm
+++ b/ios/chrome/browser/tips_notifications/model/utils.mm
@@ -205,6 +205,19 @@
             TipsNotificationType::kEnhancedSafeBrowsing,
             TipsNotificationType::kWhatsNew,
         });
+  } else if (IsIOSExpandedTipsEnabled()) {
+    return GetFieldTrialParamByFeatureAsVector<TipsNotificationType>(
+        kIOSExpandedTips, kIOSExpandedTipsOrderParam,
+        {
+            TipsNotificationType::kEnhancedSafeBrowsing,
+            TipsNotificationType::kWhatsNew,
+            TipsNotificationType::kLens,
+            TipsNotificationType::kOmniboxPosition,
+            TipsNotificationType::kSetUpListContinuation,
+            TipsNotificationType::kDefaultBrowser,
+            TipsNotificationType::kDocking,
+            TipsNotificationType::kSignin,
+        });
   }
   return {
       TipsNotificationType::kEnhancedSafeBrowsing,
diff --git a/ios/chrome/browser/variations/model/variations_app_interface.mm b/ios/chrome/browser/variations/model/variations_app_interface.mm
index 07158bfa..a6fc001 100644
--- a/ios/chrome/browser/variations/model/variations_app_interface.mm
+++ b/ios/chrome/browser/variations/model/variations_app_interface.mm
@@ -76,9 +76,11 @@
       ->GetVariationsService()
       ->GetSeedStoreForTesting()
       ->GetSafeSeedReaderWriterForTesting()
-      ->StoreValidatedSeedInfo(variations::kTestSeedData.GetCompressedData(),
-                               variations::kTestSeedData.base64_compressed_data,
-                               variations::kTestSeedData.base64_signature);
+      ->StoreValidatedSeedInfo(variations::ValidatedSeedInfo{
+          .compressed_seed_data = variations::kTestSeedData.GetCompressedData(),
+          .base64_seed_data = variations::kTestSeedData.base64_compressed_data,
+          .signature = variations::kTestSeedData.base64_signature,
+          .milestone = 92});
 }
 
 + (void)setCrashingRegularSeedAndSignature {
@@ -86,10 +88,13 @@
       ->GetVariationsService()
       ->GetSeedStoreForTesting()
       ->GetSeedReaderWriterForTesting()
-      ->StoreValidatedSeedInfo(
-          variations::kCrashingSeedData.GetCompressedData(),
-          variations::kCrashingSeedData.base64_compressed_data,
-          variations::kCrashingSeedData.base64_signature);
+      ->StoreValidatedSeedInfo(variations::ValidatedSeedInfo{
+          .compressed_seed_data =
+              variations::kCrashingSeedData.GetCompressedData(),
+          .base64_seed_data =
+              variations::kCrashingSeedData.base64_compressed_data,
+          .signature = variations::kCrashingSeedData.base64_signature,
+          .milestone = 92});
 }
 
 + (int)crashStreak {
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 f51b5ae8..e2c432fc 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 @@
-6959c752079f4f6cef33f25136f2d0d25eee9d61
\ No newline at end of file
+95e6978b4d65d1d36670da01f67a10f1f48f67e0
\ 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 504a21b..23389b1e 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 @@
-5666b7d57650eba7f436186c99ca28127042f0ca
\ No newline at end of file
+982ed659e3dd4c25f32f0d86ba9fbfe102c3dea7
\ 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 ae4dae9..2a68a49 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 @@
-0a8e945e34aedcf11271e1a28f1a8a5e05bea827
\ No newline at end of file
+f2a63289d3a9e3b4866ed0902ba1b0bfe33c35ba
\ 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 6f043ae..1fa9c6e 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 @@
-26eaf1e0bef0b21ee2fc036e0e7317544a7b269b
\ No newline at end of file
+e8956ed1fc5f152f204b51cb58ebbfbc3c55c78f
\ 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 871ab9e..03e294e4 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 @@
-b13b6d2c45bde9258ede90a23c4001691af0cf20
\ No newline at end of file
+aa56f949981c08348e424c79f73d55639ddadce3
\ 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 dd49c6f..6c034f2d 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 @@
-4ca4da2459239335296e9a95540621f1a9191936
\ No newline at end of file
+4cf4f502f20d97ba754441ef01af032c7a7b19c5
\ 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 3465cc9b..67ddf1e 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 @@
-103c193727cfd5efc75cf0b84d4e6592dc36ec68
\ No newline at end of file
+4436d4d9bdf8d814ae9cf7c8fbc8d0a1d1428cb8
\ 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 ebf0a402..d543414 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 @@
-0deeb7c46f4ca9c7f6217dedd9b4ecae2b930e59
\ No newline at end of file
+4a8bdedb83b8e2f33830a5c8f5b07d5b03e02cf2
\ 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 51c5a25..79fded4c 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 @@
-db3fa543aa392ad9ff64a5c2e6829152ed33006c
\ No newline at end of file
+66c5368c4b087ae4496bd5c2d54f1486cf91d7cb
\ 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 7884d82..084377f 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 @@
-04ab3945b99c3323fb097f3e0d222fa13fca742b
\ No newline at end of file
+a7df80cf9e95f53cc7412f0cd2b30545ac0bfe15
\ 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 2269a1f..858166b1 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 @@
-facd40adbb4b0b28426450ab6b9ace455c7a8e0d
\ No newline at end of file
+2765ec0306e371794906484249320b88e45f2606
\ 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 43ae9eda..ea44ffe 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 @@
-b46874a15f4c7fce0ca682588b3578446515698d
\ No newline at end of file
+270e580cbf56f9d7c8eef30a39c5c344641060dd
\ 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 cdd9e7ec..4e0bc07 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 @@
-21898bc0d6597fa2719a6cdbc58a193e90e721e9
\ No newline at end of file
+e16730370c818c8cc6fed42b8c0b76cb99d92c72
\ 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 62cb5d1..755f0f0 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 @@
-4bfe81b57c04eef43ed6a7997ad2673830e73eeb
\ No newline at end of file
+e3ef9ff77206196672ba1aaca43052f7a5732043
\ 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 8a447af..58a94585 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 @@
-0b1eeee752bcc5b98e7ba2563bc8fdb0c571c760
\ No newline at end of file
+3e06f7e8f6da9a67816cda40a7108a02d0e8cca2
\ 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 cb21251..c470ff9 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 @@
-e6633213b3d3839edf6d45992b56761f02f498a8
\ No newline at end of file
+9cd5b03798009c9fb57c8e7ea4b176a0523fbef4
\ 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 da663f75..0847b31a 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 @@
-22009072ad9e8379af1d7fd79ab84b7084b44aaf
\ No newline at end of file
+a18858a586f02ea18466f98d2c4a44f608d114c1
\ 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 2ef1594..be7ffdc 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 @@
-257423a14b0f6851fb0c104a67b79db0886c404a
\ No newline at end of file
+ed042fbd62fdfe4c8eabbd9fa74818ad0da3f909
\ 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 19b97d5..c43b7c6 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 @@
-494c692aebe7745bc23a902031f8bb92975d6741
\ No newline at end of file
+b6ad5a943a47834d75f2987e641b6decbf04f34e
\ 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 4252b8b..e7b3b25 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 @@
-7a1f0275cbc88939ba5c6642de252eedd5f5887a
\ No newline at end of file
+be77c08b55b4dc65f1b80b2848cafcdf1d4e2755
\ 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 8182e7171..feb1647d 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 @@
-cd3a6877c4f343ef611d9e2a9f4c38d4a482884f
\ No newline at end of file
+0f000f849843c189d82a71d3d490629b127b184c
\ 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 1f72688c..3ddfbfb 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 @@
-7689b7c4761cb0730f5ef6648234518fa4bf3f29
\ No newline at end of file
+81a9ae657c8b476a6c4a58f38c7aa446faf85d74
\ 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 fa469bd..b7b7719 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 @@
-2d8d1ef3d0445fb5b7d3d3f41902d0fe8c770328
\ No newline at end of file
+c8751ab6cebf149cf901a07dd461eeb5685bccb8
\ 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 7c6143d..5b520ac 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 @@
-9c9531d8d277dcacde9414f233df1dde21fc6a2c
\ No newline at end of file
+89c8e1967608d6960879fefd092eec22925d422b
\ 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 5c2a0025..4f27820 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 @@
-ba228c9cf8442590ec951ac6558187420865b73d
\ No newline at end of file
+766d5cbbe8b80bd71b90d3a8fac1e07a25d1eab0
\ 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 ea3fb248..5542d7e 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 @@
-f7ca0b352c2637c33e7bc9c7717ff27ca26793ff
\ No newline at end of file
+602c8a893511f9d23a754a71ad71b915a3d5d578
\ 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 9a2395e..fd9761a3 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 @@
-244c92fe0d1533c739d3b555c631da10a2cd7c1c
\ No newline at end of file
+2976328b9622721acd4337b186c38cea392ddbc3
\ 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 90a2a225..76e254ac 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 @@
-e411885d8b7d97378b8f5471048c27f8be165004
\ No newline at end of file
+4fda076ca60e25c1d474e7d8f18f6dfd4f92d15e
\ No newline at end of file
diff --git a/ios/web_view/public/cwv_export.h b/ios/web_view/public/cwv_export.h
index 2c9c6591..ca0e813 100644
--- a/ios/web_view/public/cwv_export.h
+++ b/ios/web_view/public/cwv_export.h
@@ -15,10 +15,6 @@
 // This makes it possible to export symbols only when *building* the dynamic
 // library (by checking CWV_IMPLEMENTATION), not when *using* the dynamic
 // library.
-#if defined(CWV_IMPLEMENTATION)
 #define CWV_EXPORT __attribute__((visibility("default")))
-#else
-#define CWV_EXPORT
-#endif
 
 #endif  // IOS_WEB_VIEW_PUBLIC_CWV_EXPORT_H_
diff --git a/ios_internal b/ios_internal
index 56c2b01..c32f192 160000
--- a/ios_internal
+++ b/ios_internal
@@ -1 +1 @@
-Subproject commit 56c2b01d07aad9c323ad655a8cb07b849923ac99
+Subproject commit c32f19290092f9592d3be32943becfab3f52a9cd
diff --git a/ipc/ipc_message_support_export.h b/ipc/ipc_message_support_export.h
index d74b52e..0b45100 100644
--- a/ipc/ipc_message_support_export.h
+++ b/ipc/ipc_message_support_export.h
@@ -16,11 +16,7 @@
 
 #else  // defined(WIN32)
 
-#if defined(IPC_MESSAGE_SUPPORT_IMPL)
 #define IPC_MESSAGE_SUPPORT_EXPORT __attribute__((visibility("default")))
-#else
-#define IPC_MESSAGE_SUPPORT_EXPORT
-#endif
 
 #endif
 
diff --git a/media/audio/BUILD.gn b/media/audio/BUILD.gn
index 8bcbb66..4f663e2 100644
--- a/media/audio/BUILD.gn
+++ b/media/audio/BUILD.gn
@@ -253,6 +253,8 @@
       "android/aaudio_output.h",
       "android/aaudio_stream_wrapper.cc",
       "android/aaudio_stream_wrapper.h",
+      "android/audio_device_id.cc",
+      "android/audio_device_id.h",
       "android/audio_manager_android.cc",
       "android/audio_manager_android.h",
       "android/audio_track_output_stream.cc",
@@ -454,7 +456,10 @@
   ]
 
   if (is_android) {
-    sources += [ "android/audio_android_unittest.cc" ]
+    sources += [
+      "android/audio_android_unittest.cc",
+      "android/audio_device_id_unittest.cc",
+    ]
     deps += [
       "//media/gpu:gpu",
       "//ui/gl",
diff --git a/media/audio/android/aaudio_input.cc b/media/audio/android/aaudio_input.cc
index b739ec4..13aae7c 100644
--- a/media/audio/android/aaudio_input.cc
+++ b/media/audio/android/aaudio_input.cc
@@ -5,6 +5,7 @@
 #include "media/audio/android/aaudio_input.h"
 
 #include "base/task/bind_post_task.h"
+#include "media/audio/android/audio_device_id.h"
 #include "media/audio/android/audio_manager_android.h"
 #include "media/base/amplitude_peak_detector.h"
 #include "media/base/audio_bus.h"
@@ -12,9 +13,11 @@
 namespace media {
 
 AAudioInputStream::AAudioInputStream(AudioManagerAndroid* manager,
-                                     const AudioParameters& params)
+                                     const AudioParameters& params,
+                                     android::AudioDeviceId device_id)
     : audio_manager_(manager),
       params_(params),
+      device_id_(std::move(device_id)),
       peak_detector_(base::BindRepeating(&AudioManager::TraceAmplitudePeak,
                                          base::Unretained(audio_manager_),
                                          /*trace_start=*/true)) {
@@ -30,7 +33,7 @@
 void AAudioInputStream::CreateStreamWrapper() {
   CHECK(!stream_wrapper_);
   stream_wrapper_ = std::make_unique<AAudioStreamWrapper>(
-      this, AAudioStreamWrapper::StreamType::kInput, params_,
+      this, AAudioStreamWrapper::StreamType::kInput, params_, device_id_,
       AAUDIO_USAGE_VOICE_COMMUNICATION);
 }
 
diff --git a/media/audio/android/aaudio_input.h b/media/audio/android/aaudio_input.h
index cecbac9..bcb4a67 100644
--- a/media/audio/android/aaudio_input.h
+++ b/media/audio/android/aaudio_input.h
@@ -13,6 +13,7 @@
 #include "base/sequence_checker.h"
 #include "base/synchronization/lock.h"
 #include "media/audio/android/aaudio_stream_wrapper.h"
+#include "media/audio/android/audio_device_id.h"
 #include "media/audio/audio_io.h"
 #include "media/base/amplitude_peak_detector.h"
 #include "media/base/audio_parameters.h"
@@ -27,7 +28,8 @@
       public AAudioStreamWrapper::DataCallback {
  public:
   AAudioInputStream(AudioManagerAndroid* manager,
-                    const AudioParameters& params);
+                    const AudioParameters& params,
+                    android::AudioDeviceId device_id);
 
   AAudioInputStream(const AAudioInputStream&) = delete;
   AAudioInputStream& operator=(const AAudioInputStream&) = delete;
@@ -60,6 +62,7 @@
 
   const raw_ptr<AudioManagerAndroid> audio_manager_;
   const AudioParameters params_;
+  const android::AudioDeviceId device_id_;
 
   AmplitudePeakDetector peak_detector_;
 
diff --git a/media/audio/android/aaudio_output.cc b/media/audio/android/aaudio_output.cc
index f52efd6..5c28085c 100644
--- a/media/audio/android/aaudio_output.cc
+++ b/media/audio/android/aaudio_output.cc
@@ -8,6 +8,7 @@
 #include "base/memory/raw_ptr.h"
 #include "base/numerics/checked_math.h"
 #include "base/task/sequenced_task_runner.h"
+#include "media/audio/android/audio_device_id.h"
 #include "media/audio/android/audio_manager_android.h"
 #include "media/audio/audio_manager.h"
 #include "media/base/amplitude_peak_detector.h"
@@ -17,6 +18,7 @@
 
 AAudioOutputStream::AAudioOutputStream(AudioManagerAndroid* manager,
                                        const AudioParameters& params,
+                                       android::AudioDeviceId device_id,
                                        aaudio_usage_t usage)
     : audio_manager_(manager),
       params_(params),
@@ -26,6 +28,7 @@
       stream_wrapper_(this,
                       AAudioStreamWrapper::StreamType::kOutput,
                       params,
+                      std::move(device_id),
                       usage) {
   CHECK(manager);
   CHECK(params_.IsValid());
diff --git a/media/audio/android/aaudio_output.h b/media/audio/android/aaudio_output.h
index 230d0e5f..0d5805b 100644
--- a/media/audio/android/aaudio_output.h
+++ b/media/audio/android/aaudio_output.h
@@ -13,6 +13,7 @@
 #include "base/synchronization/lock.h"
 #include "base/thread_annotations.h"
 #include "media/audio/android/aaudio_stream_wrapper.h"
+#include "media/audio/android/audio_device_id.h"
 #include "media/audio/android/muteable_audio_output_stream.h"
 #include "media/base/amplitude_peak_detector.h"
 #include "media/base/audio_parameters.h"
@@ -28,6 +29,7 @@
  public:
   AAudioOutputStream(AudioManagerAndroid* manager,
                      const AudioParameters& params,
+                     android::AudioDeviceId device_id,
                      aaudio_usage_t usage);
 
   AAudioOutputStream(const AAudioOutputStream&) = delete;
diff --git a/media/audio/android/aaudio_stream_wrapper.cc b/media/audio/android/aaudio_stream_wrapper.cc
index 875c8b6..7941adec 100644
--- a/media/audio/android/aaudio_stream_wrapper.cc
+++ b/media/audio/android/aaudio_stream_wrapper.cc
@@ -9,11 +9,14 @@
 
 #include "media/audio/android/aaudio_stream_wrapper.h"
 
+#include <aaudio/AAudio.h>
+
 #include "base/logging.h"
 #include "base/memory/raw_ptr.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/thread_annotations.h"
 #include "base/trace_event/trace_event.h"
+#include "media/audio/android/audio_device_id.h"
 #include "media/base/audio_parameters.h"
 #include "media/base/channel_layout.h"
 
@@ -158,8 +161,10 @@
 AAudioStreamWrapper::AAudioStreamWrapper(DataCallback* callback,
                                          StreamType stream_type,
                                          const AudioParameters& params,
+                                         android::AudioDeviceId device_id,
                                          aaudio_usage_t usage)
     : params_(params),
+      device_id_(std::move(device_id)),
       stream_type_(stream_type),
       usage_(usage),
       callback_(callback),
@@ -236,6 +241,7 @@
   AAudioStreamBuilder_setPerformanceMode(builder, performance_mode_);
   AAudioStreamBuilder_setFramesPerDataCallback(builder,
                                                params_.frames_per_buffer());
+  AAudioStreamBuilder_setDeviceId(builder, device_id_.ToAAudioDeviceId());
 
   if (__builtin_available(android AAUDIO_CHANNEL_MASK_MIN_API, *)) {
     SetChannelMask(builder, params_);
@@ -276,6 +282,18 @@
 
   CHECK_EQ(AAUDIO_FORMAT_PCM_FLOAT, AAudioStream_getFormat(aaudio_stream_));
 
+  if (!device_id_.IsDefault()) {
+    // `AAudioStreamBuilder_setDeviceId` is not guaranteed to set the specified
+    // device.
+    const int32_t expected_device_id = device_id_.ToAAudioDeviceId();
+    const int32_t actual_device_id = AAudioStream_getDeviceId(aaudio_stream_);
+    if (expected_device_id != actual_device_id) {
+      DLOG(WARNING) << "Failed to set device ID for AAudio stream. Expected: "
+                    << expected_device_id << "; actual: " << actual_device_id;
+      return false;
+    }
+  }
+
   // After opening the stream, sets the effective buffer size to 3X the burst
   // size to prevent glitching if the burst is small (e.g. < 128). On some
   // devices you can get by with 1X or 2X, but 3X is safer.
diff --git a/media/audio/android/aaudio_stream_wrapper.h b/media/audio/android/aaudio_stream_wrapper.h
index db1e871f..3b8c556 100644
--- a/media/audio/android/aaudio_stream_wrapper.h
+++ b/media/audio/android/aaudio_stream_wrapper.h
@@ -12,6 +12,7 @@
 #include "base/memory/raw_ptr_exclusion.h"
 #include "base/sequence_checker.h"
 #include "base/synchronization/lock.h"
+#include "media/audio/android/audio_device_id.h"
 #include "media/base/audio_bus.h"
 #include "media/base/audio_parameters.h"
 
@@ -48,6 +49,7 @@
   AAudioStreamWrapper(DataCallback* callback,
                       StreamType stream_type,
                       const AudioParameters& params,
+                      android::AudioDeviceId device_id,
                       aaudio_usage_t usage);
 
   AAudioStreamWrapper(const AAudioStreamWrapper&) = delete;
@@ -78,6 +80,7 @@
   SEQUENCE_CHECKER(sequence_checker_);
 
   const AudioParameters params_;
+  const android::AudioDeviceId device_id_;
 
   // Whether this class is using an input or an output stream.
   StreamType stream_type_;
diff --git a/media/audio/android/audio_android_unittest.cc b/media/audio/android/audio_android_unittest.cc
index 550a2b7b..df761d9c 100644
--- a/media/audio/android/audio_android_unittest.cc
+++ b/media/audio/android/audio_android_unittest.cc
@@ -163,12 +163,19 @@
   return os;
 }
 
-enum class AudioApi { AAudio, OpenSLES };
+enum class AudioApi {
+  AAudioWithPerStreamDeviceSelection,
+  AAudioWithCommunicationDevices,
+  OpenSLES
+};
 
 std::ostream& operator<<(std::ostream& os, const AudioApi& audio_api) {
   switch (audio_api) {
-    case AudioApi::AAudio:
-      os << "AAudio";
+    case AudioApi::AAudioWithPerStreamDeviceSelection:
+      os << "AAudioWithPerStreamDeviceSelection";
+      break;
+    case AudioApi::AAudioWithCommunicationDevices:
+      os << "AAudioWithCommunicationDevices";
       break;
     case AudioApi::OpenSLES:
       os << "OpenSLES";
@@ -488,22 +495,37 @@
   }
 
   void InitFeatures(AudioApi audio_api) {
-    const std::vector<base::test::FeatureRef> aaudio_features = {
-        features::kUseAAudioDriver, features::kUseAAudioInput};
+    bool enable_aaudio = false;
+    bool enable_aaudio_per_stream_device_selection = false;
     switch (audio_api) {
-      case AudioApi::AAudio:
-        if (!__builtin_available(android AAUDIO_MIN_API, *)) {
-          GTEST_SKIP() << "AAudio is not available.";
-        }
-        feature_list_.InitWithFeatures(aaudio_features, {});
+      case AudioApi::AAudioWithPerStreamDeviceSelection:
+        enable_aaudio = true;
+        enable_aaudio_per_stream_device_selection = true;
+        break;
+      case AudioApi::AAudioWithCommunicationDevices:
+        enable_aaudio = true;
         break;
       case AudioApi::OpenSLES:
-#if !BUILDFLAG(USE_OPENSLES)
-        GTEST_SKIP() << "OpenSLES is disabled.";
-#endif
-        feature_list_.InitWithFeatures({}, aaudio_features);
         break;
     }
+
+    if (enable_aaudio) {
+      if (!__builtin_available(android AAUDIO_MIN_API, *)) {
+        GTEST_SKIP() << "AAudio is not available.";
+      }
+    } else {
+      // Use OpenSL ES fallback
+#if !BUILDFLAG(USE_OPENSLES)
+      GTEST_SKIP() << "OpenSLES is not available.";
+#endif
+    }
+
+    base::flat_map<base::test::FeatureRef, bool> feature_states(
+        {{features::kUseAAudioDriver, enable_aaudio},
+         {features::kUseAAudioInput, enable_aaudio},
+         {features::kAAudioPerStreamDeviceSelection,
+          enable_aaudio_per_stream_device_selection}});
+    feature_list_.InitWithFeatureStates(feature_states);
   }
 
   // Synchronously runs the provided callback/closure on the audio thread.
@@ -551,9 +573,11 @@
     return devices;
   }
 
-  void MakeAudioOutputStreamOnAudioThread(const AudioParameters& params) {
+  void MakeAudioOutputStreamOnAudioThread(
+      const AudioParameters& params,
+      const std::string& device_id = AudioDeviceDescription::kDefaultDeviceId) {
     RunOnAudioThread(base::BindOnce(&AudioAndroidOutputTest::MakeOutputStream,
-                                    base::Unretained(this), params));
+                                    base::Unretained(this), params, device_id));
   }
 
   void CloseAudioOutputStreamOnAudioThread(raw_ptr<AudioOutputStream> stream) {
@@ -623,10 +647,17 @@
               2 * expected_time_between_callbacks_ms);
   }
 
-  void MakeOutputStream(const AudioParameters& params) {
+  std::optional<AudioDeviceDescription> GetFirstNonDefaultOutputDevice() {
+    AudioDeviceDescriptions devices =
+        GetAudioOutputDeviceDescriptionsOnAudioThread();
+    return GetFirstNonDefaultDeviceFromDescriptions(devices);
+  }
+
+  void MakeOutputStream(const AudioParameters& params,
+                        const std::string& device_id) {
     DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread());
     audio_output_stream_ = audio_manager()->MakeAudioOutputStream(
-        params, std::string(), AudioManager::LogCallback());
+        params, device_id, AudioManager::LogCallback());
     EXPECT_TRUE(audio_output_stream_);
   }
 
@@ -650,6 +681,17 @@
     audio_output_stream_ = nullptr;
   }
 
+  std::optional<AudioDeviceDescription>
+  GetFirstNonDefaultDeviceFromDescriptions(
+      const AudioDeviceDescriptions& devices) {
+    for (AudioDeviceDescription device : devices) {
+      if (!AudioDeviceDescription::IsDefaultDevice(device.unique_id)) {
+        return device;
+      }
+    }
+    return std::nullopt;
+  }
+
   base::test::ScopedFeatureList feature_list_;
   base::test::SingleThreadTaskEnvironment task_environment_;
   std::unique_ptr<AudioManager> audio_manager_;
@@ -686,9 +728,11 @@
     return devices;
   }
 
-  void MakeAudioInputStreamOnAudioThread(const AudioParameters& params) {
+  void MakeAudioInputStreamOnAudioThread(
+      const AudioParameters& params,
+      const std::string& device_id = AudioDeviceDescription::kDefaultDeviceId) {
     RunOnAudioThread(base::BindOnce(&AudioAndroidInputTest::MakeInputStream,
-                                    base::Unretained(this), params));
+                                    base::Unretained(this), params, device_id));
   }
 
   void CloseAudioInputStreamOnAudioThread(raw_ptr<AudioInputStream> stream) {
@@ -759,11 +803,17 @@
             AudioDeviceDescription::kDefaultDeviceId);
   }
 
-  void MakeInputStream(const AudioParameters& params) {
+  std::optional<AudioDeviceDescription> GetFirstNonDefaultInputDevice() {
+    AudioDeviceDescriptions devices =
+        GetAudioInputDeviceDescriptionsOnAudioThread();
+    return GetFirstNonDefaultDeviceFromDescriptions(devices);
+  }
+
+  void MakeInputStream(const AudioParameters& params,
+                       const std::string& device_id) {
     DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread());
     audio_input_stream_ = audio_manager()->MakeAudioInputStream(
-        params, AudioDeviceDescription::kDefaultDeviceId,
-        AudioManager::LogCallback());
+        params, device_id, AudioManager::LogCallback());
     EXPECT_TRUE(audio_input_stream_);
   }
 
@@ -828,6 +878,19 @@
   CloseAudioInputStreamOnAudioThread(audio_input_stream_);
 }
 
+// Ensure that an input stream with a non-default device can be created and
+// closed.
+TEST_P(AudioAndroidInputTest, CreateAndCloseInputStreamWithDevice) {
+  std::optional<AudioDeviceDescription> device =
+      GetFirstNonDefaultInputDevice();
+  if (!device.has_value()) {
+    GTEST_SKIP() << "Missing non-default input device.";
+  }
+  AudioParameters params = GetDefaultInputStreamParametersOnAudioThread();
+  MakeAudioInputStreamOnAudioThread(params, device->unique_id);
+  CloseAudioInputStreamOnAudioThread(audio_input_stream_);
+}
+
 // Ensure that a default output stream can be created and closed.
 // TODO(henrika): should we also verify that this API changes the audio mode
 // to communication mode, and calls RegisterHeadsetReceiver, the first time
@@ -838,6 +901,20 @@
   CloseAudioOutputStreamOnAudioThread(audio_output_stream_);
 }
 
+// Ensure that an output stream with a non-default device can be created and
+// closed. This test is only relevant for AAudioWithPerStreamDeviceSelection.
+TEST_F(AudioAndroidOutputTest, CreateAndCloseOutputStreamWithDevice) {
+  InitFeatures(AudioApi::AAudioWithPerStreamDeviceSelection);
+  std::optional<AudioDeviceDescription> device =
+      GetFirstNonDefaultOutputDevice();
+  if (!device.has_value()) {
+    GTEST_SKIP() << "Missing non-default output device.";
+  }
+  AudioParameters params = GetDefaultOutputStreamParametersOnAudioThread();
+  MakeAudioOutputStreamOnAudioThread(params, device->unique_id);
+  CloseAudioOutputStreamOnAudioThread(audio_output_stream_);
+}
+
 // Ensure that a default input stream can be opened and closed.
 TEST_P(AudioAndroidInputTest, OpenAndCloseInputStream) {
   AudioParameters params = GetDefaultInputStreamParametersOnAudioThread();
@@ -845,6 +922,19 @@
   OpenAndCloseAudioInputStreamOnAudioThread();
 }
 
+// Ensure that an input stream with a non-default device can be opened and
+// closed.
+TEST_P(AudioAndroidInputTest, OpenAndCloseInputStreamWithDevice) {
+  std::optional<AudioDeviceDescription> device =
+      GetFirstNonDefaultInputDevice();
+  if (!device.has_value()) {
+    GTEST_SKIP() << "Missing non-default input device.";
+  }
+  AudioParameters params = GetDefaultInputStreamParametersOnAudioThread();
+  MakeAudioInputStreamOnAudioThread(params, device->unique_id);
+  OpenAndCloseAudioInputStreamOnAudioThread();
+}
+
 // Ensure that a default output stream can be opened and closed.
 TEST_P(AudioAndroidOutputTest, OpenAndCloseOutputStream) {
   AudioParameters params = GetDefaultOutputStreamParametersOnAudioThread();
@@ -852,6 +942,20 @@
   OpenAndCloseAudioOutputStreamOnAudioThread();
 }
 
+// Ensure that an output stream with a non-default device can be opened and
+// closed. This test is only relevant for AAudioWithPerStreamDeviceSelection.
+TEST_F(AudioAndroidOutputTest, OpenAndCloseOutputStreamWithDevice) {
+  InitFeatures(AudioApi::AAudioWithPerStreamDeviceSelection);
+  std::optional<AudioDeviceDescription> device =
+      GetFirstNonDefaultOutputDevice();
+  if (!device.has_value()) {
+    GTEST_SKIP() << "Missing non-default output device.";
+  }
+  AudioParameters params = GetDefaultOutputStreamParametersOnAudioThread();
+  MakeAudioOutputStreamOnAudioThread(params, device->unique_id);
+  OpenAndCloseAudioOutputStreamOnAudioThread();
+}
+
 // Start input streaming using default input parameters and ensure that the
 // callback sequence is sane.
 TEST_P(AudioAndroidInputTest, StartInputStreamCallbacks) {
@@ -990,14 +1094,20 @@
   StopAndCloseAudioInputStreamOnAudioThread();
 }
 
-INSTANTIATE_TEST_SUITE_P(,
-                         AudioAndroidOutputTest,
-                         testing::Values(AudioApi::AAudio, AudioApi::OpenSLES),
-                         testing::PrintToStringParamName());
+INSTANTIATE_TEST_SUITE_P(
+    ,
+    AudioAndroidOutputTest,
+    testing::Values(AudioApi::AAudioWithPerStreamDeviceSelection,
+                    AudioApi::AAudioWithCommunicationDevices,
+                    AudioApi::OpenSLES),
+    testing::PrintToStringParamName());
 
-INSTANTIATE_TEST_SUITE_P(,
-                         AudioAndroidInputTest,
-                         testing::Values(AudioApi::AAudio, AudioApi::OpenSLES),
-                         testing::PrintToStringParamName());
+INSTANTIATE_TEST_SUITE_P(
+    ,
+    AudioAndroidInputTest,
+    testing::Values(AudioApi::AAudioWithPerStreamDeviceSelection,
+                    AudioApi::AAudioWithCommunicationDevices,
+                    AudioApi::OpenSLES),
+    testing::PrintToStringParamName());
 
 }  // namespace media
diff --git a/media/audio/android/audio_device_id.cc b/media/audio/android/audio_device_id.cc
new file mode 100644
index 0000000..3831902
--- /dev/null
+++ b/media/audio/android/audio_device_id.cc
@@ -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.
+
+#include "media/audio/android/audio_device_id.h"
+
+#include <aaudio/AAudio.h>
+
+#include <optional>
+#include <string_view>
+
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "media/audio/audio_device_description.h"
+
+namespace media::android {
+
+AudioDeviceId::AudioDeviceId(int32_t id) : id_(id) {}
+
+AudioDeviceId AudioDeviceId::Default() {
+  return AudioDeviceId(AAUDIO_UNSPECIFIED);
+}
+
+std::optional<AudioDeviceId> AudioDeviceId::Parse(
+    const std::string_view device_id_string) {
+  if (AudioDeviceDescription::IsDefaultDevice(device_id_string)) {
+    return AudioDeviceId::Default();
+  }
+
+  int32_t parsed_number;
+  bool success = base::StringToInt(device_id_string, &parsed_number);
+  if (!success) {
+    DLOG(ERROR) << "Failed to parse device_id_string as number: "
+                << device_id_string;
+    return std::nullopt;
+  }
+
+  // An Android device ID string containing the numeric value of
+  // `AAUDIO_UNSPECIFIED` is invalid, as this value is reserved for the
+  // "default" device case, for which a string matching
+  // `AudioDeviceDescription::IsDefaultDevice` should always be used.
+  if (parsed_number == AAUDIO_UNSPECIFIED) {
+    DLOG(ERROR) << "device_id_string unexpectedly contained the numeric value "
+                   "reserved for the default device";
+    return std::nullopt;
+  }
+
+  return AudioDeviceId(parsed_number);
+}
+
+bool AudioDeviceId::IsDefault() const {
+  return id_ == AAUDIO_UNSPECIFIED;
+}
+
+int32_t AudioDeviceId::ToAAudioDeviceId() const {
+  return id_;
+}
+
+}  // namespace media::android
diff --git a/media/audio/android/audio_device_id.h b/media/audio/android/audio_device_id.h
new file mode 100644
index 0000000..57b2cb9
--- /dev/null
+++ b/media/audio/android/audio_device_id.h
@@ -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.
+
+#ifndef MEDIA_AUDIO_ANDROID_AUDIO_DEVICE_ID_H_
+#define MEDIA_AUDIO_ANDROID_AUDIO_DEVICE_ID_H_
+
+#include <optional>
+#include <string_view>
+
+#include "media/base/media_export.h"
+
+namespace media::android {
+
+// Wrapper around Android audio device IDs which handles the virtual "default"
+// device and string conversion.
+class MEDIA_EXPORT AudioDeviceId {
+ public:
+  static AudioDeviceId Default();
+
+  // Parses a string representation of a device ID to an `AudioDeviceId`.
+  // Returns std::nullopt if parsing fails.
+  static std::optional<AudioDeviceId> Parse(std::string_view device_id_string);
+
+  bool IsDefault() const;
+
+  // Converts the device ID to a value compatible with
+  // `AAudioStreamBuilder_setDeviceId`.
+  int32_t ToAAudioDeviceId() const;
+
+ private:
+  explicit AudioDeviceId(int32_t id);
+
+  // Numeric ID compatible with AAudio and Java Android APIs, or
+  // `AAUDIO_UNSPECIFIED` in the case of the "default" device.
+  int32_t id_;
+};
+
+}  // namespace media::android
+
+#endif  // MEDIA_AUDIO_ANDROID_AUDIO_DEVICE_ID_H_
diff --git a/media/audio/android/audio_device_id_unittest.cc b/media/audio/android/audio_device_id_unittest.cc
new file mode 100644
index 0000000..fe1a1b47
--- /dev/null
+++ b/media/audio/android/audio_device_id_unittest.cc
@@ -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.
+
+#include "media/audio/android/audio_device_id.h"
+
+#include <aaudio/AAudio.h>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media::android {
+
+TEST(AudioAndroidAudioDeviceIdTest, CreateDefaultDeviceId) {
+  const AudioDeviceId id = AudioDeviceId::Default();
+  EXPECT_TRUE(id.IsDefault());
+}
+
+TEST(AudioAndroidAudioDeviceIdTest, ParseDefaultDeviceId) {
+  for (std::string id_string : {"", "default"}) {
+    const std::optional<AudioDeviceId> id = AudioDeviceId::Parse(id_string);
+    ASSERT_TRUE(id.has_value());
+    EXPECT_TRUE(id->IsDefault());
+  }
+}
+
+TEST(AudioAndroidAudioDeviceIdTest, ParseValidNonDefaultDeviceId) {
+  const std::optional<AudioDeviceId> id = AudioDeviceId::Parse("100");
+  ASSERT_TRUE(id.has_value());
+  EXPECT_FALSE(id->IsDefault());
+  EXPECT_EQ(id->ToAAudioDeviceId(), 100);
+}
+
+TEST(AudioAndroidAudioDeviceIdTest, ParseInvalidNonDefaultDeviceId) {
+  for (const std::string& id_string : {"0", "999999999999", " 4", "x"}) {
+    const std::optional<AudioDeviceId> id = AudioDeviceId::Parse(id_string);
+    EXPECT_FALSE(id.has_value());
+  }
+}
+
+TEST(AudioAndroidAudioDeviceIdTest, ConvertDefaultDeviceIdToAAudioDeviceId) {
+  const AudioDeviceId id = AudioDeviceId::Default();
+  EXPECT_EQ(id.ToAAudioDeviceId(), AAUDIO_UNSPECIFIED);
+}
+
+TEST(AudioAndroidAudioDeviceIdTest, ConvertNonDefaultDeviceIdToAAudioDeviceId) {
+  const std::optional<AudioDeviceId> id = AudioDeviceId::Parse("123");
+  ASSERT_TRUE(id.has_value());
+  EXPECT_EQ(id->ToAAudioDeviceId(), 123);
+}
+
+}  // namespace media::android
diff --git a/media/audio/android/audio_manager_android.cc b/media/audio/android/audio_manager_android.cc
index 83bbbeb..21be136 100644
--- a/media/audio/android/audio_manager_android.cc
+++ b/media/audio/android/audio_manager_android.cc
@@ -10,12 +10,12 @@
 #include "base/android/jni_array.h"
 #include "base/android/jni_string.h"
 #include "base/android/scoped_java_ref.h"
+#include "base/feature_list.h"
 #include "base/functional/bind.h"
 #include "base/logging.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "build/android_buildflags.h"
-#include "media/media_buildflags.h"
 #include "media/audio/android/aaudio_input.h"
 #include "media/audio/android/aaudio_output.h"
 #include "media/audio/android/audio_track_output_stream.h"
@@ -25,6 +25,7 @@
 #include "media/audio/fake_audio_input_stream.h"
 #include "media/base/audio_parameters.h"
 #include "media/base/channel_layout.h"
+#include "media/media_buildflags.h"
 
 #if BUILDFLAG(USE_OPENSLES)
 #include "media/audio/android/opensles_input.h"
@@ -41,6 +42,7 @@
 using base::android::JavaParamRef;
 using base::android::JavaRef;
 using base::android::ScopedJavaLocalRef;
+using media::android::AudioDeviceId;
 
 namespace media {
 namespace {
@@ -63,10 +65,18 @@
 }
 
 bool UseAAudioOutput() {
+  if (!__builtin_available(android AAUDIO_MIN_API, *)) {
+    return false;
+  }
+
   return base::FeatureList::IsEnabled(features::kUseAAudioDriver);
 }
 
 bool UseAAudioInput() {
+  if (!__builtin_available(android AAUDIO_MIN_API, *)) {
+    return false;
+  }
+
   if (!base::FeatureList::IsEnabled(features::kUseAAudioInput)) {
     return false;
   }
@@ -84,6 +94,12 @@
   return true;
 }
 
+bool UseAAudioPerStreamDeviceSelection() {
+  return UseAAudioInput() && UseAAudioOutput() &&
+         base::FeatureList::IsEnabled(
+             features::kAAudioPerStreamDeviceSelection);
+}
+
 }  // namespace
 
 std::unique_ptr<AudioManager> CreateAudioManager(
@@ -137,34 +153,83 @@
     AudioDeviceNames* device_names) {
   DCHECK(GetTaskRunner()->BelongsToCurrentThread());
 
+  if (UseAAudioPerStreamDeviceSelection()) {
+    GetDeviceNames(device_names, AudioDeviceDirection::kInput);
+    return;
+  }
+
+  // Android devices in general do not have robust support for specifying
+  // devices individually per input or output stream, and as such
+  // `AAudioPerStreamDeviceSelection` is usually disabled. Instead, if a
+  // specific device is requested, we set a single input/output pair (a.k.a. a
+  // "communication device") to be used for streams. Note that it is possible
+  // for a communication device to be an output-only device. In these cases,
+  // the framework seems to choose some other available input device for
+  // communication streams. It's not clear whether this is a real issue,
+  // considering how long this code has been around for...
+  //
+  // For compatibility with Android R-, which predates the concept of
+  // Android communication devices, the externally exposed devices are
+  // "synthetic" devices which abstract away the internal device IDs and
+  // manufacturer-given names provided by the Android framework (e.g.
+  // "Bluetooth headset" instead of "FooBuds Pro 2.0"):
+  // * On Android S+, these devices correspond to actual communication
+  // devices.
+  // * On Android R-, these devices don't correspond to devices from a list,
+  // but each one can be controlled via appropriate Android API calls, e.g.
+  // AudioManager#startBluetoothSco() for Bluetooth.
+  GetDeviceNames(device_names, AudioDeviceDirection::kCommunication);
+}
+
+void AudioManagerAndroid::GetAudioOutputDeviceNames(
+    AudioDeviceNames* device_names) {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+
+  if (UseAAudioPerStreamDeviceSelection()) {
+    GetDeviceNames(device_names, AudioDeviceDirection::kOutput);
+    return;
+  }
+
+  // Android devices in general do not have robust support for specifying
+  // devices individually per input or output stream, and as such
+  // `AAudioPerStreamDeviceSelection` is usually disabled. In these
+  // situations, if a specific device is requested, we set a single
+  // input/output pair (a.k.a. a "communication device") to be used for
+  // streams system-wide.
+  //
+  // We've only returned "default" here for quite some time, relying on output
+  // device selection being controlled by input device selection (see
+  // `GetAudioInputDeviceNames`). Populating this list with other devices has
+  // prevented confusion for users; it would've given them the option to set a
+  // different input and output device, which wouldn't actually work. However,
+  // since communication devices on Android are technically output devices for
+  // which an input device is automatically chosen, it could be more
+  // appropriate to invert the input and output device lists.
+  AddDefaultDevice(device_names);
+}
+
+void AudioManagerAndroid::GetDeviceNames(AudioDeviceNames* device_names,
+                                         AudioDeviceDirection direction) {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+
   // Always add default device parameters as first element.
   DCHECK(device_names->empty());
   AddDefaultDevice(device_names);
 
-  // Android devices in general do not have robust support for specifying
-  // devices individually per input or output stream. Instead, if a specific
-  // device is requested, we set a single input/output pair (a.k.a. a
-  // "communication device") to be used for streams. Note that it is possible
-  // for a communication device to be an output-only device. In these cases, the
-  // framework seems to choose some other available input device for
-  // communication streams. It's not clear whether this is a real issue,
-  // considering how long this code has been around for...
-  //
-  // For compatibility with Android R-, which predates the concept of Android
-  // communication devices, the externally exposed devices are "synthetic"
-  // devices which abstract away the internal device IDs and manufacturer-given
-  // names provided by the Android framework (e.g. "Bluetooth headset" instead
-  // of "FooBuds Pro 2.0"):
-  // * On Android S+, these devices correspond to actual communication devices.
-  // * On Android R-, these devices don't correspond to devices from a list, but
-  // each one can be controlled via appropriate Android API calls, e.g.
-  // AudioManager#startBluetoothSco() for Bluetooth.
-  // TODO(b/373305023): Expose specific model names here, and allow for
-  // per-stream device selection.
   JNIEnv* env = AttachCurrentThread();
-  ScopedJavaLocalRef<jobjectArray> j_device_array =
-      Java_AudioManagerAndroid_getCommunicationDevices(env,
-                                                       GetJavaAudioManager());
+  ScopedJavaLocalRef<jobjectArray> j_device_array;
+  switch (direction) {
+    case AudioDeviceDirection::kInput:
+    case AudioDeviceDirection::kOutput:
+      j_device_array = Java_AudioManagerAndroid_getDevices(
+          env, GetJavaAudioManager(),
+          /*inputs=*/direction == AudioDeviceDirection::kInput);
+      break;
+    case AudioDeviceDirection::kCommunication:
+      j_device_array = Java_AudioManagerAndroid_getCommunicationDevices(
+          env, GetJavaAudioManager());
+      break;
+  }
   if (j_device_array.is_null()) {
     // Most probable reason for a NULL result here is that the process lacks
     // MODIFY_AUDIO_SETTINGS or RECORD_AUDIO permissions.
@@ -174,10 +239,19 @@
   for (auto j_device : j_device_array.ReadElements<jobject>()) {
     ScopedJavaLocalRef<jstring> j_device_name =
         Java_AudioDevice_name(env, j_device);
-    ConvertJavaStringToUTF8(env, j_device_name.obj(), &device.device_name);
+    if (!j_device_name.is_null()) {
+      ConvertJavaStringToUTF8(env, j_device_name.obj(), &device.device_name);
+    } else {
+      device.device_name =
+          "Audio device";  // TODO(crbug.com/409028970): Also return the device
+                           // type and provide a localized, type-specific
+                           // fallback string.
+    }
+
     ScopedJavaLocalRef<jstring> j_device_id =
         Java_AudioDevice_id(env, j_device);
     ConvertJavaStringToUTF8(env, j_device_id.obj(), &device.unique_id);
+
     device_names->push_back(device);
   }
 
@@ -187,26 +261,6 @@
   }
 }
 
-void AudioManagerAndroid::GetAudioOutputDeviceNames(
-    AudioDeviceNames* device_names) {
-  // Android devices in general do not have robust support for specifying
-  // devices individually per input or output stream. Instead, if a specific
-  // device is requested, we set a single input/output pair (a.k.a. a
-  // "communication device") to be used for streams.
-  //
-  // We've only returned "default" here for quite some time, relying on output
-  // device selection being controlled by input device selection (see
-  // `GetAudioInputDeviceNames`). Populating this list with other devices has
-  // prevented confusion for users; it would've given them the option to set a
-  // different input and output device, which wouldn't actually work. However,
-  // since communication devices on Android are technically output devices for
-  // which an input device is automatically chosen, it could be more appropriate
-  // to invert the input and output device lists.
-  // TODO(b/373305023): Populate `device_names` with the real list of output
-  // devices and allow for per-stream device selection.
-  AddDefaultDevice(device_names);
-}
-
 AudioParameters AudioManagerAndroid::GetInputStreamParameters(
     const std::string& device_id) {
   DCHECK(GetTaskRunner()->BelongsToCurrentThread());
@@ -226,8 +280,9 @@
                  : AudioParameters::NO_EFFECTS;
 
   int user_buffer_size = GetUserBufferSize();
-  if (user_buffer_size)
+  if (user_buffer_size) {
     buffer_size = user_buffer_size;
+  }
 
   AudioParameters params(AudioParameters::AUDIO_PCM_LOW_LATENCY,
                          ChannelLayoutConfig::FromLayout<channel_layout>(),
@@ -248,8 +303,9 @@
   DCHECK(GetTaskRunner()->BelongsToCurrentThread());
   AudioOutputStream* stream = AudioManagerBase::MakeAudioOutputStream(
       params, device_id, AudioManager::LogCallback());
-  if (stream)
+  if (stream) {
     streams_.insert(static_cast<MuteableAudioOutputStream*>(stream));
+  }
   return stream;
 }
 
@@ -259,11 +315,10 @@
     const LogCallback& log_callback) {
   DCHECK(GetTaskRunner()->BelongsToCurrentThread());
   bool has_input_streams = !HasNoAudioInputStreams();
-  bool force_communication_mode = false;
   AudioInputStream* stream = AudioManagerBase::MakeAudioInputStream(
       params, device_id, AudioManager::LogCallback());
   // Avoid changing the communication mode if there are existing input streams.
-  if (!stream || has_input_streams) {
+  if (!stream || has_input_streams || UseAAudioPerStreamDeviceSelection()) {
     return stream;
   }
 
@@ -279,6 +334,7 @@
   // MODE_IN_COMMUNICATION. Failing to activate communication mode can result
   // in audio being routed incorrectly, leading to no sound output from the
   // Bluetooth headset.
+  bool force_communication_mode = false;
 #if BUILDFLAG(IS_DESKTOP_ANDROID)
   force_communication_mode = IsBluetoothMicrophoneOn();
 #endif
@@ -316,7 +372,8 @@
 
   if (__builtin_available(android AAUDIO_MIN_API, *)) {
     if (UseAAudioOutput()) {
-      return new AAudioOutputStream(this, params, AAUDIO_USAGE_MEDIA);
+      return new AAudioOutputStream(this, params, AudioDeviceId::Default(),
+                                    AAUDIO_USAGE_MEDIA);
     }
   }
 #if BUILDFLAG(USE_OPENSLES)
@@ -334,10 +391,18 @@
 
   if (__builtin_available(android AAUDIO_MIN_API, *)) {
     if (UseAAudioOutput()) {
+      AudioDeviceId parsed_device_id =
+          AudioDeviceId::Parse(device_id).value_or(AudioDeviceId::Default());
+      DCHECK(UseAAudioPerStreamDeviceSelection() ||
+             parsed_device_id.IsDefault())
+          << "Non-default output device chosen for output communication "
+             "stream.";
+
       const aaudio_usage_t usage = communication_mode_is_on_
                                        ? AAUDIO_USAGE_VOICE_COMMUNICATION
                                        : AAUDIO_USAGE_MEDIA;
-      return new AAudioOutputStream(this, params, usage);
+      return new AAudioOutputStream(this, params, std::move(parsed_device_id),
+                                    usage);
     }
   }
 
@@ -366,14 +431,16 @@
     const AudioParameters& params,
     const std::string& device_id,
     const LogCallback& log_callback) {
-  // TODO(henrika): add support for device selection if/when any client
-  // needs it.
-  DLOG_IF(ERROR, !device_id.empty()) << "Not implemented!";
   DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
 
   if (__builtin_available(android AAUDIO_MIN_API, *)) {
     if (UseAAudioInput()) {
-      return new AAudioInputStream(this, params);
+      AudioDeviceId parsed_device_id =
+          UseAAudioPerStreamDeviceSelection()
+              ? AudioDeviceId::Parse(device_id).value_or(
+                    AudioDeviceId::Default())
+              : AudioDeviceId::Default();
+      return new AAudioInputStream(this, params, std::move(parsed_device_id));
     }
   }
 
@@ -393,20 +460,27 @@
   DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
   DLOG_IF(ERROR, device_id.empty()) << "Invalid device ID!";
 
-  // Use the device ID to select the correct communication device. If the
-  // default device is requested, a communication device will be chosen based on
-  // an internal selection scheme. Note that a communication device is an output
-  // device that the system associates with an input device, and this
-  // selection switches the device used for all input and output streams with
-  // communication usage set.
-  if (!SetCommunicationDevice(device_id)) {
-    LOG(ERROR) << "Unable to select communication device!";
-    return nullptr;
+  if (!UseAAudioPerStreamDeviceSelection()) {
+    // Use the device ID to select the correct communication device. If the
+    // default device is requested, a communication device will be chosen based
+    // on an internal selection scheme. Note that a communication device is an
+    // output device that the system associates with an input device, and this
+    // selection switches the device used for all input and output streams with
+    // communication usage set.
+    if (!SetCommunicationDevice(device_id)) {
+      LOG(ERROR) << "Unable to select communication device!";
+      return nullptr;
+    }
   }
 
   if (__builtin_available(android AAUDIO_MIN_API, *)) {
     if (UseAAudioInput()) {
-      return new AAudioInputStream(this, params);
+      AudioDeviceId parsed_device_id =
+          UseAAudioPerStreamDeviceSelection()
+              ? AudioDeviceId::Parse(device_id).value_or(
+                    AudioDeviceId::Default())
+              : AudioDeviceId::Default();
+      return new AAudioInputStream(this, params, std::move(parsed_device_id));
     }
   }
 
@@ -463,8 +537,9 @@
 
     // AudioManager APIs for GetOptimalOutputFrameSize() don't support channel
     // layouts greater than stereo unless low latency audio is supported.
-    if (input_params.channels() <= 2 || IsAudioLowLatencySupported())
+    if (input_params.channels() <= 2 || IsAudioLowLatencySupported()) {
       channel_layout_config = input_params.channel_layout_config();
+    }
 
     // For high latency playback on supported platforms, pass through the
     // requested buffer size; this provides significant power savings (~25%) and
@@ -478,8 +553,9 @@
   }
 
   int user_buffer_size = GetUserBufferSize();
-  if (user_buffer_size)
+  if (user_buffer_size) {
     buffer_size = user_buffer_size;
+  }
 
   // Check if device supports additional audio encodings.
   if (IsAudioSinkConnected()) {
@@ -555,13 +631,14 @@
 
 int AudioManagerAndroid::GetOptimalOutputFrameSize(int sample_rate,
                                                    int channels) {
-  if (IsAudioLowLatencySupported())
+  if (IsAudioLowLatencySupported()) {
     return GetAudioLowLatencyOutputFrameSize();
+  }
 
-  return std::max(kDefaultOutputBufferSize,
-                  Java_AudioManagerAndroid_getMinOutputFrameSize(
-                      base::android::AttachCurrentThread(),
-                      sample_rate, channels));
+  return std::max(
+      kDefaultOutputBufferSize,
+      Java_AudioManagerAndroid_getMinOutputFrameSize(
+          base::android::AttachCurrentThread(), sample_rate, channels));
 }
 
 // Returns a bit mask of AudioParameters::Format enum values sink device
@@ -591,9 +668,8 @@
 
 void AudioManagerAndroid::DoSetMuteOnAudioThread(bool muted) {
   DCHECK(GetTaskRunner()->BelongsToCurrentThread());
-  for (OutputStreams::iterator it = streams_.begin();
-       it != streams_.end(); ++it) {
-    (*it)->SetMute(muted);
+  for (auto stream : streams_) {
+    stream->SetMute(muted);
   }
 }
 
@@ -602,9 +678,8 @@
   output_volume_override_ = volume;
 
   DCHECK(GetTaskRunner()->BelongsToCurrentThread());
-  for (OutputStreams::iterator it = streams_.begin(); it != streams_.end();
-       ++it) {
-    (*it)->SetVolume(volume);
+  for (auto stream : streams_) {
+    stream->SetVolume(volume);
   }
 }
 
diff --git a/media/audio/android/audio_manager_android.h b/media/audio/android/audio_manager_android.h
index f8058e1..e6a5333 100644
--- a/media/audio/android/audio_manager_android.h
+++ b/media/audio/android/audio_manager_android.h
@@ -96,8 +96,16 @@
       const AudioParameters& input_params) override;
 
  private:
+  enum class AudioDeviceDirection {
+    kInput,         // Audio source
+    kOutput,        // Audio sink
+    kCommunication  // Communication device, i.e. an input/output pair.
+  };
+
   const base::android::JavaRef<jobject>& GetJavaAudioManager();
   bool HasNoAudioInputStreams();
+  void GetDeviceNames(AudioDeviceNames* device_names,
+                      AudioDeviceDirection direction);
   void SetCommunicationAudioModeOn(bool on);
   bool SetCommunicationDevice(const std::string& device_id);
   int GetNativeOutputSampleRate();
diff --git a/media/base/android/java/src/org/chromium/media/AudioManagerAndroid.java b/media/base/android/java/src/org/chromium/media/AudioManagerAndroid.java
index 33c0bf4..c141af2b 100644
--- a/media/base/android/java/src/org/chromium/media/AudioManagerAndroid.java
+++ b/media/base/android/java/src/org/chromium/media/AudioManagerAndroid.java
@@ -30,6 +30,8 @@
 import org.chromium.build.annotations.Nullable;
 
 import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Optional;
 
 @JNINamespace("media")
@@ -44,9 +46,9 @@
     /** Simple container for device information. */
     public static class AudioDevice {
         private final int mId;
-        private final String mName;
+        private final @Nullable String mName;
 
-        public AudioDevice(int id, String name) {
+        public AudioDevice(int id, @Nullable String name) {
             mId = id;
             mName = name;
         }
@@ -57,7 +59,7 @@
         }
 
         @CalledByNative("AudioDevice")
-        private String name() {
+        private @Nullable String name() {
             return mName;
         }
     }
@@ -257,11 +259,57 @@
     }
 
     /**
+     * @param inputs If true, input devices will be returned; otherwise, output devices will be
+     *     returned.
+     * @return The current list of available audio devices. Note that this call does not trigger any
+     *     update of the list of devices, it only copies the current state into the output array.
+     */
+    @CalledByNative
+    private AudioDevice @Nullable [] getDevices(boolean inputs) {
+        if (DEBUG) logd("getDevices");
+
+        AudioDeviceInfo[] deviceInfos =
+                mAudioManager.getDevices(
+                        inputs
+                                ? AudioManager.GET_DEVICES_INPUTS
+                                : AudioManager.GET_DEVICES_OUTPUTS);
+
+        List<AudioDevice> devices = new ArrayList<>();
+        for (int deviceIndex = 0; deviceIndex < deviceInfos.length; deviceIndex++) {
+            AudioDeviceInfo deviceInfo = deviceInfos[deviceIndex];
+
+            int type = deviceInfo.getType();
+            switch (type) {
+                case 28: // AudioDeviceInfo.TYPE_ECHO_REFERENCE
+                case AudioDeviceInfo.TYPE_REMOTE_SUBMIX:
+                case AudioDeviceInfo.TYPE_TELEPHONY:
+                    // Unusable device types.
+                    continue;
+                case AudioDeviceInfo.TYPE_BLUETOOTH_A2DP:
+                case AudioDeviceInfo.TYPE_BLUETOOTH_SCO:
+                    // TODO(crbug.com/405955144): Bluetooth Classic streams do not work correctly,
+                    // as they do not manage or react to SCO state changes.
+                    continue;
+            }
+
+            int id = deviceInfo.getId();
+            String name = deviceInfo.getProductName().toString();
+            if (name.equals(android.os.Build.MODEL)) {
+                // Undo the Android framework's substitution of a missing name with
+                // `android.os.Build.MODEL` to facilitate providing a custom fallback name instead.
+                name = null;
+            }
+            devices.add(new AudioDevice(id, name));
+        }
+        return devices.toArray(new AudioDevice[0]);
+    }
+
+    /**
      * Required permissions: android.Manifest.permission.MODIFY_AUDIO_SETTINGS and
      * android.Manifest.permission.RECORD_AUDIO.
      *
-     * @return the current list of available communication devices. Note that this call does not
-     *     trigger any update of the list of devices, it only copies the current state in to the
+     * @return The current list of available communication devices. Note that this call does not
+     *     trigger any update of the list of devices, it only copies the current state into the
      *     output array.
      */
     @CalledByNative
diff --git a/media/base/android/java/src/org/chromium/media/MediaFormatBuilder.java b/media/base/android/java/src/org/chromium/media/MediaFormatBuilder.java
index 8fc3250..ed4c170 100644
--- a/media/base/android/java/src/org/chromium/media/MediaFormatBuilder.java
+++ b/media/base/android/java/src/org/chromium/media/MediaFormatBuilder.java
@@ -28,7 +28,6 @@
             boolean allowAdaptivePlayback,
             int profile) {
         MediaFormat format = MediaFormat.createVideoFormat(mime, width, height);
-        if (format == null) return null;
         setCodecSpecificData(format, csds);
         if (hdrMetadata != null) {
             hdrMetadata.addMetadataToFormat(format);
diff --git a/media/base/media_shmem_export.h b/media/base/media_shmem_export.h
index 352d219a..52c3e7a 100644
--- a/media/base/media_shmem_export.h
+++ b/media/base/media_shmem_export.h
@@ -18,11 +18,7 @@
 #endif  // defined(MEDIA_SHMEM_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(MEDIA_SHMEM_IMPLEMENTATION)
 #define MEDIA_SHMEM_EXPORT __attribute__((visibility("default")))
-#else
-#define MEDIA_SHMEM_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc
index e164751..2fe23aa 100644
--- a/media/base/media_switches.cc
+++ b/media/base/media_switches.cc
@@ -1721,10 +1721,11 @@
 std::optional<base::TimeDelta> GetAecAddedDelay() {
 #if BUILDFLAG(CHROME_WIDE_ECHO_CANCELLATION) && \
     (BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC))
-  return base::Milliseconds(kAddedProcessingDelay.Get());
-#else
-  return std::nullopt;
+  if (IsSystemLoopbackAsAecReferenceEnabled()) {
+    return base::Milliseconds(kAddedProcessingDelay.Get());
+  }
 #endif
+  return std::nullopt;
 }
 
 bool IsSystemEchoCancellationEnforced() {
diff --git a/media/capture/capture_export.h b/media/capture/capture_export.h
index 980fd10..5c07a4c8 100644
--- a/media/capture/capture_export.h
+++ b/media/capture/capture_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(CAPTURE_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(CAPTURE_IMPLEMENTATION)
 #define CAPTURE_EXPORT __attribute__((visibility("default")))
-#else
-#define CAPTURE_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/media/gpu/android/ndk_video_encode_accelerator.cc b/media/gpu/android/ndk_video_encode_accelerator.cc
index 4408018..d6706a0e 100644
--- a/media/gpu/android/ndk_video_encode_accelerator.cc
+++ b/media/gpu/android/ndk_video_encode_accelerator.cc
@@ -507,7 +507,7 @@
   VideoCodec codec = VideoCodecProfileToVideoCodec(config.output_profile);
 
   // These should already be filtered out by VideoEncodeAcceleratorUtil.
-  if (codec != VideoCodec::kH264 && codec == VideoCodec::kHEVC) {
+  if (codec != VideoCodec::kH264) {
     config_.required_encoder_type = EncoderType::kHardware;
   }
 
diff --git a/media/gpu/chromeos/image_processor_perf_test.cc b/media/gpu/chromeos/image_processor_perf_test.cc
index a8bb939..0a6709d 100644
--- a/media/gpu/chromeos/image_processor_perf_test.cc
+++ b/media/gpu/chromeos/image_processor_perf_test.cc
@@ -126,7 +126,7 @@
   constexpr base::TimeDelta kNullTimestamp;
   if (type == VideoFrame::STORAGE_GPU_MEMORY_BUFFER) {
     CHECK(test_sii);
-    return CreateGmbOrMappableSIVideoFrame(
+    return CreateMappableVideoFrame(
         VideoPixelFormat::PIXEL_FORMAT_NV12, size, visible_rect, size,
         kNullTimestamp, gfx::BufferUsage::SCANOUT_CPU_READ_WRITE, test_sii);
   } else if (type == VideoFrame::STORAGE_DMABUFS) {
diff --git a/media/gpu/chromeos/image_processor_test.cc b/media/gpu/chromeos/image_processor_test.cc
index 37d2998..be4e5bfb1 100644
--- a/media/gpu/chromeos/image_processor_test.cc
+++ b/media/gpu/chromeos/image_processor_test.cc
@@ -281,7 +281,7 @@
   const gfx::Rect visible_rect(size);
   constexpr base::TimeDelta kNullTimestamp;
   if (type == VideoFrame::STORAGE_GPU_MEMORY_BUFFER) {
-    return CreateGmbOrMappableSIVideoFrame(
+    return CreateMappableVideoFrame(
         VideoPixelFormat::PIXEL_FORMAT_NV12, size, visible_rect, size,
         kNullTimestamp, gfx::BufferUsage::SCANOUT_CPU_READ_WRITE, test_sii);
   } else {
diff --git a/media/gpu/chromeos/platform_video_frame_utils.cc b/media/gpu/chromeos/platform_video_frame_utils.cc
index ded50ab..5551f45 100644
--- a/media/gpu/chromeos/platform_video_frame_utils.cc
+++ b/media/gpu/chromeos/platform_video_frame_utils.cc
@@ -27,7 +27,6 @@
 #include "base/strings/stringprintf.h"
 #include "base/synchronization/lock.h"
 #include "components/viz/common/resources/shared_image_format_utils.h"
-#include "gpu/ipc/common/gpu_memory_buffer_support.h"
 #include "media/base/color_plane_layout.h"
 #include "media/base/format_utils.h"
 #include "media/base/media_switches.h"
@@ -387,7 +386,7 @@
   return gfx::GpuMemoryBufferId(next_gpu_memory_buffer_id++);
 }
 
-scoped_refptr<VideoFrame> CreateGmbOrMappableSIVideoFrame(
+scoped_refptr<VideoFrame> CreateMappableVideoFrame(
     VideoPixelFormat pixel_format,
     const gfx::Size& coded_size,
     const gfx::Rect& visible_rect,
@@ -416,14 +415,13 @@
     base::TimeDelta timestamp,
     gfx::BufferUsage buffer_usage,
     gpu::SharedImageInterface* sii) {
+  CHECK(sii);
   const bool supports_zero_copy_webgpu_import =
       gmb_handle.native_pixmap_handle().supports_zero_copy_webgpu_import;
 
   auto buffer_format = VideoPixelFormatToGfxBufferFormat(pixel_format);
   DCHECK(buffer_format);
 
-  scoped_refptr<VideoFrame> video_frame;
-  if (sii) {
     const auto si_usage = gpu::SHARED_IMAGE_USAGE_CPU_WRITE_ONLY |
                           gpu::SHARED_IMAGE_USAGE_DISPLAY_READ;
     auto shared_image = sii->CreateSharedImage(
@@ -432,28 +430,14 @@
          "PlatformVideoFrameUtils"},
         gpu::kNullSurfaceHandle, buffer_usage, std::move(gmb_handle));
 
-    video_frame = media::VideoFrame::WrapMappableSharedImage(
+    auto video_frame = media::VideoFrame::WrapMappableSharedImage(
         std::move(shared_image), sii->GenVerifiedSyncToken(),
         base::NullCallback(), visible_rect, natural_size, timestamp);
-  } else {
-    gpu::GpuMemoryBufferSupport support;
-    std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer =
-        support.CreateGpuMemoryBufferImplFromHandle(
-            std::move(gmb_handle), coded_size, *buffer_format, buffer_usage,
-            base::NullCallback());
-    if (!gpu_memory_buffer) {
+
+    if (!video_frame) {
       return nullptr;
     }
 
-    // It is not necessary to pass a SharedImage because this VideoFrame is not
-    // rendered.
-    video_frame = VideoFrame::WrapExternalGpuMemoryBuffer(
-        visible_rect, natural_size, std::move(gpu_memory_buffer), timestamp);
-  }
-  if (!video_frame) {
-    return nullptr;
-  }
-
   // We only support importing non-DISJOINT multi-planar GbmBuffer right now.
   // TODO(crbug.com/40201271): Add DISJOINT support.
   video_frame->metadata().is_webgpu_compatible =
diff --git a/media/gpu/chromeos/platform_video_frame_utils.h b/media/gpu/chromeos/platform_video_frame_utils.h
index a524f50..0609aea 100644
--- a/media/gpu/chromeos/platform_video_frame_utils.h
+++ b/media/gpu/chromeos/platform_video_frame_utils.h
@@ -74,14 +74,14 @@
 // GpuMemoryBuffer or Mappable SharedImage allocated with |buffer_usage|.
 // See //media/base/video_frame.h for the other parameters. This function is
 // thread-safe.
-MEDIA_GPU_EXPORT scoped_refptr<VideoFrame> CreateGmbOrMappableSIVideoFrame(
+MEDIA_GPU_EXPORT scoped_refptr<VideoFrame> CreateMappableVideoFrame(
     VideoPixelFormat pixel_format,
     const gfx::Size& coded_size,
     const gfx::Rect& visible_rect,
     const gfx::Size& natural_size,
     base::TimeDelta timestamp,
     gfx::BufferUsage buffer_usage,
-    gpu::SharedImageInterface* sii = nullptr);
+    gpu::SharedImageInterface* sii);
 
 // Creates a STORAGE_GPU_MEMORY_BUFFER VideoFrame from a GpuMemoryBufferHandle.
 // See //media/base/video_frame.h for the other parameters. This function is
@@ -94,7 +94,7 @@
     const gfx::Size& natural_size,
     base::TimeDelta timestamp,
     gfx::BufferUsage buffer_usage,
-    gpu::SharedImageInterface* sii = nullptr);
+    gpu::SharedImageInterface* sii);
 
 // Creates a STORAGE_DMABUFS VideoFrame whose buffer is allocated with
 // |buffer_usage|. See //media/base/video_frame.h for the other parameters. This
diff --git a/media/gpu/chromeos/platform_video_frame_utils_unittest.cc b/media/gpu/chromeos/platform_video_frame_utils_unittest.cc
index 304c505..b08f851 100644
--- a/media/gpu/chromeos/platform_video_frame_utils_unittest.cc
+++ b/media/gpu/chromeos/platform_video_frame_utils_unittest.cc
@@ -133,7 +133,7 @@
 
 // TODO(b/230370976): remove this #if/#endif guard. To do so, we need to be able
 // to mock/fake the allocator used by CreatePlatformVideoFrame() and
-// CreateGmbOrMappableSIVideoFrame() so that those functions return a
+// CreateMappableVideoFrame() so that those functions return a
 // non-nullptr frame on platforms where allocating NV12 buffers is not
 // supported.
 #if BUILDFLAG(IS_CHROMEOS)
@@ -160,9 +160,9 @@
                                      kNaturalSize, kTimeStamp, kBufferUsage);
         break;
       case VideoFrame::STORAGE_GPU_MEMORY_BUFFER:
-        frame = CreateGmbOrMappableSIVideoFrame(
-            kPixelFormat, kCodedSize, kVisibleRect, kNaturalSize, kTimeStamp,
-            kBufferUsage, test_sii.get());
+        frame = CreateMappableVideoFrame(kPixelFormat, kCodedSize, kVisibleRect,
+                                         kNaturalSize, kTimeStamp, kBufferUsage,
+                                         test_sii.get());
         break;
       default:
         NOTREACHED();
diff --git a/media/gpu/chromeos/vulkan_overlay_adaptor_test.cc b/media/gpu/chromeos/vulkan_overlay_adaptor_test.cc
index 05d4b8c..3430eaa2 100644
--- a/media/gpu/chromeos/vulkan_overlay_adaptor_test.cc
+++ b/media/gpu/chromeos/vulkan_overlay_adaptor_test.cc
@@ -705,7 +705,7 @@
                           kMM21TileHeight) *
           bpp_numerator / bpp_denom);
 
-  scoped_refptr<VideoFrame> frame = CreateGmbOrMappableSIVideoFrame(
+  scoped_refptr<VideoFrame> frame = CreateMappableVideoFrame(
       VideoPixelFormat::PIXEL_FORMAT_NV12, alloc_size, visible_rect, alloc_size,
       kNullTimestamp, gfx::BufferUsage::SCANOUT_CPU_READ_WRITE,
       test_sii_.get());
@@ -746,7 +746,7 @@
     bool is_10bit) {
   constexpr base::TimeDelta kNullTimestamp;
 
-  scoped_refptr<VideoFrame> frame = CreateGmbOrMappableSIVideoFrame(
+  scoped_refptr<VideoFrame> frame = CreateMappableVideoFrame(
       is_10bit ? VideoPixelFormat::PIXEL_FORMAT_XR30
                : VideoPixelFormat::PIXEL_FORMAT_ARGB,
       coded_size, gfx::Rect(coded_size), coded_size, kNullTimestamp,
diff --git a/media/gpu/media_gpu_export.h b/media/gpu/media_gpu_export.h
index 8c364dc..7ed33b7 100644
--- a/media/gpu/media_gpu_export.h
+++ b/media/gpu/media_gpu_export.h
@@ -18,11 +18,7 @@
 #endif  // defined(MEDIA_GPU_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(MEDIA_GPU_IMPLEMENTATION)
 #define MEDIA_GPU_EXPORT __attribute__((visibility("default")))
-#else
-#define MEDIA_GPU_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/media/gpu/test/video_player/test_vda_video_decoder.cc b/media/gpu/test/video_player/test_vda_video_decoder.cc
index 99a9d27..5f6b76d 100644
--- a/media/gpu/test/video_player/test_vda_video_decoder.cc
+++ b/media/gpu/test/video_player/test_vda_video_decoder.cc
@@ -211,7 +211,7 @@
   // Create a video frame for each of the picture buffers and provide memory
   // handles to the video frame's data to the decoder.
   for (const PictureBuffer& picture_buffer : picture_buffers) {
-    scoped_refptr<VideoFrame> video_frame = CreateGmbOrMappableSIVideoFrame(
+    scoped_refptr<VideoFrame> video_frame = CreateMappableVideoFrame(
         format, dimensions, visible_rect, visible_rect.size(),
         base::TimeDelta(),
         linear_output_ ? gfx::BufferUsage::SCANOUT_CPU_READ_WRITE
diff --git a/media/gpu/v4l2/BUILD.gn b/media/gpu/v4l2/BUILD.gn
index 43bcf037..f5dcc83 100644
--- a/media/gpu/v4l2/BUILD.gn
+++ b/media/gpu/v4l2/BUILD.gn
@@ -100,6 +100,7 @@
     "//gpu/ipc/service",
     "//media",
     "//media/gpu:buildflags",
+    "//media/gpu:command_buffer_helper",
     "//media/gpu:common",
     "//media/gpu:video_frame_mapper_common",
     "//media/gpu/chromeos:common",
diff --git a/media/gpu/v4l2/v4l2_video_encode_accelerator.cc b/media/gpu/v4l2/v4l2_video_encode_accelerator.cc
index bedb24a..e23cb08 100644
--- a/media/gpu/v4l2/v4l2_video_encode_accelerator.cc
+++ b/media/gpu/v4l2/v4l2_video_encode_accelerator.cc
@@ -36,6 +36,8 @@
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
 #include "base/trace_event/trace_event.h"
+#include "gpu/ipc/service/gpu_channel_shared_image_interface.h"
+#include "gpu/ipc/service/shared_image_stub.h"
 #include "media/base/bitstream_buffer.h"
 #include "media/base/color_plane_layout.h"
 #include "media/base/encoder_status.h"
@@ -47,6 +49,7 @@
 #include "media/gpu/chromeos/fourcc.h"
 #include "media/gpu/chromeos/image_processor_factory.h"
 #include "media/gpu/chromeos/platform_video_frame_utils.h"
+#include "media/gpu/command_buffer_helper.h"
 #include "media/gpu/gpu_video_encode_accelerator_helpers.h"
 #include "media/gpu/macros.h"
 #include "media/gpu/v4l2/v4l2_utils.h"
@@ -555,11 +558,12 @@
   for (size_t i = 0; i < count; i++) {
     switch (output_config.storage_type) {
       case VideoFrame::STORAGE_GPU_MEMORY_BUFFER:
-        image_processor_output_buffers_[i] = CreateGmbOrMappableSIVideoFrame(
+        CHECK(sii_);
+        image_processor_output_buffers_[i] = CreateMappableVideoFrame(
             output_config.fourcc.ToVideoPixelFormat(), output_config.size,
             output_config.visible_rect, output_config.visible_rect.size(),
             base::TimeDelta(),
-            gfx::BufferUsage::VEA_READ_CAMERA_AND_CPU_READ_WRITE);
+            gfx::BufferUsage::VEA_READ_CAMERA_AND_CPU_READ_WRITE, sii_.get());
         break;
       default:
         LOG(ERROR) << "Unsupported output storage type of image processor: "
@@ -705,6 +709,33 @@
   return is_flush_supported_;
 }
 
+void V4L2VideoEncodeAccelerator::OnSharedImageInterfaceAvailable(
+    scoped_refptr<gpu::SharedImageInterface> sii) {
+  sii_ = std::move(sii);
+}
+
+void V4L2VideoEncodeAccelerator::SetCommandBufferHelperCB(
+    base::RepeatingCallback<scoped_refptr<CommandBufferHelper>()>
+        get_command_buffer_helper_cb,
+    scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner) {
+  gpu_task_runner->PostTaskAndReplyWithResult(
+      FROM_HERE,
+      base::BindOnce(
+          [](base::RepeatingCallback<scoped_refptr<CommandBufferHelper>()>
+                 get_command_buffer_helper_cb)
+              -> scoped_refptr<gpu::SharedImageInterface> {
+            auto helper = get_command_buffer_helper_cb.Run();
+            if (helper && helper->GetSharedImageStub()) {
+              return helper->GetSharedImageStub()->shared_image_interface();
+            }
+            return nullptr;
+          },
+          get_command_buffer_helper_cb),
+      base::BindOnce(
+          &V4L2VideoEncodeAccelerator::OnSharedImageInterfaceAvailable,
+          weak_this_));
+}
+
 VideoEncodeAccelerator::SupportedProfiles
 V4L2VideoEncodeAccelerator::GetSupportedProfiles() {
   auto device = base::MakeRefCounted<V4L2Device>();
diff --git a/media/gpu/v4l2/v4l2_video_encode_accelerator.h b/media/gpu/v4l2/v4l2_video_encode_accelerator.h
index 0fd38f1..914a665 100644
--- a/media/gpu/v4l2/v4l2_video_encode_accelerator.h
+++ b/media/gpu/v4l2/v4l2_video_encode_accelerator.h
@@ -21,6 +21,8 @@
 #include "base/sequence_checker.h"
 #include "base/threading/thread.h"
 #include "base/time/time.h"
+#include "gpu/command_buffer/client/shared_image_interface.h"
+#include "gpu/ipc/service/command_buffer_stub.h"
 #include "media/base/encoder_status.h"
 #include "media/gpu/chromeos/image_processor.h"
 #include "media/gpu/media_gpu_export.h"
@@ -71,6 +73,10 @@
   void Destroy() override;
   void Flush(FlushCallback flush_callback) override;
   bool IsFlushSupported() override;
+  void SetCommandBufferHelperCB(
+      base::RepeatingCallback<scoped_refptr<CommandBufferHelper>()>
+          get_command_buffer_helper_cb,
+      scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner) override;
 
  private:
   // Auto-destroy reference for BitstreamBuffer, for tracking buffers passed to
@@ -276,6 +282,9 @@
   // Initializes input_memory_type_.
   bool InitInputMemoryType(const Config& config);
 
+  void OnSharedImageInterfaceAvailable(
+      scoped_refptr<gpu::SharedImageInterface> sii);
+
   // Having too many encoder instances at once may cause us to run out of FDs
   // and subsequently crash (crbug.com/1289465). To avoid that, we limit the
   // maximum number of encoder instances that can exist at once.
@@ -388,6 +397,7 @@
   // |child_task_runner_|.
   base::WeakPtr<Client> client_;
   std::unique_ptr<base::WeakPtrFactory<Client>> client_ptr_factory_;
+  scoped_refptr<gpu::SharedImageInterface> sii_;
 
   // WeakPtr<> pointing to |this| for use in posting tasks to
   // |encoder_task_runner_|.
diff --git a/media/midi/midi_export.h b/media/midi/midi_export.h
index 705b238..bb4b533 100644
--- a/media/midi/midi_export.h
+++ b/media/midi/midi_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(MIDI_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(MIDI_IMPLEMENTATION)
 #define MIDI_EXPORT __attribute__((visibility("default")))
-#else
-#define MIDI_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/media/renderers/video_resource_updater.cc b/media/renderers/video_resource_updater.cc
index 627f399..9e8ed53 100644
--- a/media/renderers/video_resource_updater.cc
+++ b/media/renderers/video_resource_updater.cc
@@ -886,65 +886,54 @@
     int bits_per_channel,
     const gfx::ColorSpace& input_frame_color_space,
     bool& texture_needs_rgb_conversion_out) {
-  // TODO(crbug.com/332564976, hitawala): Simplify this format conversion
-  // process.
   if (software_compositor()) {
     return viz::SinglePlaneFormat::kBGRA_8888;
   }
-
-  viz::SharedImageFormat output_si_format;
   if (IsFrameFormat32BitRGB(input_frame_format)) {
-    texture_needs_rgb_conversion_out = false;
-    output_si_format = GetRGBSharedImageFormat(input_frame_format);
-  } else if (input_frame_format == PIXEL_FORMAT_Y16) {
+    return GetRGBSharedImageFormat(input_frame_format);
+  }
+
+  if (input_frame_format == PIXEL_FORMAT_Y16) {
     // Unable to display directly as yuv planes so convert it to RGB.
     texture_needs_rgb_conversion_out = true;
-  } else if (context_provider_->ContextCapabilities()
-                 .disable_one_component_textures) {
+    return PaintCanvasVideoRenderer::GetRGBPixelsOutputFormat();
+  }
+  const auto& caps = context_provider_->ContextCapabilities();
+  if (caps.disable_one_component_textures) {
     // If GPU compositing is enabled, we need to convert texture to RGB if one
     // component textures are disabled.
     texture_needs_rgb_conversion_out = true;
-  } else {
-    // Can be composited directly from yuv planes.
-    output_si_format = YuvSharedImageFormat(bits_per_channel);
+    return PaintCanvasVideoRenderer::GetRGBPixelsOutputFormat();
+  }
 
-    // Some YUV resources have different sized planes. If we lack the proper
-    // SharedImageFormat just convert to RGB. We could do something better like
-    // unpacking to I420/I016, but texture_rg and r16 support should be pretty
-    // universal and we expect these frames to be rare.
-    if (input_frame_format == PIXEL_FORMAT_NV12) {
-      if (output_si_format != viz::SinglePlaneFormat::kR_8) {
-        texture_needs_rgb_conversion_out = true;
-      }
-    } else {
-      DCHECK_EQ(VideoFrame::BytesPerElement(input_frame_format, 0),
-                VideoFrame::BytesPerElement(input_frame_format, 1));
-    }
-
-    // If it is multiplanar and does not need RGB conversion, go through
-    // RasterDecoder WritePixelsYUV path.
-    if (!texture_needs_rgb_conversion_out) {
-      // Get the supported channel format for the `output_si_format`'s first
-      // plane.
-      auto channel_format = SupportedMultiPlaneChannelFormat(output_si_format);
-      // Now get the multiplanar shared image format for `input_frame_format`.
-      output_si_format =
-          VideoPixelFormatToMultiPlanarSharedImageFormat(input_frame_format);
-      if (output_si_format.channel_format() != channel_format) {
-        // If the requested channel format is not supported, use the supported
-        // channel format and downsample later if needed.
-        output_si_format = viz::SharedImageFormat::MultiPlane(
-            output_si_format.plane_config(), output_si_format.subsampling(),
-            channel_format);
-      }
+  // Get the multiplanar shared image format for `input_frame_format`.
+  auto yuv_si_format =
+      VideoPixelFormatToMultiPlanarSharedImageFormat(input_frame_format);
+  if (yuv_si_format.plane_config() ==
+      viz::SharedImageFormat::PlaneConfig::kY_UV) {
+    // Only 8-bit formats are supported with UV planes for software decoding.
+    CHECK_EQ(yuv_si_format.channel_format(),
+             viz::SharedImageFormat::ChannelFormat::k8);
+    const auto& shared_image_caps =
+        context_provider_->SharedImageInterface()->GetCapabilities();
+    // Two channel formats are supported only with texture_rg.
+    if (!caps.texture_rg || shared_image_caps.disable_r8_shared_images) {
+      texture_needs_rgb_conversion_out = true;
+      return PaintCanvasVideoRenderer::GetRGBPixelsOutputFormat();
     }
   }
 
-  if (texture_needs_rgb_conversion_out) {
-    output_si_format = PaintCanvasVideoRenderer::GetRGBPixelsOutputFormat();
+  // Get the supported channel format for `yuv_si_format`'s first plane.
+  auto channel_format =
+      SupportedMultiPlaneChannelFormat(YuvSharedImageFormat(bits_per_channel));
+  if (yuv_si_format.channel_format() != channel_format) {
+    // If the requested channel format is not supported, use the supported
+    // channel format and downsample later if needed.
+    yuv_si_format = viz::SharedImageFormat::MultiPlane(
+        yuv_si_format.plane_config(), yuv_si_format.subsampling(),
+        channel_format);
   }
-
-  return output_si_format;
+  return yuv_si_format;
 }
 
 void VideoResourceUpdater::TransferRGBPixelsToPaintCanvas(
diff --git a/mojo/core/system_impl_export.h b/mojo/core/system_impl_export.h
index 40e51cdd..717f442 100644
--- a/mojo/core/system_impl_export.h
+++ b/mojo/core/system_impl_export.h
@@ -18,11 +18,7 @@
 #endif  // defined(MOJO_SYSTEM_IMPL_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(MOJO_SYSTEM_IMPL_IMPLEMENTATION)
 #define MOJO_SYSTEM_IMPL_EXPORT __attribute__((visibility("default")))
-#else
-#define MOJO_SYSTEM_IMPL_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/mojo/public/c/system/system_export.h b/mojo/public/c/system/system_export.h
index 1bf5d5a..6529e192 100644
--- a/mojo/public/c/system/system_export.h
+++ b/mojo/public/c/system/system_export.h
@@ -16,11 +16,7 @@
 
 #else  // !defined(WIN32)
 
-#if defined(MOJO_SYSTEM_IMPLEMENTATION)
 #define MOJO_SYSTEM_EXPORT __attribute__((visibility("default")))
-#else
-#define MOJO_SYSTEM_EXPORT
-#endif
 
 #endif  // defined(WIN32)
 
diff --git a/mojo/public/cpp/bindings/tests/mojo_test_blink_export.h b/mojo/public/cpp/bindings/tests/mojo_test_blink_export.h
index 864d2190..0295395 100644
--- a/mojo/public/cpp/bindings/tests/mojo_test_blink_export.h
+++ b/mojo/public/cpp/bindings/tests/mojo_test_blink_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(MOJO_TEST_BLINK_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(MOJO_TEST_BLINK_IMPLEMENTATION)
 #define MOJO_TEST_BLINK_EXPORT __attribute__((visibility("default")))
-#else
-#define MOJO_TEST_BLINK_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/mojo/public/cpp/bindings/tests/mojo_test_export.h b/mojo/public/cpp/bindings/tests/mojo_test_export.h
index 802f808..c872602 100644
--- a/mojo/public/cpp/bindings/tests/mojo_test_export.h
+++ b/mojo/public/cpp/bindings/tests/mojo_test_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(MOJO_TEST_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(MOJO_TEST_IMPLEMENTATION)
 #define MOJO_TEST_EXPORT __attribute__((visibility("default")))
-#else
-#define MOJO_TEST_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/net/base/net_export.h b/net/base/net_export.h
index 35e1773..ca64828 100644
--- a/net/base/net_export.h
+++ b/net/base/net_export.h
@@ -21,13 +21,8 @@
 #endif  // defined(NET_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(NET_IMPLEMENTATION)
 #define NET_EXPORT __attribute__((visibility("default")))
 #define NET_EXPORT_PRIVATE __attribute__((visibility("default")))
-#else
-#define NET_EXPORT
-#define NET_EXPORT_PRIVATE
-#endif
 #endif
 
 #else  /// defined(COMPONENT_BUILD)
diff --git a/net/http/http_stream_pool_attempt_manager.cc b/net/http/http_stream_pool_attempt_manager.cc
index be2d487..aa05b77 100644
--- a/net/http/http_stream_pool_attempt_manager.cc
+++ b/net/http/http_stream_pool_attempt_manager.cc
@@ -1666,19 +1666,8 @@
     StreamSocketCloseReason refresh_group_reason) {
   CHECK(!is_failing_);
   CHECK(!quic_attempt_);
-  // TODO(crbug.com/415127271): Remove debug alias and change CHECK to DCHECK
-  // once we identify why this doesn't always hold.
-  bool is_ip_based_pooling_enabled = IsIpBasedPoolingEnabled();
-  bool is_alternative_service_enabled = IsAlternativeServiceEnabled();
-  bool requires_h1 = RequiresHTTP11();
-  QuicChromiumClientSession* quic_session =
-      quic_session_pool()->FindExistingSession(
-          quic_session_alias_key().session_key(),
-          quic_session_alias_key().destination());
-  base::debug::Alias(&is_ip_based_pooling_enabled);
-  base::debug::Alias(&is_alternative_service_enabled);
-  base::debug::Alias(&requires_h1);
-  base::debug::Alias(quic_session);
+  // TODO(crbug.com/415488524): Change to DCHECK once we confirm the bug is
+  // fixed.
   CHECK(CanUseExistingQuicSession());
 
   TRACE_EVENT_INSTANT("net.stream", "AttemptManager::QuicSessionReady", track_);
diff --git a/net/http/http_stream_pool_attempt_manager_quic_attempt.cc b/net/http/http_stream_pool_attempt_manager_quic_attempt.cc
index 38bd84be..0e5660c 100644
--- a/net/http/http_stream_pool_attempt_manager_quic_attempt.cc
+++ b/net/http/http_stream_pool_attempt_manager_quic_attempt.cc
@@ -148,11 +148,8 @@
 void HttpStreamPool::AttemptManager::QuicAttempt::OnSessionAttemptComplete(
     int rv) {
   if (rv == OK) {
-    QuicChromiumClientSession* session =
-        GetQuicSessionPool()->FindExistingSession(GetKey().session_key(),
-                                                  GetKey().destination());
-    if (!session) {
-      // QUIC session is closed before stream can be created.
+    if (!manager_->CanUseExistingQuicSession()) {
+      // QUIC session is closed or marked broken before stream can be created.
       rv = ERR_CONNECTION_CLOSED;
     }
   }
diff --git a/net/http/http_stream_pool_attempt_manager_unittest.cc b/net/http/http_stream_pool_attempt_manager_unittest.cc
index 22c51ab5c..f276dc2 100644
--- a/net/http/http_stream_pool_attempt_manager_unittest.cc
+++ b/net/http/http_stream_pool_attempt_manager_unittest.cc
@@ -7487,4 +7487,41 @@
   EXPECT_THAT(requester.result(), Optional(IsOk()));
 }
 
+// Regression test for crbug.com/415488524. A QUIC destination may be marked
+// broken after a successful QUIC session attempt. Ensure that a request
+// doesn't use QUIC in a such situation.
+TEST_F(HttpStreamPoolAttemptManagerTest, QuicBrokenWhenSessionCreated) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(net::features::kAsyncQuicSession);
+
+  resolver()
+      ->AddFakeRequest()
+      ->add_endpoint(ServiceEndpointBuilder().add_v4("192.0.2.1").endpoint())
+      .CompleteStartSynchronously(OK);
+
+  MockConnectCompleter quic_completer;
+  AddQuicData(/*host=*/kDefaultDestination, &quic_completer);
+
+  SequencedSocketData tcp_data;
+  socket_factory()->AddSocketDataProvider(&tcp_data);
+  SSLSocketDataProvider ssl(ASYNC, OK);
+  socket_factory()->AddSSLSocketDataProvider(&ssl);
+
+  StreamRequester requester;
+  requester.set_destination(kDefaultDestination)
+      .set_quic_version(quic_version())
+      .RequestStream(pool());
+  ASSERT_FALSE(requester.result().has_value());
+
+  AlternativeService alternative_service(NextProto::kProtoQUIC,
+                                         "www.example.org", 443);
+  http_server_properties()->MarkAlternativeServiceBroken(
+      alternative_service, NetworkAnonymizationKey());
+
+  quic_completer.Complete(OK);
+  requester.WaitForResult();
+  EXPECT_THAT(requester.result(), Optional(IsOk()));
+  EXPECT_NE(requester.negotiated_protocol(), NextProto::kProtoQUIC);
+}
+
 }  // namespace net
diff --git a/net/log/net_log.cc b/net/log/net_log.cc
index f05cd9c..fb5a17ad 100644
--- a/net/log/net_log.cc
+++ b/net/log/net_log.cc
@@ -255,10 +255,11 @@
                              "after NextID() or called multiple times";
 }
 
-void NetLog::AddEntryInternal(NetLogEventType type,
-                              const NetLogSource& source,
-                              NetLogEventPhase phase,
-                              const GetParamsInterface* get_params) {
+void NetLog::AddEntryInternal(
+    NetLogEventType type,
+    const NetLogSource& source,
+    NetLogEventPhase phase,
+    base::FunctionRef<base::Value::Dict(NetLogCaptureMode)> get_params) {
   NetLogCaptureModeSet observer_capture_modes = GetObserverCaptureModes();
 
   for (int i = 0; i <= static_cast<int>(NetLogCaptureMode::kLast); ++i) {
@@ -266,7 +267,7 @@
     if (!NetLogCaptureModeSetContains(capture_mode, observer_capture_modes))
       continue;
 
-    base::Value::Dict params = get_params->GetParams(capture_mode);
+    base::Value::Dict params = get_params(capture_mode);
     if (capture_mode == NetLogCaptureMode::kHeavilyRedacted) {
       HeavilyRedactParams(params);
     }
diff --git a/net/log/net_log.h b/net/log/net_log.h
index 45f169d..bebc9d8 100644
--- a/net/log/net_log.h
+++ b/net/log/net_log.h
@@ -13,6 +13,7 @@
 #include <vector>
 
 #include "base/compiler_specific.h"
+#include "base/functional/function_ref.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/raw_ref.h"
 #include "base/synchronization/lock.h"
@@ -235,22 +236,7 @@
       return;
     }
 
-    // Indirect through virtual dispatch to reduce code bloat, as this is
-    // inlined in a number of places.
-    class GetParamsImpl : public GetParamsInterface {
-     public:
-      explicit GetParamsImpl(const ParametersCallback& get_params)
-          : get_params_(get_params) {}
-      base::Value::Dict GetParams(NetLogCaptureMode mode) const override {
-        return (*get_params_)(mode);
-      }
-
-     private:
-      const raw_ref<const ParametersCallback> get_params_;
-    };
-
-    GetParamsImpl wrapper(get_params);
-    AddEntryInternal(type, source, phase, &wrapper);
+    AddEntryInternal(type, source, phase, get_params);
   }
 
   // Emits a global event to the log stream, with its own unique source ID.
@@ -336,18 +322,13 @@
   static const char* EventPhaseToString(NetLogEventPhase event_phase);
 
  private:
-  class GetParamsInterface {
-   public:
-    virtual base::Value::Dict GetParams(NetLogCaptureMode mode) const = 0;
-    virtual ~GetParamsInterface() = default;
-  };
-
   // Helper for implementing AddEntry() that indirects parameter getting through
   // virtual dispatch.
-  void AddEntryInternal(NetLogEventType type,
-                        const NetLogSource& source,
-                        NetLogEventPhase phase,
-                        const GetParamsInterface* get_params);
+  NOINLINE void AddEntryInternal(
+      NetLogEventType type,
+      const NetLogSource& source,
+      NetLogEventPhase phase,
+      base::FunctionRef<base::Value::Dict(NetLogCaptureMode)> get_params);
 
   // Returns the set of all capture modes being observed.
   NetLogCaptureModeSet GetObserverCaptureModes() const {
diff --git a/net/quic/dedicated_web_transport_http3_client.cc b/net/quic/dedicated_web_transport_http3_client.cc
index b1c857e..9879fd4 100644
--- a/net/quic/dedicated_web_transport_http3_client.cc
+++ b/net/quic/dedicated_web_transport_http3_client.cc
@@ -28,6 +28,7 @@
 #include "net/third_party/quiche/src/quiche/quic/core/quic_connection.h"
 #include "net/third_party/quiche/src/quiche/quic/core/quic_types.h"
 #include "net/third_party/quiche/src/quiche/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quiche/web_transport/web_transport_headers.h"
 #include "net/url_request/url_request_context.h"
 #include "url/scheme_host_port.h"
 
@@ -365,6 +366,7 @@
     : url_(url),
       origin_(origin),
       anonymization_key_(anonymization_key),
+      application_protocols_(parameters.application_protocols),
       context_(context),
       visitor_(visitor),
       quic_context_(context->quic_context()),
@@ -748,6 +750,14 @@
   headers[":protocol"] = "webtransport";
   headers["sec-webtransport-http3-draft02"] = "1";
   headers["origin"] = origin_.Serialize();
+  if (!application_protocols_.empty()) {
+    absl::StatusOr<std::string> protocols_header =
+        webtransport::SerializeSubprotocolRequestHeader(application_protocols_);
+    if (protocols_header.ok()) {
+      headers[webtransport::kSubprotocolRequestHeader] =
+          *std::move(protocols_header);
+    }
+  }
   stream->WriteHeaders(std::move(headers), /*fin=*/false, nullptr);
 
   web_transport_session_ = stream->web_transport();
diff --git a/net/quic/dedicated_web_transport_http3_client.h b/net/quic/dedicated_web_transport_http3_client.h
index 30d000ce..516bef6 100644
--- a/net/quic/dedicated_web_transport_http3_client.h
+++ b/net/quic/dedicated_web_transport_http3_client.h
@@ -150,6 +150,7 @@
   const GURL url_;
   const url::Origin origin_;
   const NetworkAnonymizationKey anonymization_key_;
+  const std::vector<std::string> application_protocols_;
   const raw_ptr<URLRequestContext> context_;          // Unowned.
   const raw_ptr<WebTransportClientVisitor> visitor_;  // Unowned.
 
diff --git a/net/quic/dedicated_web_transport_http3_client_test.cc b/net/quic/dedicated_web_transport_http3_client_test.cc
index de28319..26adffd 100644
--- a/net/quic/dedicated_web_transport_http3_client_test.cc
+++ b/net/quic/dedicated_web_transport_http3_client_test.cc
@@ -210,7 +210,7 @@
       GetURL("/echo"), origin_, &visitor_, anonymization_key_, context_.get(),
       WebTransportParameters());
 
-  EXPECT_CALL(visitor_, OnConnected(_)).WillOnce(StopRunning());
+  EXPECT_CALL(visitor_, OnConnected).WillOnce(StopRunning());
   client_->Connect();
   Run();
   ASSERT_TRUE(client_->session() != nullptr);
@@ -251,7 +251,7 @@
       GetURL("/echo"), origin_, &visitor_, anonymization_key_, context_.get(),
       WebTransportParameters());
 
-  EXPECT_CALL(visitor_, OnConnected(_)).WillOnce(StopRunning());
+  EXPECT_CALL(visitor_, OnConnected).WillOnce(StopRunning());
   client_->Connect();
   Run();
   ASSERT_TRUE(client_->session() != nullptr);
@@ -277,7 +277,7 @@
       GetURL("/session-close"), origin_, &visitor_, anonymization_key_,
       context_.get(), WebTransportParameters());
 
-  EXPECT_CALL(visitor_, OnConnected(_)).WillOnce(StopRunning());
+  EXPECT_CALL(visitor_, OnConnected).WillOnce(StopRunning());
   client_->Connect();
   Run();
   ASSERT_TRUE(client_->session() != nullptr);
@@ -296,5 +296,42 @@
   EXPECT_THAT(received_close_info, Optional(close_info));
 }
 
+// Test negotiation of the application protocol via
+// https://www.ietf.org/archive/id/draft-ietf-webtrans-http3-12.html#name-application-protocol-negoti
+TEST_F(DedicatedWebTransportHttp3Test, SubprotocolHeader) {
+  StartServer();
+  WebTransportParameters parameters;
+  parameters.application_protocols = {"first", "second", "third"};
+  // The selected-subprotocol endpoint selects the first of the offered
+  // protocols by default, and echoes it on a unidirectional stream.
+  client_ = std::make_unique<DedicatedWebTransportHttp3Client>(
+      GetURL("/selected-subprotocol"), origin_, &visitor_, anonymization_key_,
+      context_.get(), parameters);
+
+  bool stream_received = false;
+  EXPECT_CALL(visitor_, OnConnected).WillOnce(StopRunning());
+  EXPECT_CALL(visitor_, OnIncomingUnidirectionalStreamAvailable).WillOnce([&] {
+    stream_received = true;
+    StopRunning();
+  });
+  client_->Connect();
+  Run();
+  ASSERT_TRUE(client_->session() != nullptr);
+
+  EXPECT_EQ(client_->session()->GetNegotiatedSubprotocol(), "first");
+
+  if (!stream_received) {
+    Run();
+  }
+
+  quic::WebTransportStream* stream =
+      client_->session()->AcceptIncomingUnidirectionalStream();
+  ASSERT_TRUE(stream != nullptr);
+  std::string read_buffer;
+  webtransport::Stream::ReadResult read_result = stream->Read(&read_buffer);
+  ASSERT_TRUE(read_result.fin);
+  EXPECT_EQ(read_buffer, "first");
+}
+
 }  // namespace
 }  // namespace net::test
diff --git a/net/quic/web_transport_client.h b/net/quic/web_transport_client.h
index f597a4dc..7b7e187 100644
--- a/net/quic/web_transport_client.h
+++ b/net/quic/web_transport_client.h
@@ -106,9 +106,13 @@
   bool enable_web_transport_http3 = false;
 
   // A vector of fingerprints for expected server certificates, as described in
-  // https://wicg.github.io/web-transport/#dom-quictransportconfiguration-server_certificate_fingerprints
+  // https://w3c.github.io/webtransport/#dom-webtransportoptions-servercertificatehashes
   // When empty, Web PKI is used.
   std::vector<quic::CertificateFingerprint> server_certificate_fingerprints;
+
+  // A vector of strings offered by client as a list of potential subprotocols.
+  // https://w3c.github.io/webtransport/#dom-webtransportoptions-protocols
+  std::vector<std::string> application_protocols;
 };
 
 // An abstract base for a WebTransport client.  Most of the useful operations
diff --git a/pdf/pdf_ink_module.cc b/pdf/pdf_ink_module.cc
index 28bb1812..2af8a969 100644
--- a/pdf/pdf_ink_module.cc
+++ b/pdf/pdf_ink_module.cc
@@ -54,6 +54,7 @@
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/base/cursor/cursor.h"
+#include "ui/base/cursor/mojom/cursor_type.mojom.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/geometry/point_conversions.h"
 #include "ui/gfx/geometry/point_f.h"
@@ -370,6 +371,10 @@
 }
 
 void PdfInkModule::OnGeometryChanged() {
+  // If the highlighter tool is selected, and zooming moves the cursor onto
+  // text, the cursor should be an I-beam, but it will instead be the drawing
+  // cursor until a mousemove event occurs. There is not a way to get the new
+  // mouse position on geometry change.
   MaybeSetCursor();
 }
 
@@ -470,11 +475,11 @@
     return false;
   }
 
+  gfx::PointF position = event.PositionInWidget();
   if (features::kPdfInk2TextHighlighting.Get() && is_text_highlighting()) {
-    return FinishTextHighlight();
+    return FinishTextHighlight(position);
   }
 
-  gfx::PointF position = event.PositionInWidget();
   return is_drawing_stroke()
              ? FinishStroke(position, event.TimeStamp(),
                             ink::StrokeInput::ToolType::kMouse)
@@ -484,10 +489,8 @@
 bool PdfInkModule::OnMouseMove(const blink::WebMouseEvent& event) {
   CHECK(enabled());
 
-  // TODO(crbug.com/342445982): Set the cursor for hovering over text with the
-  // highlighter brush while not drawing.
-
   gfx::PointF position = event.PositionInWidget();
+
   bool still_interacting_with_ink =
       event.GetModifiers() & blink::WebInputEvent::kLeftButtonDown;
   if (still_interacting_with_ink) {
@@ -507,6 +510,7 @@
   // that now, and compensate by synthesizing a mouse-up input event at the
   // last known input position.  Intentionally do not use `position`.
   if (is_drawing_stroke()) {
+    MaybeSetCursorOnMouseMove(position);
     DrawingStrokeState& state = drawing_stroke_state();
     if (!state.input_last_event.has_value()) {
       // Ignore when not drawing.
@@ -772,7 +776,12 @@
   state.page_index = -1;
   state.input_last_event.reset();
 
-  if (MaybeSetDrawingBrush()) {
+  bool set_drawing_brush = MaybeSetDrawingBrush();
+  if (features::kPdfInk2TextHighlighting.Get() &&
+      state.brush_type == PdfInkBrush::Type::kHighlighter &&
+      client_->IsSelectableTextOrLinkArea(position)) {
+    client_->UpdateInkCursor(ui::mojom::CursorType::kIBeam);
+  } else if (set_drawing_brush) {
     MaybeSetCursor();
   }
 
@@ -984,7 +993,7 @@
   return true;
 }
 
-bool PdfInkModule::FinishTextHighlight() {
+bool PdfInkModule::FinishTextHighlight(const gfx::PointF& position) {
   CHECK(is_text_highlighting());
 
   auto& highlight_strokes = text_highlight_state().highlight_strokes;
@@ -1018,6 +1027,10 @@
   drawing_stroke_state().brush_type = PdfInkBrush::Type::kHighlighter;
 
   client_->ClearSelection();
+
+  if (!client_->IsSelectableTextOrLinkArea(position)) {
+    MaybeSetCursor();
+  }
   return true;
 }
 
@@ -1609,7 +1622,6 @@
   }
 
   if (features::kPdfInk2TextHighlighting.Get() && is_text_highlighting()) {
-    // TODO(crbug.com/342445982): Set the cursor for text highlighting.
     return;
   }
 
@@ -1633,6 +1645,25 @@
       ui::Cursor::NewCustom(std::move(bitmap), std::move(hotspot)));
 }
 
+void PdfInkModule::MaybeSetCursorOnMouseMove(const gfx::PointF& position) {
+  if (!features::kPdfInk2TextHighlighting.Get()) {
+    return;
+  }
+
+  CHECK(is_drawing_stroke());
+  if (drawing_stroke_state().brush_type != PdfInkBrush::Type::kHighlighter ||
+      !client_->IsSelectableTextOrLinkArea(position)) {
+    if (client_->GetCursor().type() == ui::mojom::CursorType::kIBeam) {
+      MaybeSetCursor();
+    }
+    return;
+  }
+
+  if (client_->GetCursor().type() != ui::mojom::CursorType::kIBeam) {
+    client_->UpdateInkCursor(ui::mojom::CursorType::kIBeam);
+  }
+}
+
 PdfInkModule::DrawingStrokeState::DrawingStrokeState() = default;
 
 PdfInkModule::DrawingStrokeState::~DrawingStrokeState() = default;
diff --git a/pdf/pdf_ink_module.h b/pdf/pdf_ink_module.h
index 14ff977..45adf9d 100644
--- a/pdf/pdf_ink_module.h
+++ b/pdf/pdf_ink_module.h
@@ -356,7 +356,7 @@
                           int click_count,
                           base::TimeTicks timestamp);
   bool ContinueTextHighlight(const gfx::PointF& position);
-  bool FinishTextHighlight();
+  bool FinishTextHighlight(const gfx::PointF& position);
 
   // Returns a highlighter stroke that matches the position and size of
   // `selection_rect`. `selection_rect` must be in screen coordinates.
@@ -455,8 +455,14 @@
   void ApplyUndoRedoDiscards(
       const PdfInkUndoRedoModel::DiscardedDrawCommands& discards);
 
+  // Sets the cursor to a drawing/erasing brush cursor when necessary.
   void MaybeSetCursor();
 
+  // Handles setting the cursor only for mousemove events at `position`. This
+  // differs from `MaybeSetCursor()` in that it may also set the cursor to an
+  // I-beam for text highlighting.
+  void MaybeSetCursorOnMouseMove(const gfx::PointF& position);
+
   // Returns whether the drawing brush was set or not.
   bool MaybeSetDrawingBrush();
 
diff --git a/pdf/pdf_ink_module_client.h b/pdf/pdf_ink_module_client.h
index 731e5b2..a4a2807 100644
--- a/pdf/pdf_ink_module_client.h
+++ b/pdf/pdf_ink_module_client.h
@@ -12,6 +12,7 @@
 #include "pdf/pdf_ink_ids.h"
 #include "pdf/ui/thumbnail.h"
 #include "third_party/ink/src/ink/geometry/partitioned_mesh.h"
+#include "ui/base/cursor/cursor.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/vector2d.h"
 
@@ -55,6 +56,9 @@
   // `point`. `point` must be in device coordinates.
   virtual void ExtendSelectionByPoint(const gfx::PointF& point) {}
 
+  // Returns the current cursor.
+  virtual ui::Cursor GetCursor() = 0;
+
   // Gets the current page orientation.
   virtual PageOrientation GetOrientation() const = 0;
 
diff --git a/pdf/pdf_ink_module_unittest.cc b/pdf/pdf_ink_module_unittest.cc
index 5a3225d3..434cdba 100644
--- a/pdf/pdf_ink_module_unittest.cc
+++ b/pdf/pdf_ink_module_unittest.cc
@@ -199,6 +199,13 @@
   return visible_stroke_shapes;
 }
 
+blink::WebMouseEvent CreateMouseMoveEventAtPoint(const gfx::PointF& point) {
+  return MouseEventBuilder()
+      .SetType(blink::WebInputEvent::Type::kMouseMove)
+      .SetPosition(point)
+      .Build();
+}
+
 blink::WebMouseEvent CreateMouseMoveWithLeftButtonEventAtPoint(
     const gfx::PointF& point) {
   return MouseEventBuilder()
@@ -261,6 +268,8 @@
               (const gfx::PointF& point),
               (override));
 
+  MOCK_METHOD(ui::Cursor, GetCursor, (), (override));
+
   PageOrientation GetOrientation() const override { return orientation_; }
 
   MOCK_METHOD(std::vector<gfx::Rect>, GetSelectionRects, (), (override));
@@ -3098,6 +3107,8 @@
         .WillRepeatedly(Return(selection_rects));
     EXPECT_CALL(client(), IsSelectableTextOrLinkArea(kStartPointInsidePage0))
         .WillRepeatedly(Return(true));
+    EXPECT_CALL(client(), IsSelectableTextOrLinkArea(kEndPointInsidePage0))
+        .WillRepeatedly(Return(true));
 
     EXPECT_CALL(client(), OnTextOrLinkAreaClick(kStartPointInsidePage0,
                                                 /*click_count=*/1));
@@ -3330,10 +3341,12 @@
       .WillRepeatedly(Return(selection_rects));
   EXPECT_CALL(client(), IsSelectableTextOrLinkArea(kStartPointInsidePage0))
       .WillRepeatedly(Return(true));
+  constexpr gfx::PointF kEndPoint2InsidePage0{25.0, 30.0};
+  EXPECT_CALL(client(), IsSelectableTextOrLinkArea(kEndPoint2InsidePage0))
+      .WillRepeatedly(Return(true));
 
   EXPECT_CALL(client(), OnTextOrLinkAreaClick(kStartPointInsidePage0,
                                               /*click_count=*/1));
-  constexpr gfx::PointF kEndPoint2InsidePage0{25.0, 30.0};
   EXPECT_CALL(client(), ExtendSelectionByPoint(kEndPoint2InsidePage0));
 
   // Apply a text highlight stroke at the given points.
@@ -3663,7 +3676,7 @@
   std::vector<gfx::Rect> selection_rects{gfx::Rect(9, 14, 5, 10)};
   EXPECT_CALL(client(), GetSelectionRects())
       .WillRepeatedly(Return(selection_rects));
-  EXPECT_CALL(client(), IsSelectableTextOrLinkArea(kMouseDownPoint))
+  EXPECT_CALL(client(), IsSelectableTextOrLinkArea(_))
       .WillRepeatedly(Return(true));
 
   // There should not be any text selection extension after the miss, as the
@@ -3673,6 +3686,173 @@
   RunStrokeMissedEndEventThenMouseMoveTest();
 }
 
+TEST_P(PdfInkModuleTextHighlightTest, CursorOnMouseMove) {
+  EnableAnnotationMode();
+  InitializeSimpleSinglePageBasicLayout();
+
+  // Select the pen tool with a "Light Red" color. The cursor should be the
+  // custom pen cursor.
+  EXPECT_CALL(client(),
+              UpdateInkCursor(CursorBitmapImageSizeEq(SkISize(8, 8))));
+
+  TestAnnotationBrushMessageParams params = {/*color_r=*/0xF2, /*color_g=*/0x8B,
+                                             /*color_b=*/0x82, /*size=*/6.0f};
+  SelectBrushTool(PdfInkBrush::Type::kPen, params);
+
+  // `kStartPointInsidePage0` will be the selectable text area position, while
+  // all other positions will be non-text areas.
+  EXPECT_CALL(client(), IsSelectableTextOrLinkArea(_))
+      .WillRepeatedly(Return(false));
+  EXPECT_CALL(client(), IsSelectableTextOrLinkArea(kStartPointInsidePage0))
+      .WillRepeatedly(Return(true));
+
+  // Move to a text position. The cursor should remain as the custom pen cursor.
+  blink::WebMouseEvent mouse_move_event =
+      CreateMouseMoveEventAtPoint(kEndPointInsidePage0);
+  // The event will be considered not handled, but the cursor will still update.
+  EXPECT_FALSE(ink_module().HandleInputEvent(mouse_move_event));
+
+  VerifyAndClearExpectations();
+
+  // Select the highlighter tool. The cursor should be the custom highlighter
+  // cursor.
+  EXPECT_CALL(client(),
+              UpdateInkCursor(CursorBitmapImageSizeEq(SkISize(10, 10))));
+  params.size = 8.0f;
+  SelectBrushTool(PdfInkBrush::Type::kHighlighter, params);
+
+  VerifyAndClearExpectations();
+
+  // Move to a text position. The cursor should be an I-beam.
+  EXPECT_CALL(client(),
+              UpdateInkCursor(ui::Cursor(ui::mojom::CursorType::kIBeam)));
+  mouse_move_event = CreateMouseMoveEventAtPoint(kStartPointInsidePage0);
+  EXPECT_FALSE(ink_module().HandleInputEvent(mouse_move_event));
+
+  VerifyAndClearExpectations();
+
+  // Move to a non-text position. The cursor should restore to the custom
+  // highlighter cursor.
+  {
+    InSequence seq;
+    EXPECT_CALL(client(), GetCursor())
+        .WillOnce(Return(ui::mojom::CursorType::kIBeam));
+    EXPECT_CALL(client(),
+                UpdateInkCursor(CursorBitmapImageSizeEq(SkISize(10, 10))));
+  }
+  mouse_move_event = CreateMouseMoveEventAtPoint(kEndPointInsidePage0);
+  EXPECT_FALSE(ink_module().HandleInputEvent(mouse_move_event));
+}
+
+TEST_P(PdfInkModuleTextHighlightTest, CursorOnMouseMoveWhileTextSelecting) {
+  EnableAnnotationMode();
+  InitializeSimpleSinglePageBasicLayout();
+
+  // Select the highlighter tool. The cursor should be the custom highlighter
+  // cursor.
+  EXPECT_CALL(client(),
+              UpdateInkCursor(CursorBitmapImageSizeEq(SkISize(10, 10))));
+  TestAnnotationBrushMessageParams params = {/*color_r=*/0xF2, /*color_g=*/0x8B,
+                                             /*color_b=*/0x82, /*size=*/8.0f};
+  SelectBrushTool(PdfInkBrush::Type::kHighlighter, params);
+
+  VerifyAndClearExpectations();
+
+  // `kStartPointInsidePage0` will be the selectable text area position, while
+  // all other positions will be non-text areas.
+  EXPECT_CALL(client(), IsSelectableTextOrLinkArea(_))
+      .WillRepeatedly(Return(false));
+  EXPECT_CALL(client(), IsSelectableTextOrLinkArea(kStartPointInsidePage0))
+      .WillRepeatedly(Return(true));
+
+  // Move to a text position. The cursor should be an I-beam.
+  EXPECT_CALL(client(),
+              UpdateInkCursor(ui::Cursor(ui::mojom::CursorType::kIBeam)));
+  blink::WebMouseEvent mouse_move_event =
+      CreateMouseMoveEventAtPoint(kStartPointInsidePage0);
+  EXPECT_FALSE(ink_module().HandleInputEvent(mouse_move_event));
+
+  VerifyAndClearExpectations();
+
+  // Start text highlighting and move to a non-text position. The cursor should
+  // remain as an I-beam.
+  EXPECT_CALL(client(), UpdateInkCursor(_)).Times(0);
+  blink::WebMouseEvent mouse_down_event =
+      MouseEventBuilder()
+          .CreateLeftClickAtPosition(kStartPointInsidePage0)
+          .Build();
+  EXPECT_TRUE(ink_module().HandleInputEvent(mouse_down_event));
+  mouse_move_event =
+      CreateMouseMoveWithLeftButtonEventAtPoint(kEndPointInsidePage0);
+  EXPECT_TRUE(ink_module().HandleInputEvent(mouse_move_event));
+
+  VerifyAndClearExpectations();
+
+  // End text highlighting. The cursor should restore to the custom highlighter
+  // cursor.
+  EXPECT_CALL(client(),
+              UpdateInkCursor(CursorBitmapImageSizeEq(SkISize(10, 10))));
+  blink::WebMouseEvent mouse_up_event =
+      MouseEventBuilder()
+          .CreateLeftMouseUpAtPosition(kEndPointInsidePage0)
+          .Build();
+  EXPECT_TRUE(ink_module().HandleInputEvent(mouse_up_event));
+}
+
+TEST_P(PdfInkModuleTextHighlightTest, CursorOnMouseMoveWhileBrushDrawing) {
+  EnableAnnotationMode();
+  InitializeSimpleSinglePageBasicLayout();
+
+  // Select the highlighter tool. The cursor should be the custom highlighter
+  // cursor.
+  EXPECT_CALL(client(),
+              UpdateInkCursor(CursorBitmapImageSizeEq(SkISize(10, 10))));
+  TestAnnotationBrushMessageParams params = {/*color_r=*/0xF2, /*color_g=*/0x8B,
+                                             /*color_b=*/0x82, /*size=*/8.0f};
+  SelectBrushTool(PdfInkBrush::Type::kHighlighter, params);
+
+  VerifyAndClearExpectations();
+
+  // `kEndPointInsidePage0` will be the selectable text area position, while
+  // all other positions will be non-text areas.
+  EXPECT_CALL(client(), IsSelectableTextOrLinkArea(_))
+      .WillRepeatedly(Return(false));
+  EXPECT_CALL(client(), IsSelectableTextOrLinkArea(kEndPointInsidePage0))
+      .WillRepeatedly(Return(true));
+
+  // Move to a non-text position. The cursor should remain as the custom
+  // highlighter cursor.
+  EXPECT_CALL(client(), UpdateInkCursor(_)).Times(0);
+  blink::WebMouseEvent mouse_move_event =
+      CreateMouseMoveEventAtPoint(kStartPointInsidePage0);
+  EXPECT_FALSE(ink_module().HandleInputEvent(mouse_move_event));
+
+  VerifyAndClearExpectations();
+
+  // Start drawing and move to a text position. The cursor should remain as the
+  // custom highlighter cursor.
+  EXPECT_CALL(client(), UpdateInkCursor(_)).Times(0);
+  blink::WebMouseEvent mouse_down_event =
+      MouseEventBuilder()
+          .CreateLeftClickAtPosition(kStartPointInsidePage0)
+          .Build();
+  EXPECT_TRUE(ink_module().HandleInputEvent(mouse_down_event));
+  mouse_move_event =
+      CreateMouseMoveWithLeftButtonEventAtPoint(kEndPointInsidePage0);
+  EXPECT_TRUE(ink_module().HandleInputEvent(mouse_move_event));
+
+  VerifyAndClearExpectations();
+
+  // End drawing. The cursor should be an I-beam.
+  EXPECT_CALL(client(),
+              UpdateInkCursor(ui::Cursor(ui::mojom::CursorType::kIBeam)));
+  blink::WebMouseEvent mouse_up_event =
+      MouseEventBuilder()
+          .CreateLeftMouseUpAtPosition(kEndPointInsidePage0)
+          .Build();
+  EXPECT_TRUE(ink_module().HandleInputEvent(mouse_up_event));
+}
+
 INSTANTIATE_TEST_SUITE_P(All,
                          PdfInkModuleTest,
                          testing::ValuesIn(GetAllInkTestVariations()));
diff --git a/pdf/pdf_view_web_plugin.cc b/pdf/pdf_view_web_plugin.cc
index 7eed5a2..bcb7097 100644
--- a/pdf/pdf_view_web_plugin.cc
+++ b/pdf/pdf_view_web_plugin.cc
@@ -300,6 +300,8 @@
     plugin_->engine_->ExtendSelectionByPoint(point);
   }
 
+  ui::Cursor GetCursor() override { return plugin_->cursor_; }
+
   PageOrientation GetOrientation() const override {
     return plugin_->engine_->GetCurrentOrientation();
   }
diff --git a/pdf/pdf_view_web_plugin_unittest.cc b/pdf/pdf_view_web_plugin_unittest.cc
index 9d6d226d..87ade8d6 100644
--- a/pdf/pdf_view_web_plugin_unittest.cc
+++ b/pdf/pdf_view_web_plugin_unittest.cc
@@ -3049,6 +3049,16 @@
   SendThumbnail(/*message_id=*/"foo", /*page_size=*/{50, 25});
 }
 
+TEST_P(PdfViewWebPluginInkTest, GetCursor) {
+  plugin_->set_cursor_type_for_testing(ui::mojom::CursorType::kPointer);
+  EXPECT_EQ(ui::mojom::CursorType::kPointer,
+            plugin_->ink_module_client_for_testing()->GetCursor().type());
+
+  plugin_->set_cursor_type_for_testing(ui::mojom::CursorType::kIBeam);
+  EXPECT_EQ(ui::mojom::CursorType::kIBeam,
+            plugin_->ink_module_client_for_testing()->GetCursor().type());
+}
+
 TEST_P(PdfViewWebPluginInkTest, UpdateCursor) {
   UpdatePluginGeometryWithoutWaiting(2.0f, {0, 0, 20, 20});
 
diff --git a/ppapi/host/ppapi_host_export.h b/ppapi/host/ppapi_host_export.h
index 6bab7e9a7..18c674a 100644
--- a/ppapi/host/ppapi_host_export.h
+++ b/ppapi/host/ppapi_host_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(PPAPI_HOST_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(PPAPI_HOST_IMPLEMENTATION)
 #define PPAPI_HOST_EXPORT __attribute__((visibility("default")))
-#else
-#define PPAPI_HOST_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/ppapi/proxy/ppapi_proxy_export.h b/ppapi/proxy/ppapi_proxy_export.h
index de76892..d5aa604 100644
--- a/ppapi/proxy/ppapi_proxy_export.h
+++ b/ppapi/proxy/ppapi_proxy_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(PPAPI_PROXY_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(PPAPI_PROXY_IMPLEMENTATION)
 #define PPAPI_PROXY_EXPORT __attribute__((visibility("default")))
-#else
-#define PPAPI_PROXY_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/ppapi/shared_impl/ppapi_shared_export.h b/ppapi/shared_impl/ppapi_shared_export.h
index dd3686d..e7308a2 100644
--- a/ppapi/shared_impl/ppapi_shared_export.h
+++ b/ppapi/shared_impl/ppapi_shared_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(PPAPI_SHARED_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(PPAPI_SHARED_IMPLEMENTATION)
 #define PPAPI_SHARED_EXPORT __attribute__((visibility("default")))
-#else
-#define PPAPI_SHARED_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/ppapi/thunk/ppapi_thunk_export.h b/ppapi/thunk/ppapi_thunk_export.h
index 9073469..7c13557 100644
--- a/ppapi/thunk/ppapi_thunk_export.h
+++ b/ppapi/thunk/ppapi_thunk_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(PPAPI_THUNK_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(PPAPI_THUNK_IMPLEMENTATION)
 #define PPAPI_THUNK_EXPORT __attribute__((visibility("default")))
-#else
-#define PPAPI_THUNK_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/remoting/host/host_export.h b/remoting/host/host_export.h
index 37a50de..a2476318 100644
--- a/remoting/host/host_export.h
+++ b/remoting/host/host_export.h
@@ -14,11 +14,7 @@
 #endif  // defined(HOST_IMPLEMENTATION)
 
 #else  // !defined(WIN32)
-#if defined(HOST_IMPLEMENTATION)
 #define HOST_EXPORT __attribute__((visibility("default")))
-#else
-#define HOST_EXPORT
-#endif  // defined(HOST_IMPLEMENTATION)
 #endif  // !defined(WIN32)
 
 #endif  // REMOTING_HOST_HOST_EXPORT_H_
diff --git a/remoting/host/installer/mac/Scripts/remoting_postflight.sh b/remoting/host/installer/mac/Scripts/remoting_postflight.sh
index 202a2d3..76b3d9e 100755
--- a/remoting/host/installer/mac/Scripts/remoting_postflight.sh
+++ b/remoting/host/installer/mac/Scripts/remoting_postflight.sh
@@ -22,6 +22,7 @@
 REMOTE_ASSISTANCE_HOST_BUNDLE_NAME=@@REMOTE_ASSISTANCE_HOST_BUNDLE_NAME@@
 HOST_EXE="$HELPERTOOLS/$HOST_BUNDLE_NAME/Contents/MacOS/remoting_me2me_host"
 USERS_TMP_FILE="$HOST_SERVICE_BINARY.users"
+BROKER_SERVICE_TARGET="system/org.chromium.chromoting.broker"
 
 # ksadmin moved from MacOS to Helpers in Keystone 1.2.13.112, 2019-11-12. A
 # symbolic link from the old location was left in place, but may not remain
@@ -138,6 +139,8 @@
 # started until a host process connects to
 # chromoting.agent_process_broker_mojo_ipc.
 logger Loading broker service
+logger launchctl enable $BROKER_SERVICE_TARGET
+launchctl enable $BROKER_SERVICE_TARGET
 logger launchctl bootstrap system $BROKER_PLIST
 launchctl bootstrap system $BROKER_PLIST
 
diff --git a/remoting/host/linux/BUILD.gn b/remoting/host/linux/BUILD.gn
index 0082130..6236b58 100644
--- a/remoting/host/linux/BUILD.gn
+++ b/remoting/host/linux/BUILD.gn
@@ -154,6 +154,8 @@
     "keyboard_layout_monitor_utils.h",
     "pipewire_capture_stream.cc",
     "pipewire_capture_stream.h",
+    "pipewire_desktop_capturer.cc",
+    "pipewire_desktop_capturer.h",
   ]
   deps = [
     ":x11",
diff --git a/remoting/host/linux/pipewire_desktop_capturer.cc b/remoting/host/linux/pipewire_desktop_capturer.cc
new file mode 100644
index 0000000..d3d6ce9
--- /dev/null
+++ b/remoting/host/linux/pipewire_desktop_capturer.cc
@@ -0,0 +1,119 @@
+// 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/host/linux/pipewire_desktop_capturer.h"
+
+#include <cstdint>
+#include <memory>
+#include <utility>
+
+#include "base/check.h"
+#include "base/functional/bind.h"
+#include "base/location.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/notreached.h"
+#include "base/task/sequenced_task_runner.h"
+#include "remoting/host/linux/pipewire_capture_stream.h"
+#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
+
+namespace remoting {
+
+PipewireDesktopCapturer::PipewireDesktopCapturer(
+    base::WeakPtr<PipewireCaptureStream> stream)
+    : stream_(std::move(stream)) {}
+
+PipewireDesktopCapturer::~PipewireDesktopCapturer() {
+  if (!creating_sequence_->RunsTasksInCurrentSequence()) {
+    creating_sequence_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&PipewireCaptureStream::StopVideoCapture, stream_));
+    // The callback proxy may continue to receive frames until StopVideoCapture
+    // is called, so ensure it is deleted after that.
+    creating_sequence_->DeleteSoon(FROM_HERE, callback_proxy_.release());
+  } else if (stream_) {
+    stream_->StopVideoCapture();
+  }
+}
+
+bool PipewireDesktopCapturer::SupportsFrameCallbacks() {
+  return true;
+}
+
+void PipewireDesktopCapturer::Start(Callback* callback) {
+  // The capturer is created by GnomeInteractionStrategy on its sequence, but
+  // is potentially passed to and owned by a different sequence, which will be
+  // the sequence that calls Start().
+  capture_sequence_ = base::SequencedTaskRunner::GetCurrentDefault();
+  callback_ = callback;
+  callback_proxy_ = std::make_unique<CallbackProxy>(
+      capture_sequence_, weak_ptr_factory_.GetWeakPtr());
+  if (!creating_sequence_->RunsTasksInCurrentSequence()) {
+    // Unretained is safe because callback_proxy_ is always deleted on the
+    // creating sequence after StopVideoCapture is called.
+    creating_sequence_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&PipewireCaptureStream::StartVideoCapture, stream_,
+                       base::Unretained(callback_proxy_.get())));
+  } else if (stream_) {
+    stream_->StartVideoCapture(callback_proxy_.get());
+  }
+}
+
+void PipewireDesktopCapturer::CaptureFrame() {
+  // Capturer will push frames as they are ready.
+  DCHECK(capture_sequence_->RunsTasksInCurrentSequence());
+}
+
+bool PipewireDesktopCapturer::GetSourceList(SourceList* sources) {
+  NOTREACHED();
+}
+
+bool PipewireDesktopCapturer::SelectSource(SourceId id) {
+  NOTREACHED();
+}
+
+void PipewireDesktopCapturer::SetMaxFrameRate(std::uint32_t max_frame_rate) {
+  if (!creating_sequence_->RunsTasksInCurrentSequence()) {
+    creating_sequence_->PostTask(
+        FROM_HERE, base::BindOnce(&PipewireCaptureStream::SetMaxFrameRate,
+                                  stream_, max_frame_rate));
+  } else if (stream_) {
+    stream_->SetMaxFrameRate(max_frame_rate);
+  }
+}
+
+PipewireDesktopCapturer::CallbackProxy::CallbackProxy(
+    scoped_refptr<base::SequencedTaskRunner> capture_sequence,
+    base::WeakPtr<PipewireDesktopCapturer> capturer)
+    : capture_sequence_(std::move(capture_sequence)),
+      capturer_(std::move(capturer)) {}
+
+PipewireDesktopCapturer::CallbackProxy::~CallbackProxy() = default;
+
+void PipewireDesktopCapturer::CallbackProxy::OnFrameCaptureStart() {
+  capture_sequence_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&PipewireDesktopCapturer::OnFrameCaptureStart, capturer_));
+}
+
+void PipewireDesktopCapturer::CallbackProxy::OnCaptureResult(
+    Result result,
+    std::unique_ptr<webrtc::DesktopFrame> frame) {
+  capture_sequence_->PostTask(
+      FROM_HERE, base::BindOnce(&PipewireDesktopCapturer::OnCaptureResult,
+                                capturer_, result, std::move(frame)));
+}
+
+void PipewireDesktopCapturer::OnFrameCaptureStart() {
+  callback_->OnFrameCaptureStart();
+}
+
+void PipewireDesktopCapturer::OnCaptureResult(
+    Result result,
+    std::unique_ptr<webrtc::DesktopFrame> frame) {
+  callback_->OnCaptureResult(result, std::move(frame));
+}
+
+}  // namespace remoting
diff --git a/remoting/host/linux/pipewire_desktop_capturer.h b/remoting/host/linux/pipewire_desktop_capturer.h
new file mode 100644
index 0000000..a1e80fbc
--- /dev/null
+++ b/remoting/host/linux/pipewire_desktop_capturer.h
@@ -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.
+
+#ifndef REMOTING_HOST_LINUX_PIPEWIRE_DESKTOP_CAPTURER_H_
+#define REMOTING_HOST_LINUX_PIPEWIRE_DESKTOP_CAPTURER_H_
+
+#include <cstdint>
+#include <memory>
+
+#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"
+#include "remoting/host/linux/pipewire_capture_stream.h"
+#include "remoting/protocol/desktop_capturer.h"
+#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
+
+namespace remoting {
+
+// DesktopCapturer implementation that allows capturing a single screen via the
+// provided PipewireCaptureStream.
+class PipewireDesktopCapturer : public DesktopCapturer {
+ public:
+  explicit PipewireDesktopCapturer(base::WeakPtr<PipewireCaptureStream> stream);
+  PipewireDesktopCapturer(const PipewireDesktopCapturer&) = delete;
+  PipewireDesktopCapturer& operator=(const PipewireDesktopCapturer&) = delete;
+  ~PipewireDesktopCapturer() override;
+
+  // DesktopCapturer interface.
+  bool SupportsFrameCallbacks() override;
+  void Start(Callback* callback) override;
+  void CaptureFrame() override;
+  void SetMaxFrameRate(std::uint32_t max_frame_rate) override;
+
+  // Unimplemented DesktopCapturer methods that should not be called. Rather,
+  // the appropriate PipewireCaptureStream is provided to the constructor by the
+  // DesktopInteractionStrategy based on the screen ID passed to
+  // DesktopInteractionStrategy::CreateVideoCapturer().
+  bool GetSourceList(SourceList* sources) override;
+  bool SelectSource(SourceId id) override;
+
+ private:
+  // SharedScreencastStream runs the pipewire loop, and invokes frame callbacks,
+  // on a separate thread. This class is responsible for bouncing them back to
+  // the capture thread.
+  class CallbackProxy : public Callback {
+   public:
+    CallbackProxy(scoped_refptr<base::SequencedTaskRunner> capture_sequence,
+                  base::WeakPtr<PipewireDesktopCapturer> capturer);
+    ~CallbackProxy() override;
+
+    // Callback interface
+    void OnFrameCaptureStart() override;
+    void OnCaptureResult(Result result,
+                         std::unique_ptr<webrtc::DesktopFrame> frame) override;
+
+   private:
+    scoped_refptr<base::SequencedTaskRunner> capture_sequence_;
+    base::WeakPtr<PipewireDesktopCapturer> capturer_;
+  };
+
+  // Invoke the corresponding method on callback_.
+  void OnFrameCaptureStart();
+  void OnCaptureResult(Result result,
+                       std::unique_ptr<webrtc::DesktopFrame> frame);
+
+  scoped_refptr<base::SequencedTaskRunner> creating_sequence_ =
+      base::SequencedTaskRunner::GetCurrentDefault();
+  scoped_refptr<base::SequencedTaskRunner> capture_sequence_;
+
+  // Must only be tested for validity and dereferenced on the creating sequence.
+  base::WeakPtr<PipewireCaptureStream> stream_;
+
+  // Per the webrtc::DesktopCapturer interface, callback is required to remain
+  // valid until this is destroyed.
+  raw_ptr<Callback> callback_;
+
+  std::unique_ptr<CallbackProxy> callback_proxy_;
+
+  // Will be bound to the capture sequence when Start() is called and used by
+  // tasks posted by CallbackProxy.
+  base::WeakPtrFactory<PipewireDesktopCapturer> weak_ptr_factory_{this};
+};
+
+}  // namespace remoting
+
+#endif  // REMOTING_HOST_LINUX_PIPEWIRE_DESKTOP_CAPTURER_H_
diff --git a/sandbox/mac/seatbelt_export.h b/sandbox/mac/seatbelt_export.h
index 4f3c196..4167d930 100644
--- a/sandbox/mac/seatbelt_export.h
+++ b/sandbox/mac/seatbelt_export.h
@@ -7,11 +7,7 @@
 
 #if defined(COMPONENT_BUILD)
 
-#if defined(SEATBELT_IMPLEMENTATION)
 #define SEATBELT_EXPORT __attribute__((visibility("default")))
-#else
-#define SEATBELT_EXPORT
-#endif  // defined(SEATBELT_IMPLEMENTATION)
 
 #else  // defined(COMPONENT_BUILD)
 
diff --git a/sandbox/policy/export.h b/sandbox/policy/export.h
index fcef8f6..5546afb7 100644
--- a/sandbox/policy/export.h
+++ b/sandbox/policy/export.h
@@ -15,11 +15,7 @@
 #endif  // defined(SANDBOX_POLICY_IMPL)
 
 #else  // defined(WIN32)
-#if defined(SANDBOX_POLICY_IMPL)
 #define SANDBOX_POLICY_EXPORT __attribute__((visibility("default")))
-#else
-#define SANDBOX_POLICY_EXPORT
-#endif  // defined(SANDBOX_POLICY_IMPL)
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/sandbox/sandbox_export.h b/sandbox/sandbox_export.h
index 06a2aab..443da7b 100644
--- a/sandbox/sandbox_export.h
+++ b/sandbox/sandbox_export.h
@@ -11,11 +11,7 @@
 
 #if defined(COMPONENT_BUILD)
 
-#if defined(SANDBOX_IMPLEMENTATION)
 #define SANDBOX_EXPORT __attribute__((visibility("default")))
-#else
-#define SANDBOX_EXPORT
-#endif  // defined(SANDBOX_IMPLEMENTATION)
 
 #else  // defined(COMPONENT_BUILD)
 
diff --git a/services/device/public/cpp/device_features_export.h b/services/device/public/cpp/device_features_export.h
index 7a66542..b13a9ce 100644
--- a/services/device/public/cpp/device_features_export.h
+++ b/services/device/public/cpp/device_features_export.h
@@ -17,11 +17,7 @@
 
 #else  // !defined(WIN32)
 
-#if defined(DEVICE_FEATURES_IMPLEMENTATION)
 #define DEVICE_FEATURES_EXPORT __attribute__((visibility("default")))
-#else
-#define DEVICE_FEATURES_EXPORT
-#endif
 
 #endif
 
diff --git a/services/metrics/public/cpp/metrics_export.h b/services/metrics/public/cpp/metrics_export.h
index 21e4970..5c61fcda 100644
--- a/services/metrics/public/cpp/metrics_export.h
+++ b/services/metrics/public/cpp/metrics_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(METRICS_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(METRICS_IMPLEMENTATION)
 #define METRICS_EXPORT __attribute__((visibility("default")))
-#else
-#define METRICS_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/services/network/public/cpp/features.cc b/services/network/public/cpp/features.cc
index 4f39a458..536ed91 100644
--- a/services/network/public/cpp/features.cc
+++ b/services/network/public/cpp/features.cc
@@ -281,7 +281,7 @@
 // integrity enforced.
 BASE_FEATURE(kIntegrityPolicyScript,
              "IntegrityPolicyScript",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 BASE_FEATURE(kVisibilityAwareResourceScheduler,
              "VisibilityAwareResourceScheduler",
diff --git a/services/on_device_model/fake/fake_chrome_ml_api.cc b/services/on_device_model/fake/fake_chrome_ml_api.cc
index a19daf28..6ec2976 100644
--- a/services/on_device_model/fake/fake_chrome_ml_api.cc
+++ b/services/on_device_model/fake/fake_chrome_ml_api.cc
@@ -241,23 +241,12 @@
   for (size_t i = 0; i < options->input_size; i++) {
     // SAFETY: `options->input_size` describes how big `options->input` is.
     const ml::InputPiece& piece = UNSAFE_BUFFERS(options->input[i]);
-    if (!std::holds_alternative<std::string>(piece) &&
-        !std::holds_alternative<ml::Token>(piece)) {
-      // We could write code to handle token options and non-text inputs being
-      // passed together, but it would only be exercised by unit tests so would
-      // not improve real-world coverage.
-      CHECK(options->token_offset == 0);
-    }
-
     CHECK(!std::holds_alternative<SkBitmap>(piece) ||
           instance->enable_image_input);
     CHECK(!std::holds_alternative<ml::AudioBuffer>(piece) ||
           instance->enable_audio_input);
     text += PieceToString(piece);
   }
-  if (options->token_offset > 0) {
-    text.erase(text.begin(), text.begin() + options->token_offset);
-  }
   if (options->max_tokens < text.size()) {
     text.resize(options->max_tokens);
   }
@@ -328,7 +317,6 @@
   ChromeMLAppendOptions append_opts{
       .input = options->input,
       .input_size = options->input_size,
-      .token_offset = options->token_offset,
       .max_tokens = options->max_tokens,
       .context_saved_fn = options->context_saved_fn,
   };
diff --git a/services/on_device_model/ml/chrome_ml_api.h b/services/on_device_model/ml/chrome_ml_api.h
index 65377e3..1e577ac 100644
--- a/services/on_device_model/ml/chrome_ml_api.h
+++ b/services/on_device_model/ml/chrome_ml_api.h
@@ -213,8 +213,6 @@
   const ml::InputPiece* input;
   // Number of pieces in input.
   size_t input_size;
-  // A number of tokens to skip in input before adding tokens to the context.
-  uint32_t token_offset;
   // The maximum number of tokens to add to the context.
   uint32_t max_tokens;
   // How to return the result on completion.
@@ -236,7 +234,6 @@
 struct ChromeMLExecuteOptions {
   int context_mode;
   uint32_t max_tokens;
-  uint32_t token_offset;
   uint32_t max_output_tokens;
   const ChromeMLContextSavedFn* context_saved_fn;
   const ChromeMLExecutionOutputFn* execution_output_fn;
diff --git a/services/on_device_model/ml/session_accessor.cc b/services/on_device_model/ml/session_accessor.cc
index 33d6c1e..dec12b5 100644
--- a/services/on_device_model/ml/session_accessor.cc
+++ b/services/on_device_model/ml/session_accessor.cc
@@ -211,7 +211,6 @@
   ChromeMLAppendOptions options{
       .input = append_options->input->pieces.data(),
       .input_size = append_options->input->pieces.size(),
-      .token_offset = append_options->token_offset,
       .max_tokens = append_options->max_tokens,
       .context_saved_fn = &context_saved_fn,
   };
diff --git a/services/on_device_model/on_device_model_service_unittest.cc b/services/on_device_model/on_device_model_service_unittest.cc
index c7ea840..802fbfee 100644
--- a/services/on_device_model/on_device_model_service_unittest.cc
+++ b/services/on_device_model/on_device_model_service_unittest.cc
@@ -380,16 +380,15 @@
 
   ContextClientWaiter client2;
   auto offset_input = MakeInput("big cheese");
-  offset_input->token_offset = 4;
   session->Append(std::move(offset_input), client2.BindRemote());
-  EXPECT_EQ(client2.WaitForCompletion(), 6);
+  EXPECT_EQ(client2.WaitForCompletion(), 10);
 
   session->Append(MakeInput("cheddar"), {});
   session->Generate(mojom::GenerateOptions::New(), response.BindRemote());
   response.WaitForCompletion();
 
   EXPECT_THAT(response.responses(),
-              ElementsAre("Context: big \n", "Context: cheese\n",
+              ElementsAre("Context: big \n", "Context: big cheese\n",
                           "Context: cheddar\n"));
 }
 
diff --git a/services/on_device_model/public/cpp/test_support/fake_service.cc b/services/on_device_model/public/cpp/test_support/fake_service.cc
index d5565f8..44c08c8c3 100644
--- a/services/on_device_model/public/cpp/test_support/fake_service.cc
+++ b/services/on_device_model/public/cpp/test_support/fake_service.cc
@@ -75,10 +75,6 @@
                         const Capabilities& capabilities) {
   std::string suffix;
   std::string context = OnDeviceInputToString(*input.input, capabilities);
-  if (input.token_offset > 0) {
-    context.erase(context.begin(), context.begin() + input.token_offset);
-  }
-  suffix += " off:" + base::NumberToString(input.token_offset);
   if (input.max_tokens > 0) {
     if (input.max_tokens < context.size()) {
       context.resize(input.max_tokens);
@@ -245,8 +241,7 @@
       OnDeviceInputToString(*options->input, capabilities_).size());
   uint32_t max_tokens =
       options->max_tokens > 0 ? options->max_tokens : input_tokens;
-  uint32_t token_offset = options->token_offset;
-  uint32_t tokens_processed = std::min(input_tokens - token_offset, max_tokens);
+  uint32_t tokens_processed = std::min(input_tokens, max_tokens);
   context_.emplace_back(std::move(options));
   if (client) {
     client->OnComplete(tokens_processed);
diff --git a/services/on_device_model/public/mojom/on_device_model.mojom b/services/on_device_model/public/mojom/on_device_model.mojom
index 499c6fe..286630b0 100644
--- a/services/on_device_model/public/mojom/on_device_model.mojom
+++ b/services/on_device_model/public/mojom/on_device_model.mojom
@@ -132,10 +132,12 @@
   // probable.
   // `top_k` should be a value from 1 to the max top K value the model was
   // initialized with.
-  [MinVersion=1] uint32 top_k;
+  [MinVersion=1]
+  uint32 top_k;
   // `temperature` should be a value greater than 0.0. Values above 1.0 may give
   // poor results.
-  [MinVersion=1] float temperature;
+  [MinVersion=1]
+  float temperature;
 };
 
 // The set of tokens that can be added as part of an input.
@@ -191,8 +193,7 @@
   // process all tokens from this input.
   uint32 max_tokens = 0;
 
-  // After text is tokenized, the offset into that vector to start processing.
-  // If zero, will start at the first token.
+  // Deprecated. Does nothing.
   uint32 token_offset = 0;
 };
 
@@ -223,11 +224,13 @@
   float? temperature;
 
   // Deprecated: use `constraint` instead.
-  [MinVersion=1] string? response_json_schema;
+  [MinVersion=1]
+  string? response_json_schema;
 
   // Specifies any constraints on the output for this Generate() call. Note that
   // only one of the JSON schema or regex constraints should be provided.
-  [MinVersion=2] ResponseConstraint? constraint;
+  [MinVersion=2]
+  ResponseConstraint? constraint;
 };
 
 // Priorities which determine how requests to a session are scheduled.
@@ -290,7 +293,7 @@
   // Starts a session with this model. Sessions are logically independent,
   // but requests may block progress of other sessions.
   StartSession@0(pending_receiver<Session> session,
-      [MinVersion=1] SessionParams? params);
+                 [MinVersion=1] SessionParams? params);
 
   // DEPRECATED: Does nothing
   ClassifyTextSafety@1(string text) => (SafetyInfo? safety_info);
diff --git a/services/screen_ai/screen_ai_service_impl.cc b/services/screen_ai/screen_ai_service_impl.cc
index ff7609b..8103785 100644
--- a/services/screen_ai/screen_ai_service_impl.cc
+++ b/services/screen_ai/screen_ai_service_impl.cc
@@ -445,8 +445,6 @@
                             result.has_value());
   base::UmaHistogramCounts100("Accessibility.ScreenAI.OCR.LinesCount",
                               lines_count);
-  base::UmaHistogramCounts10M("Accessibility.ScreenAI.OCR.ImageSize10M",
-                              image.width() * image.height());
   if (image.width() < max_dimension && image.height() < max_dimension) {
     base::UmaHistogramTimes("Accessibility.ScreenAI.OCR.Latency.NotDownsampled",
                             elapsed_time);
diff --git a/services/service_manager/public/cpp/export.h b/services/service_manager/public/cpp/export.h
index 33229a6..5a3f5c3 100644
--- a/services/service_manager/public/cpp/export.h
+++ b/services/service_manager/public/cpp/export.h
@@ -15,11 +15,7 @@
 #endif  // defined(SERVICE_MANAGER_PUBLIC_CPP_IMPL)
 
 #else  // defined(WIN32)
-#if defined(SERVICE_MANAGER_PUBLIC_CPP_IMPL)
 #define SERVICE_MANAGER_PUBLIC_CPP_EXPORT __attribute__((visibility("default")))
-#else
-#define SERVICE_MANAGER_PUBLIC_CPP_EXPORT
-#endif  // defined(SERVICE_MANAGER_PUBLIC_CPP_IMPL)
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/services/webnn/tflite/graph_builder_tflite.cc b/services/webnn/tflite/graph_builder_tflite.cc
index 2504c4d3..d4ef9f4 100644
--- a/services/webnn/tflite/graph_builder_tflite.cc
+++ b/services/webnn/tflite/graph_builder_tflite.cc
@@ -1642,6 +1642,43 @@
 }
 
 std::optional<GraphBuilderTflite::TensorInfo>
+GraphBuilderTflite::CanFuseQuantizeAndGetOutput(const mojom::Elu& elu) {
+  if (!IsDequantizeOutput(elu.input_operand_id)) {
+    return std::nullopt;
+  }
+
+  // TODO(crbug.com/413083273): Consider the restriction in GPU delegate.
+  // For XNNPack delegate, the input must be dequantized from int8, the input
+  // and output scale must be scaler.
+  // https://source.chromium.org/chromium/chromium/src/+/main:third_party/tflite/src/tensorflow/lite/delegates/xnnpack/xnnpack_delegate.cc;l=4136;drc=f667feb8a5c6f227b49328ce78a062acc4f81187
+  const mojom::DequantizeLinear& input_dequantize =
+      GetDequantizeOp(elu.input_operand_id);
+  if (GetOperand(input_dequantize.input_operand_id).descriptor.data_type() !=
+      OperandDataType::kInt8) {
+    return std::nullopt;
+  }
+
+  if (GetOperand(input_dequantize.scale_operand_id)
+          .descriptor.NumberOfElements() != 1) {
+    return std::nullopt;
+  }
+
+  std::optional<size_t> next_op =
+      IsNextOpQuantize(elu.output_operand_id, {OperandDataType::kInt8});
+  if (!next_op) {
+    return std::nullopt;
+  }
+
+  const mojom::QuantizeLinear& output_quantize = GetQuantizeOp(*next_op);
+  if (GetOperand(output_quantize.scale_operand_id)
+          .descriptor.NumberOfElements() != 1) {
+    return std::nullopt;
+  }
+
+  return TrySerializeQuantizedOutput(*next_op);
+}
+
+std::optional<GraphBuilderTflite::TensorInfo>
 GraphBuilderTflite::CanFuseQuantizeAndGetOutput(
     const mojom::Transpose& transpose) {
   if (!IsDequantizeOutput(transpose.input_operand_id)) {
@@ -3456,13 +3493,24 @@
         "Setting a custom alpha is not supported in tflite schema.");
   }
 
+  std::optional<TensorInfo> quantized_output = CanFuseQuantizeAndGetOutput(elu);
+  const bool fuse_dequantize = quantized_output.has_value();
   ASSIGN_OR_RETURN(const TensorInfo& input_tensor_info,
-                   SerializeInputTensorInfo(elu.input_operand_id));
-  ASSIGN_OR_RETURN(const TensorInfo& output_tensor_info,
-                   SerializeOutputTensorInfo(elu.output_operand_id));
+                   SerializeInputTensorInfo(
+                       elu.input_operand_id, /*quantize_params=*/0,
+                       /*operation_supports_float16=*/false, fuse_dequantize));
+
+  TensorIndex output_tensor_index;
+  if (fuse_dequantize) {
+    output_tensor_index = quantized_output->index;
+  } else {
+    ASSIGN_OR_RETURN(const TensorInfo& output_tensor_info,
+                     SerializeOutputTensorInfo(elu.output_operand_id));
+    output_tensor_index = output_tensor_info.index;
+  }
+
   return SerializeUnaryOperation(::tflite::BuiltinOperator_ELU,
-                                 input_tensor_info.index,
-                                 output_tensor_info.index);
+                                 input_tensor_info.index, output_tensor_index);
 }
 
 auto GraphBuilderTflite::SerializeErf(const TensorInfo& input_tensor_info,
diff --git a/services/webnn/tflite/graph_builder_tflite.h b/services/webnn/tflite/graph_builder_tflite.h
index 45273dd3..bf39351 100644
--- a/services/webnn/tflite/graph_builder_tflite.h
+++ b/services/webnn/tflite/graph_builder_tflite.h
@@ -721,6 +721,7 @@
       const mojom::Concat& concat);
   std::optional<TensorInfo> CanFuseQuantizeAndGetOutput(
       const mojom::ElementWiseBinary& binary);
+  std::optional<TensorInfo> CanFuseQuantizeAndGetOutput(const mojom::Elu& elu);
   std::optional<TensorInfo> CanFuseQuantizeAndGetOutput(
       const mojom::Transpose& transpose);
   std::optional<TensorInfo> CanFuseQuantizeAndGetOutput(
diff --git a/skia/ext/switches_export.h b/skia/ext/switches_export.h
index 2301d3c..720a1f6 100644
--- a/skia/ext/switches_export.h
+++ b/skia/ext/switches_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(SKIA_SWITCHES_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(SKIA_SWITCHES_IMPLEMENTATION)
 #define SKIA_SWITCHES_EXPORT __attribute__((visibility("default")))
-#else
-#define SKIA_SWITCHES_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/storage/browser/quota/quota_database_migrations.cc b/storage/browser/quota/quota_database_migrations.cc
index 81abe40..b69fbcc5 100644
--- a/storage/browser/quota/quota_database_migrations.cc
+++ b/storage/browser/quota/quota_database_migrations.cc
@@ -27,6 +27,9 @@
 // The name for the implicit/default bucket before V10.
 constexpr char kDefaultNamePreV10[] = "default";
 
+// Quota type for deprecated persistent quota.
+constexpr int kDeprecatedPersistentQuotaType = 1;
+
 // Overwrites the buckets table with the new_buckets table after data has been
 // copied from the former into the latter.
 bool OverwriteBucketsTableSetUpIndexes(sql::Database* db) {
@@ -351,8 +354,7 @@
       "DELETE FROM buckets WHERE type = ? ";
   sql::Statement delete_statement(
       db->GetCachedStatement(SQL_FROM_HERE, kDeletePersistentTypeBuckets));
-  delete_statement.BindInt(
-      0, static_cast<int>(blink::mojom::StorageType::kDeprecatedPersistent));
+  delete_statement.BindInt(0, kDeprecatedPersistentQuotaType);
   if (!delete_statement.Run()) {
     return false;
   }
diff --git a/storage/browser/quota/quota_manager_impl.cc b/storage/browser/quota/quota_manager_impl.cc
index 85ddf5ee..26fc10b 100644
--- a/storage/browser/quota/quota_manager_impl.cc
+++ b/storage/browser/quota/quota_manager_impl.cc
@@ -74,7 +74,6 @@
 #include "url/origin.h"
 
 using ::blink::StorageKey;
-using ::blink::mojom::StorageType;
 
 namespace storage {
 
diff --git a/storage/browser/quota/quota_manager_unittest.cc b/storage/browser/quota/quota_manager_unittest.cc
index faa1f42..9748daba 100644
--- a/storage/browser/quota/quota_manager_unittest.cc
+++ b/storage/browser/quota/quota_manager_unittest.cc
@@ -60,7 +60,6 @@
 
 using ::blink::StorageKey;
 using ::blink::mojom::QuotaStatusCode;
-using ::blink::mojom::StorageType;
 
 namespace storage {
 
diff --git a/storage/browser/quota/quota_temporary_storage_evictor_unittest.cc b/storage/browser/quota/quota_temporary_storage_evictor_unittest.cc
index 744d5465..a02de9b 100644
--- a/storage/browser/quota/quota_temporary_storage_evictor_unittest.cc
+++ b/storage/browser/quota/quota_temporary_storage_evictor_unittest.cc
@@ -25,8 +25,6 @@
 #include "storage/browser/quota/quota_temporary_storage_evictor.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using ::blink::mojom::StorageType;
-
 namespace storage {
 
 class QuotaTemporaryStorageEvictorTest;
diff --git a/storage/browser/quota/usage_tracker_unittest.cc b/storage/browser/quota/usage_tracker_unittest.cc
index 7736f06..bc85c90 100644
--- a/storage/browser/quota/usage_tracker_unittest.cc
+++ b/storage/browser/quota/usage_tracker_unittest.cc
@@ -32,7 +32,6 @@
 
 using ::blink::StorageKey;
 using ::blink::mojom::QuotaStatusCode;
-using ::blink::mojom::StorageType;
 
 namespace storage {
 
diff --git a/testing/buildbot/filters/android.desktop.emulator_15.chrome_public_test_apk.filter b/testing/buildbot/filters/android.desktop.emulator_15.chrome_public_test_apk.filter
index fbeeb350..ef747dc 100644
--- a/testing/buildbot/filters/android.desktop.emulator_15.chrome_public_test_apk.filter
+++ b/testing/buildbot/filters/android.desktop.emulator_15.chrome_public_test_apk.filter
@@ -81,7 +81,9 @@
 -org.chromium.chrome.browser.page_info.PageInfoViewDarkModeTest.testShowOnSecureWebsiteDark
 
 # crbug.com/415101606
+-org.chromium.chrome.browser.contextmenu.ContextMenuTest.testPrintPage
 -org.chromium.chrome.browser.contextmenu.ContextMenuTest.testSavePage
+-org.chromium.chrome.browser.contextmenu.ContextMenuTest.testSharePage
 
 # crbug.com/415118264
 -org.chromium.chrome.browser.customtabs.CustomTabActivityIncognitoTest.ensureAddCustomMenuItemHasNoEffect
diff --git a/testing/buildbot/filters/ozone-linux.browser_tests_mutter.filter b/testing/buildbot/filters/ozone-linux.browser_tests_mutter.filter
index d0e958d..ea4f9e6d 100644
--- a/testing/buildbot/filters/ozone-linux.browser_tests_mutter.filter
+++ b/testing/buildbot/filters/ozone-linux.browser_tests_mutter.filter
@@ -28,6 +28,3 @@
 # in mutter with some additional steps.
 -All/WebRtcScreenCaptureBrowserTestWithPicker.ScreenCaptureVideo/1
 -All/WebRtcScreenCaptureBrowserTestWithPicker.ScreenCaptureVideoAndAudio/1
-
-# TODO(crbug.com/395595608) Failing on exit due to dangling ptr
--IFrameTest.FileChooserInDestroyedSubframe
diff --git a/testing/buildbot/filters/trees_in_viz.cc_unittests.filter b/testing/buildbot/filters/trees_in_viz.cc_unittests.filter
index 605b84d..a60592d60 100644
--- a/testing/buildbot/filters/trees_in_viz.cc_unittests.filter
+++ b/testing/buildbot/filters/trees_in_viz.cc_unittests.filter
@@ -42,25 +42,9 @@
 -LayerTreeHostImplTest.ScrollCheckerboardsIncompleteRecording/CommitToActiveTree
 -LayerTreeHostImplTest.ScrollCheckerboardsIncompleteRecording/CommitToPendingTree
 
-# TODO(crbug.com/401566971) implement ThrottleDecider updates for TreesInViz mode.
--OccludedSurfaceThrottlingLayerTreeHostImplTest.ThrottleOccludedSurface/CommitToActiveTree
--OccludedSurfaceThrottlingLayerTreeHostImplTest.ThrottleOccludedSurface/CommitToPendingTree
-
 # TODO(crbug.com/401567193) LayerImpl WillDraw and DidDraw implementation for TreesInViz mode.
--LayerTreeHostImplTest.WillDrawNotCalledOnOccludedLayer/CommitToActiveTree
--LayerTreeHostImplTest.WillDrawNotCalledOnOccludedLayer/CommitToPendingTree
 -LayerTreeHostImplTest.WillDrawReturningFalseDoesNotCall/CommitToActiveTree
 -LayerTreeHostImplTest.WillDrawReturningFalseDoesNotCall/CommitToPendingTree
--LayerTreeHostImplTest.DidDrawCalledOnAllLayers/CommitToActiveTree
--LayerTreeHostImplTest.DidDrawCalledOnAllLayers/CommitToPendingTree
--LayerTreeHostImplTest.DidDrawNotCalledOnHiddenLayer/CommitToActiveTree
--LayerTreeHostImplTest.DidDrawNotCalledOnHiddenLayer/CommitToPendingTree
--LayerTreeHostImplTest.ForcedDrawToSoftwareDeviceSkipsUnsupportedLayers/CommitToActiveTree
--LayerTreeHostImplTest.ForcedDrawToSoftwareDeviceSkipsUnsupportedLayers/CommitToPendingTree
-
-# TODO(crbug.com/401568010) Investigate LayerTreeHostImplTest.LayersFreeTextures
--LayerTreeHostImplTest.LayersFreeTextures/CommitToActiveTree
--LayerTreeHostImplTest.LayersFreeTextures/CommitToPendingTree
 
 # TODO (crbug.com/404940328) Fix LayerTreeHostTestInvisibleLayersSkipRenderPass
 -LayerTreeHostTestInvisibleLayersSkipRenderPass.RunSingleThread_DelegatingRenderer
diff --git a/testing/buildbot/filters/trees_in_viz.content_browsertests.filter b/testing/buildbot/filters/trees_in_viz.content_browsertests.filter
index 0728785..5eb7e30 100644
--- a/testing/buildbot/filters/trees_in_viz.content_browsertests.filter
+++ b/testing/buildbot/filters/trees_in_viz.content_browsertests.filter
@@ -10,8 +10,6 @@
 -All/NavigationBrowserTestPaintHoldingSubframe.BasicInProcessIframe/0
 -All/NavigationBrowserTestPaintHoldingSubframe.BasicInProcessIframe/1
 -All/RenderWidgetHostItemSequenceNumberInRenderFrameMetadataTest.ItemSequenceNumberExpectedNoContentChange/SameDoc
--All/SitePerProcessBrowserTest.ScrollOopifInPinchZoomedPage/0
--All/SitePerProcessBrowserTest.ScrollOopifInPinchZoomedPage/1
 -PrerenderBrowserDeathTest.PrerenderCannotHaveInnerContents
 -RenderFrameHostImplDeathTest.ReloadInPendingDeletionOrBFCache
--SecurityExploitBrowserTest.AllowBindingsForNonWebUIProcess
\ No newline at end of file
+-SecurityExploitBrowserTest.AllowBindingsForNonWebUIProcess
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 6aae15b..2819f4d90 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -12496,26 +12496,6 @@
             ]
         }
     ],
-    "IntegrityPolicyScript": [
-        {
-            "platforms": [
-                "android",
-                "android_webview",
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "IntegrityPolicyScript"
-                    ]
-                }
-            ]
-        }
-    ],
     "InvalidateSearchEngineChoiceOnDeviceRestoreDetection": [
         {
             "platforms": [
@@ -17356,6 +17336,26 @@
             ]
         }
     ],
+    "PreloadingNoSamePageFragmentAnchorTracking": [
+        {
+            "platforms": [
+                "android",
+                "chromeos",
+                "chromeos_lacros",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "NoSamePageFragmentPreloadingAnchorTracking"
+                    ]
+                }
+            ]
+        }
+    ],
     "Prerender2BookmarkBarTriggerV2": [
         {
             "platforms": [
@@ -23870,6 +23870,25 @@
             ]
         }
     ],
+    "UseSharedRebindServiceConnection": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "EnabledDeferredBindings10",
+                    "params": {
+                        "max-deferred-bindings": "10"
+                    },
+                    "enable_features": [
+                        "GroupRebindingForGroupImportance",
+                        "UseSharedRebindServiceConnection"
+                    ]
+                }
+            ]
+        }
+    ],
     "UseSmartRefForGPUFenceHandle": [
         {
             "platforms": [
diff --git a/third_party/abseil-cpp/MODULE.bazel b/third_party/abseil-cpp/MODULE.bazel
index 405d2c6..29e624a 100644
--- a/third_party/abseil-cpp/MODULE.bazel
+++ b/third_party/abseil-cpp/MODULE.bazel
@@ -39,5 +39,5 @@
 # intended to be used by Abseil users depend on GoogleTest.
 bazel_dep(
     name = "googletest",
-    version = "1.16.0.bcr.1",
+    version = "1.17.0",
 )
diff --git a/third_party/abseil-cpp/README.chromium b/third_party/abseil-cpp/README.chromium
index 595721d..3f47276 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: 5f3435aba00bcd7f12062d2e8e1839b4eaf1a575
+Revision: 2bbec17a3f20da4d908e5627aa72b46f48e064ac
 Security Critical: yes
 Shipped: yes
 
diff --git a/third_party/abseil-cpp/absl/algorithm/container.h b/third_party/abseil-cpp/absl/algorithm/container.h
index 3193656..913268d 100644
--- a/third_party/abseil-cpp/absl/algorithm/container.h
+++ b/third_party/abseil-cpp/absl/algorithm/container.h
@@ -44,7 +44,6 @@
 #include <cassert>
 #include <iterator>
 #include <numeric>
-#include <random>
 #include <type_traits>
 #include <unordered_map>
 #include <unordered_set>
@@ -847,25 +846,9 @@
           typename UniformRandomBitGenerator>
 OutputIterator c_sample(const C& c, OutputIterator result, Distance n,
                         UniformRandomBitGenerator&& gen) {
-#if defined(__cpp_lib_sample) && __cpp_lib_sample >= 201603L
   return std::sample(container_algorithm_internal::c_begin(c),
                      container_algorithm_internal::c_end(c), result, n,
                      std::forward<UniformRandomBitGenerator>(gen));
-#else
-  // Fall back to a stable selection-sampling implementation.
-  auto first = container_algorithm_internal::c_begin(c);
-  Distance unsampled_elements = c_distance(c);
-  n = (std::min)(n, unsampled_elements);
-  for (; n != 0; ++first) {
-    Distance r =
-        std::uniform_int_distribution<Distance>(0, --unsampled_elements)(gen);
-    if (r < n) {
-      *result++ = *first;
-      --n;
-    }
-  }
-  return result;
-#endif
 }
 
 //------------------------------------------------------------------------------
diff --git a/third_party/abseil-cpp/absl/container/internal/hash_policy_testing.h b/third_party/abseil-cpp/absl/container/internal/hash_policy_testing.h
index 66bb12e..e9f5757 100644
--- a/third_party/abseil-cpp/absl/container/internal/hash_policy_testing.h
+++ b/third_party/abseil-cpp/absl/container/internal/hash_policy_testing.h
@@ -119,7 +119,11 @@
   using propagate_on_container_swap = std::true_type;
 
   // Using old paradigm for this to ensure compatibility.
-  explicit Alloc(size_t id = 0) : id_(id) {}
+  //
+  // NOTE: As of 2025-05, this constructor cannot be explicit in order to work
+  // with the libstdc++ that ships with GCC15.
+  // NOLINTNEXTLINE(google-explicit-constructor)
+  Alloc(size_t id = 0) : id_(id) {}
 
   Alloc(const Alloc&) = default;
   Alloc& operator=(const Alloc&) = default;
diff --git a/third_party/abseil-cpp/absl/container/internal/raw_hash_set.cc b/third_party/abseil-cpp/absl/container/internal/raw_hash_set.cc
index f66f486..f19e87b 100644
--- a/third_party/abseil-cpp/absl/container/internal/raw_hash_set.cc
+++ b/third_party/abseil-cpp/absl/container/internal/raw_hash_set.cc
@@ -617,7 +617,7 @@
 void ResizeNonSooImpl(CommonFields& common, const PolicyFunctions& policy,
                       size_t new_capacity, HashtablezInfoHandle infoz) {
   ABSL_SWISSTABLE_ASSERT(IsValidCapacity(new_capacity));
-  ABSL_SWISSTABLE_ASSERT(new_capacity > policy.soo_capacity);
+  ABSL_SWISSTABLE_ASSERT(new_capacity > policy.soo_capacity());
 
   const size_t old_capacity = common.capacity();
   [[maybe_unused]] ctrl_t* old_ctrl = common.control();
@@ -643,7 +643,7 @@
   size_t total_probe_length = 0;
   ResetCtrl(common, slot_size);
   ABSL_SWISSTABLE_ASSERT(kMode != ResizeNonSooMode::kGuaranteedEmpty ||
-                         old_capacity == policy.soo_capacity);
+                         old_capacity == policy.soo_capacity());
   ABSL_SWISSTABLE_ASSERT(kMode != ResizeNonSooMode::kGuaranteedAllocated ||
                          old_capacity > 0);
   if constexpr (kMode == ResizeNonSooMode::kGuaranteedAllocated) {
@@ -670,9 +670,9 @@
                                       const PolicyFunctions& policy,
                                       size_t new_capacity, bool force_infoz) {
   ABSL_SWISSTABLE_ASSERT(IsValidCapacity(new_capacity));
-  ABSL_SWISSTABLE_ASSERT(new_capacity > policy.soo_capacity);
-  ABSL_SWISSTABLE_ASSERT(!force_infoz || policy.soo_capacity > 0);
-  ABSL_SWISSTABLE_ASSERT(common.capacity() <= policy.soo_capacity);
+  ABSL_SWISSTABLE_ASSERT(new_capacity > policy.soo_capacity());
+  ABSL_SWISSTABLE_ASSERT(!force_infoz || policy.soo_enabled);
+  ABSL_SWISSTABLE_ASSERT(common.capacity() <= policy.soo_capacity());
   ABSL_SWISSTABLE_ASSERT(common.empty());
   const size_t slot_size = policy.slot_size;
   HashtablezInfoHandle infoz;
@@ -680,7 +680,7 @@
       policy.is_hashtablez_eligible && (force_infoz || ShouldSampleNextTable());
   if (ABSL_PREDICT_FALSE(should_sample)) {
     infoz = ForcedTrySample(slot_size, policy.key_size, policy.value_size,
-                            policy.soo_capacity);
+                            policy.soo_capacity());
   }
   ResizeNonSooImpl<ResizeNonSooMode::kGuaranteedEmpty>(common, policy,
                                                        new_capacity, infoz);
@@ -694,8 +694,8 @@
                                                const PolicyFunctions& policy,
                                                size_t hash, ctrl_t* new_ctrl,
                                                void* new_slots) {
-  ABSL_SWISSTABLE_ASSERT(c.size() == policy.soo_capacity);
-  ABSL_SWISSTABLE_ASSERT(policy.soo_capacity == SooCapacity());
+  ABSL_SWISSTABLE_ASSERT(c.size() == policy.soo_capacity());
+  ABSL_SWISSTABLE_ASSERT(policy.soo_enabled);
   size_t new_capacity = c.capacity();
 
   c.generate_new_seed();
@@ -717,12 +717,21 @@
   kForceSampleNoResizeIfUnsampled,
 };
 
+void AssertSoo([[maybe_unused]] CommonFields& common,
+               [[maybe_unused]] const PolicyFunctions& policy) {
+  ABSL_SWISSTABLE_ASSERT(policy.soo_enabled);
+  ABSL_SWISSTABLE_ASSERT(common.capacity() == policy.soo_capacity());
+}
+void AssertFullSoo([[maybe_unused]] CommonFields& common,
+                   [[maybe_unused]] const PolicyFunctions& policy) {
+  AssertSoo(common, policy);
+  ABSL_SWISSTABLE_ASSERT(common.size() == policy.soo_capacity());
+}
+
 void ResizeFullSooTable(CommonFields& common, const PolicyFunctions& policy,
                         size_t new_capacity,
                         ResizeFullSooTableSamplingMode sampling_mode) {
-  ABSL_SWISSTABLE_ASSERT(common.capacity() == policy.soo_capacity);
-  ABSL_SWISSTABLE_ASSERT(common.size() == policy.soo_capacity);
-  ABSL_SWISSTABLE_ASSERT(policy.soo_capacity == SooCapacity());
+  AssertFullSoo(common, policy);
   const size_t slot_size = policy.slot_size;
   const size_t slot_align = policy.slot_align;
 
@@ -731,7 +740,7 @@
       ResizeFullSooTableSamplingMode::kForceSampleNoResizeIfUnsampled) {
     if (ABSL_PREDICT_FALSE(policy.is_hashtablez_eligible)) {
       infoz = ForcedTrySample(slot_size, policy.key_size, policy.value_size,
-                              policy.soo_capacity);
+                              policy.soo_capacity());
     }
 
     if (!infoz.IsSampled()) {
@@ -1218,11 +1227,11 @@
   ABSL_SWISSTABLE_ASSERT(common.growth_left() == 0);
   const size_t old_capacity = common.capacity();
   ABSL_SWISSTABLE_ASSERT(old_capacity == 0 ||
-                         old_capacity > policy.soo_capacity);
+                         old_capacity > policy.soo_capacity());
 
   const size_t new_capacity = NextCapacity(old_capacity);
   ABSL_SWISSTABLE_ASSERT(IsValidCapacity(new_capacity));
-  ABSL_SWISSTABLE_ASSERT(new_capacity > policy.soo_capacity);
+  ABSL_SWISSTABLE_ASSERT(new_capacity > policy.soo_capacity());
 
   ctrl_t* old_ctrl = common.control();
   void* old_slots = common.slot_array();
@@ -1238,7 +1247,7 @@
         policy.is_hashtablez_eligible && ShouldSampleNextTable();
     if (ABSL_PREDICT_FALSE(should_sample)) {
       infoz = ForcedTrySample(slot_size, policy.key_size, policy.value_size,
-                              policy.soo_capacity);
+                              policy.soo_capacity());
     }
   }
   const bool has_infoz = infoz.IsSampled();
@@ -1291,7 +1300,7 @@
       find_info = find_first_non_full(common, new_hash);
       SetCtrlInLargeTable(common, find_info.offset, new_h2, policy.slot_size);
     }
-    ABSL_SWISSTABLE_ASSERT(old_capacity > policy.soo_capacity);
+    ABSL_SWISSTABLE_ASSERT(old_capacity > policy.soo_capacity());
     (*policy.dealloc)(alloc, old_capacity, old_ctrl, slot_size, slot_align,
                       has_infoz);
   }
@@ -1415,9 +1424,9 @@
 
 // Resizes empty non-allocated table to the capacity to fit new_size elements.
 // Requires:
-//   1. `c.capacity() == policy.soo_capacity`.
+//   1. `c.capacity() == policy.soo_capacity()`.
 //   2. `c.empty()`.
-//   3. `new_size > policy.soo_capacity`.
+//   3. `new_size > policy.soo_capacity()`.
 // The table will be attempted to be sampled.
 void ReserveEmptyNonAllocatedTableToFitNewSize(CommonFields& common,
                                                const PolicyFunctions& policy,
@@ -1435,7 +1444,7 @@
 // allocated backing array.
 //
 // Requires:
-//   1. `c.capacity() > policy.soo_capacity` OR `!c.empty()`.
+//   1. `c.capacity() > policy.soo_capacity()` OR `!c.empty()`.
 // Reserving already allocated tables is considered to be a rare case.
 ABSL_ATTRIBUTE_NOINLINE void ReserveAllocatedTable(
     CommonFields& common, const PolicyFunctions& policy, size_t new_size) {
@@ -1443,12 +1452,12 @@
   ValidateMaxSize(new_size, policy.slot_size);
   ABSL_ASSUME(new_size > 0);
   const size_t new_capacity = SizeToCapacity(new_size);
-  if (cap == policy.soo_capacity) {
+  if (cap == policy.soo_capacity()) {
     ABSL_SWISSTABLE_ASSERT(!common.empty());
     ResizeFullSooTable(common, policy, new_capacity,
                        ResizeFullSooTableSamplingMode::kNoSampling);
   } else {
-    ABSL_SWISSTABLE_ASSERT(cap > policy.soo_capacity);
+    ABSL_SWISSTABLE_ASSERT(cap > policy.soo_capacity());
     // TODO(b/382423690): consider using GrowToNextCapacity, when applicable.
     ResizeAllocatedTableWithSeedChange(common, policy, new_capacity);
   }
@@ -1486,16 +1495,14 @@
                                                   const PolicyFunctions& policy,
                                                   size_t new_hash,
                                                   ctrl_t soo_slot_ctrl) {
-  ABSL_SWISSTABLE_ASSERT(common.capacity() == policy.soo_capacity);
-  ABSL_SWISSTABLE_ASSERT(policy.soo_capacity == SooCapacity());
+  AssertSoo(common, policy);
   if (ABSL_PREDICT_FALSE(soo_slot_ctrl == ctrl_t::kEmpty)) {
     // The table is empty, it is only used for forced sampling of SOO tables.
     return GrowEmptySooTableToNextCapacityForceSamplingAndPrepareInsert(
         common, policy, new_hash);
   }
-  ABSL_SWISSTABLE_ASSERT(common.size() == policy.soo_capacity);
+  ABSL_SWISSTABLE_ASSERT(common.size() == policy.soo_capacity());
   static constexpr size_t kNewCapacity = NextCapacity(SooCapacity());
-  ABSL_SWISSTABLE_ASSERT(kNewCapacity > policy.soo_capacity);
   const size_t slot_size = policy.slot_size;
   const size_t slot_align = policy.slot_align;
   common.set_capacity(kNewCapacity);
@@ -1560,9 +1567,7 @@
 
 void GrowFullSooTableToNextCapacityForceSampling(
     CommonFields& common, const PolicyFunctions& policy) {
-  ABSL_SWISSTABLE_ASSERT(common.capacity() == policy.soo_capacity);
-  ABSL_SWISSTABLE_ASSERT(common.size() == policy.soo_capacity);
-  ABSL_SWISSTABLE_ASSERT(policy.soo_capacity == SooCapacity());
+  AssertFullSoo(common, policy);
   ResizeFullSooTable(
       common, policy, NextCapacity(SooCapacity()),
       ResizeFullSooTableSamplingMode::kForceSampleNoResizeIfUnsampled);
@@ -1573,18 +1578,18 @@
 
   auto clear_backing_array = [&]() {
     ClearBackingArray(common, policy, policy.get_char_alloc(common),
-                      /*reuse=*/false, policy.soo_capacity > 0);
+                      /*reuse=*/false, policy.soo_enabled);
   };
 
   const size_t slot_size = policy.slot_size;
 
   if (n == 0) {
-    if (cap <= policy.soo_capacity) return;
+    if (cap <= policy.soo_capacity()) return;
     if (common.empty()) {
       clear_backing_array();
       return;
     }
-    if (common.size() <= policy.soo_capacity) {
+    if (common.size() <= policy.soo_capacity()) {
       // When the table is already sampled, we keep it sampled.
       if (common.infoz().IsSampled()) {
         static constexpr size_t kInitialSampledCapacity =
@@ -1618,7 +1623,7 @@
       NormalizeCapacity(n | SizeToCapacity(common.size()));
   // n == 0 unconditionally rehashes as per the standard.
   if (n == 0 || new_capacity > cap) {
-    if (cap == policy.soo_capacity) {
+    if (cap == policy.soo_capacity()) {
       if (common.empty()) {
         ResizeEmptyNonAllocatedTableImpl(common, policy, new_capacity,
                                          /*force_infoz=*/false);
@@ -1640,7 +1645,7 @@
           absl::FunctionRef<void(void*, const void*)> copy_fn) {
   const size_t size = other.size();
   ABSL_SWISSTABLE_ASSERT(size > 0);
-  const size_t soo_capacity = policy.soo_capacity;
+  const size_t soo_capacity = policy.soo_capacity();
   const size_t slot_size = policy.slot_size;
   if (size <= soo_capacity) {
     ABSL_SWISSTABLE_ASSERT(size == 1);
@@ -1714,17 +1719,17 @@
                               const PolicyFunctions& policy, size_t new_size) {
   common.reset_reserved_growth(new_size);
   common.set_reservation_size(new_size);
-  ABSL_SWISSTABLE_ASSERT(new_size > policy.soo_capacity);
+  ABSL_SWISSTABLE_ASSERT(new_size > policy.soo_capacity());
   const size_t cap = common.capacity();
-  if (ABSL_PREDICT_TRUE(common.empty() && cap <= policy.soo_capacity)) {
+  if (ABSL_PREDICT_TRUE(common.empty() && cap <= policy.soo_capacity())) {
     return ReserveEmptyNonAllocatedTableToFitNewSize(common, policy, new_size);
   }
 
-  ABSL_SWISSTABLE_ASSERT(!common.empty() || cap > policy.soo_capacity);
+  ABSL_SWISSTABLE_ASSERT(!common.empty() || cap > policy.soo_capacity());
   ABSL_SWISSTABLE_ASSERT(cap > 0);
   const size_t max_size_before_growth =
-      cap <= policy.soo_capacity ? policy.soo_capacity
-                                 : common.size() + common.growth_left();
+      cap <= policy.soo_capacity() ? policy.soo_capacity()
+                                   : common.size() + common.growth_left();
   if (new_size <= max_size_before_growth) {
     return;
   }
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 f8c0731d..512c946 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
@@ -514,6 +514,8 @@
   // Sets the has_infoz bit.
   void set_has_infoz() { data_ |= kHasInfozMask; }
 
+  void set_no_seed_for_testing() { data_ &= ~kSeedMask; }
+
  private:
   static constexpr size_t kSizeShift = 64 - kSizeBitCount;
   static constexpr uint64_t kSizeOneNoMetadata = uint64_t{1} << kSizeShift;
@@ -1037,6 +1039,7 @@
   // `kGenerateSeed && !empty() && !is_single_group(capacity())` because H1 is
   // being changed. In such cases, we will need to rehash the table.
   void generate_new_seed() { size_.generate_new_seed(); }
+  void set_no_seed_for_testing() { size_.set_no_seed_for_testing(); }
 
   // The total number of available slots.
   size_t capacity() const { return capacity_; }
@@ -1637,7 +1640,7 @@
   uint32_t value_size;
   uint32_t slot_size;
   uint16_t slot_align;
-  uint8_t soo_capacity;
+  bool soo_enabled;
   bool is_hashtablez_eligible;
 
   // Returns the pointer to the hash function stored in the set.
@@ -1675,6 +1678,10 @@
       void* probed_storage,
       void (*encode_probed_element)(void* probed_storage, h2_t h2,
                                     size_t source_offset, size_t h1));
+
+  uint8_t soo_capacity() const {
+    return static_cast<uint8_t>(soo_enabled ? SooCapacity() : 0);
+  }
 };
 
 // Returns the maximum valid size for a table with 1-byte slots.
@@ -3606,8 +3613,7 @@
         static_cast<uint32_t>(sizeof(key_type)),
         static_cast<uint32_t>(sizeof(value_type)),
         static_cast<uint16_t>(sizeof(slot_type)),
-        static_cast<uint16_t>(alignof(slot_type)),
-        static_cast<uint8_t>(SooEnabled() ? SooCapacity() : 0),
+        static_cast<uint16_t>(alignof(slot_type)), SooEnabled(),
         ShouldSampleHashtablezInfoForAlloc<CharAlloc>(),
         // TODO(b/328722020): try to type erase
         // for standard layout and alignof(Hash) <= alignof(CommonFields).
diff --git a/third_party/abseil-cpp/absl/container/internal/raw_hash_set_test.cc b/third_party/abseil-cpp/absl/container/internal/raw_hash_set_test.cc
index 404d1eb..9a323c41b 100644
--- a/third_party/abseil-cpp/absl/container/internal/raw_hash_set_test.cc
+++ b/third_party/abseil-cpp/absl/container/internal/raw_hash_set_test.cc
@@ -4238,9 +4238,8 @@
   // artificially update growth info to force resize.
   absl::flat_hash_set<uint8_t, ConstUint8Hash> t(63, ConstUint8Hash{&hash});
   CommonFields& common = RawHashSetTestOnlyAccess::GetCommon(t);
-  // Assign value to the seed, so that H1 is always 0.
-  // That helps to test all buffer overflows in GrowToNextCapacity.
-  hash = common.seed().seed() << 7;
+  // Set 0 seed so that H1 is always 0.
+  common.set_no_seed_for_testing();
   ASSERT_EQ(H1(t.hash_function()(75), common.seed()), 0);
   uint8_t inserted_till = 210;
   for (uint8_t i = 0; i < inserted_till; ++i) {
diff --git a/third_party/abseil-cpp/absl/debugging/internal/stacktrace_aarch64-inl.inc b/third_party/abseil-cpp/absl/debugging/internal/stacktrace_aarch64-inl.inc
index 25073f7..1746b5d4 100644
--- a/third_party/abseil-cpp/absl/debugging/internal/stacktrace_aarch64-inl.inc
+++ b/third_party/abseil-cpp/absl/debugging/internal/stacktrace_aarch64-inl.inc
@@ -134,17 +134,13 @@
   if ((reinterpret_cast<uintptr_t>(new_frame_pointer) & 7) != 0)
     return nullptr;
 
-  // Check that alleged frame pointer is actually readable. This is to
-  // prevent "double fault" in case we hit the first fault due to e.g.
-  // stack corruption.
-  if (!absl::debugging_internal::AddressIsReadable(
-          new_frame_pointer)) {
-    return nullptr;
-  }
-
+  uintptr_t new_fp_comparable = reinterpret_cast<uintptr_t>(new_frame_pointer);
   // Only check the size if both frames are in the same stack.
-  if (InsideSignalStack(new_frame_pointer, stack_info) ==
-      InsideSignalStack(old_frame_pointer, stack_info)) {
+  const bool old_inside_signal_stack =
+      InsideSignalStack(old_frame_pointer, stack_info);
+  const bool new_inside_signal_stack =
+      InsideSignalStack(new_frame_pointer, stack_info);
+  if (new_inside_signal_stack == old_inside_signal_stack) {
     // Check frame size.  In strict mode, we assume frames to be under
     // 100,000 bytes.  In non-strict mode, we relax the limit to 1MB.
     const size_t max_size = STRICT_UNWINDING ? 100000 : 1000000;
@@ -158,16 +154,15 @@
     if (frame_size > max_size) {
       size_t stack_low = stack_info->stack_low;
       size_t stack_high = stack_info->stack_high;
-      if (InsideSignalStack(new_frame_pointer, stack_info)) {
+      if (new_inside_signal_stack) {
         stack_low = stack_info->sig_stack_low;
         stack_high = stack_info->sig_stack_high;
       }
       if (stack_high < kUnknownStackEnd &&
           static_cast<size_t>(getpagesize()) < stack_low) {
-        const uintptr_t new_fp_u =
-            reinterpret_cast<uintptr_t>(new_frame_pointer);
         // Stack bounds are known.
-        if (!(stack_low < new_fp_u && new_fp_u <= stack_high)) {
+        if (!(stack_low < new_fp_comparable &&
+              new_fp_comparable <= stack_high)) {
           // new_frame_pointer is not within a known stack.
           return nullptr;
         }
@@ -177,8 +172,19 @@
       }
     }
   }
+  // New frame pointer is valid if it is inside either known stack or readable.
+  // This assumes that everything within either known stack is readable. Outside
+  // either known stack but readable is unexpected, and possibly corrupt, but
+  // for now assume it is valid. If it isn't actually valid, the next frame will
+  // be corrupt and we will detect that next iteration.
+  if (new_inside_signal_stack ||
+      (new_fp_comparable >= stack_info->stack_low &&
+       new_fp_comparable < stack_info->stack_high) ||
+      absl::debugging_internal::AddressIsReadable(new_frame_pointer)) {
+    return new_frame_pointer;
+  }
 
-  return new_frame_pointer;
+  return nullptr;
 }
 
 template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT>
diff --git a/third_party/abseil-cpp/absl/log/check.h b/third_party/abseil-cpp/absl/log/check.h
index 50f633d..9e2219b 100644
--- a/third_party/abseil-cpp/absl/log/check.h
+++ b/third_party/abseil-cpp/absl/log/check.h
@@ -42,7 +42,8 @@
 
 // CHECK()
 //
-// `CHECK` terminates the program with a fatal error if `condition` is not true.
+// `CHECK` enforces that the `condition` is true. If the condition is false,
+// the program is terminated with a fatal error.
 //
 // The message may include additional information such as stack traces, when
 // available.
diff --git a/third_party/abseil-cpp/absl/log/check_test_impl.inc b/third_party/abseil-cpp/absl/log/check_test_impl.inc
index be58a3d..7a0000e 100644
--- a/third_party/abseil-cpp/absl/log/check_test_impl.inc
+++ b/third_party/abseil-cpp/absl/log/check_test_impl.inc
@@ -39,6 +39,7 @@
 namespace absl_log_internal {
 
 using ::testing::AllOf;
+using ::testing::AnyOf;
 using ::testing::HasSubstr;
 using ::testing::Not;
 
@@ -634,14 +635,12 @@
 TEST(CHECKDeathTest, TestPointerPrintedAsNumberDespiteAbslStringify) {
   const auto* p = reinterpret_cast<const PointerIsStringifiable*>(0x1234);
 
-#ifdef _MSC_VER
   EXPECT_DEATH(
       ABSL_TEST_CHECK_EQ(p, nullptr),
-      HasSubstr("Check failed: p == nullptr (0000000000001234 vs. (null))"));
-#else   // _MSC_VER
-  EXPECT_DEATH(ABSL_TEST_CHECK_EQ(p, nullptr),
-               HasSubstr("Check failed: p == nullptr (0x1234 vs. (null))"));
-#endif  // _MSC_VER
+      AnyOf(
+        HasSubstr("Check failed: p == nullptr (0000000000001234 vs. (null))"),
+        HasSubstr("Check failed: p == nullptr (0x1234 vs. (null))")
+      ));
 }
 
 // An uncopyable object with operator<<.
diff --git a/third_party/abseil-cpp/absl/strings/BUILD.bazel b/third_party/abseil-cpp/absl/strings/BUILD.bazel
index 49562f7..bb152ac 100644
--- a/third_party/abseil-cpp/absl/strings/BUILD.bazel
+++ b/third_party/abseil-cpp/absl/strings/BUILD.bazel
@@ -329,6 +329,7 @@
     visibility = ["//visibility:private"],
     deps = [
         ":internal",
+        ":string_view",
         "//absl/base:core_headers",
         "@googletest//:gtest",
         "@googletest//:gtest_main",
@@ -1316,6 +1317,7 @@
     linkopts = ABSL_DEFAULT_LINKOPTS,
     visibility = ["//visibility:private"],
     deps = [
+        ":internal",
         ":strings",
         "//absl/base:config",
         "//absl/base:core_headers",
diff --git a/third_party/abseil-cpp/absl/strings/BUILD.gn b/third_party/abseil-cpp/absl/strings/BUILD.gn
index dbc5f48..51c3e690 100644
--- a/third_party/abseil-cpp/absl/strings/BUILD.gn
+++ b/third_party/abseil-cpp/absl/strings/BUILD.gn
@@ -138,6 +138,7 @@
   ]
   visibility = [ ":*" ]
   deps = [
+    ":internal",
     ":string_view",
     ":strings",
     "//third_party/abseil-cpp/absl/base:config",
diff --git a/third_party/abseil-cpp/absl/strings/CMakeLists.txt b/third_party/abseil-cpp/absl/strings/CMakeLists.txt
index ee73860..547ef26 100644
--- a/third_party/abseil-cpp/absl/strings/CMakeLists.txt
+++ b/third_party/abseil-cpp/absl/strings/CMakeLists.txt
@@ -243,6 +243,7 @@
   COPTS
     ${ABSL_TEST_COPTS}
   DEPS
+    absl::string_view
     absl::strings_internal
     absl::base
     absl::core_headers
@@ -518,6 +519,7 @@
     absl::utility
     absl::int128
     absl::span
+    absl::strings_internal
 )
 
 absl_cc_test(
diff --git a/third_party/abseil-cpp/absl/strings/internal/str_format/arg.cc b/third_party/abseil-cpp/absl/strings/internal/str_format/arg.cc
index eeb2108..103c85d1 100644
--- a/third_party/abseil-cpp/absl/strings/internal/str_format/arg.cc
+++ b/third_party/abseil-cpp/absl/strings/internal/str_format/arg.cc
@@ -34,6 +34,7 @@
 #include "absl/numeric/int128.h"
 #include "absl/strings/internal/str_format/extension.h"
 #include "absl/strings/internal/str_format/float_conversion.h"
+#include "absl/strings/internal/utf8.h"
 #include "absl/strings/numbers.h"
 #include "absl/strings/string_view.h"
 
@@ -311,68 +312,16 @@
                                conv.has_left_flag());
 }
 
-struct ShiftState {
-  bool saw_high_surrogate = false;
-  uint8_t bits = 0;
-};
-
-// Converts `v` from UTF-16 or UTF-32 to UTF-8 and writes to `buf`. `buf` is
-// assumed to have enough space for the output. `s` is used to carry state
-// between successive calls with a UTF-16 surrogate pair. Returns the number of
-// chars written, or `static_cast<size_t>(-1)` on failure.
-//
-// This is basically std::wcrtomb(), but always outputting UTF-8 instead of
-// respecting the current locale.
-inline size_t WideToUtf8(wchar_t wc, char *buf, ShiftState &s) {
-  const auto v = static_cast<uint32_t>(wc);
-  if (v < 0x80) {
-    *buf = static_cast<char>(v);
-    return 1;
-  } else if (v < 0x800) {
-    *buf++ = static_cast<char>(0xc0 | (v >> 6));
-    *buf = static_cast<char>(0x80 | (v & 0x3f));
-    return 2;
-  } else if (v < 0xd800 || (v - 0xe000) < 0x2000) {
-    *buf++ = static_cast<char>(0xe0 | (v >> 12));
-    *buf++ = static_cast<char>(0x80 | ((v >> 6) & 0x3f));
-    *buf = static_cast<char>(0x80 | (v & 0x3f));
-    return 3;
-  } else if ((v - 0x10000) < 0x100000) {
-    *buf++ = static_cast<char>(0xf0 | (v >> 18));
-    *buf++ = static_cast<char>(0x80 | ((v >> 12) & 0x3f));
-    *buf++ = static_cast<char>(0x80 | ((v >> 6) & 0x3f));
-    *buf = static_cast<char>(0x80 | (v & 0x3f));
-    return 4;
-  } else if (v < 0xdc00) {
-    s.saw_high_surrogate = true;
-    s.bits = static_cast<uint8_t>(v & 0x3);
-    const uint8_t high_bits = ((v >> 6) & 0xf) + 1;
-    *buf++ = static_cast<char>(0xf0 | (high_bits >> 2));
-    *buf =
-        static_cast<char>(0x80 | static_cast<uint8_t>((high_bits & 0x3) << 4) |
-                          static_cast<uint8_t>((v >> 2) & 0xf));
-    return 2;
-  } else if (v < 0xe000 && s.saw_high_surrogate) {
-    *buf++ = static_cast<char>(0x80 | static_cast<uint8_t>(s.bits << 4) |
-                               static_cast<uint8_t>((v >> 6) & 0xf));
-    *buf = static_cast<char>(0x80 | (v & 0x3f));
-    s.saw_high_surrogate = false;
-    s.bits = 0;
-    return 2;
-  } else {
-    return static_cast<size_t>(-1);
-  }
-}
-
 inline bool ConvertStringArg(const wchar_t *v,
                              size_t len,
                              const FormatConversionSpecImpl conv,
                              FormatSinkImpl *sink) {
   FixedArray<char> mb(len * 4);
-  ShiftState s;
+  strings_internal::ShiftState s;
   size_t chars_written = 0;
   for (size_t i = 0; i < len; ++i) {
-    const size_t chars = WideToUtf8(v[i], &mb[chars_written], s);
+    const size_t chars =
+        strings_internal::WideToUtf8(v[i], &mb[chars_written], s);
     if (chars == static_cast<size_t>(-1)) { return false; }
     chars_written += chars;
   }
@@ -382,8 +331,8 @@
 bool ConvertWCharTImpl(wchar_t v, const FormatConversionSpecImpl conv,
                        FormatSinkImpl *sink) {
   char mb[4];
-  ShiftState s;
-  const size_t chars_written = WideToUtf8(v, mb, s);
+  strings_internal::ShiftState s;
+  const size_t chars_written = strings_internal::WideToUtf8(v, mb, s);
   return chars_written != static_cast<size_t>(-1) && !s.saw_high_surrogate &&
          ConvertStringArg(string_view(mb, chars_written), conv, sink);
 }
diff --git a/third_party/abseil-cpp/absl/strings/internal/utf8.cc b/third_party/abseil-cpp/absl/strings/internal/utf8.cc
index 7ecb93d..4370c7c 100644
--- a/third_party/abseil-cpp/absl/strings/internal/utf8.cc
+++ b/third_party/abseil-cpp/absl/strings/internal/utf8.cc
@@ -16,6 +16,11 @@
 
 #include "absl/strings/internal/utf8.h"
 
+#include <cstddef>
+#include <cstdint>
+
+#include "absl/base/config.h"
+
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 namespace strings_internal {
@@ -48,6 +53,47 @@
   }
 }
 
+size_t WideToUtf8(wchar_t wc, char *buf, ShiftState &s) {
+  const auto v = static_cast<uint32_t>(wc);
+  if (v < 0x80) {
+    *buf = static_cast<char>(v);
+    return 1;
+  } else if (v < 0x800) {
+    *buf++ = static_cast<char>(0xc0 | (v >> 6));
+    *buf = static_cast<char>(0x80 | (v & 0x3f));
+    return 2;
+  } else if (v < 0xd800 || (v - 0xe000) < 0x2000) {
+    *buf++ = static_cast<char>(0xe0 | (v >> 12));
+    *buf++ = static_cast<char>(0x80 | ((v >> 6) & 0x3f));
+    *buf = static_cast<char>(0x80 | (v & 0x3f));
+    return 3;
+  } else if ((v - 0x10000) < 0x100000) {
+    *buf++ = static_cast<char>(0xf0 | (v >> 18));
+    *buf++ = static_cast<char>(0x80 | ((v >> 12) & 0x3f));
+    *buf++ = static_cast<char>(0x80 | ((v >> 6) & 0x3f));
+    *buf = static_cast<char>(0x80 | (v & 0x3f));
+    return 4;
+  } else if (v < 0xdc00) {
+    s.saw_high_surrogate = true;
+    s.bits = static_cast<uint8_t>(v & 0x3);
+    const uint8_t high_bits = ((v >> 6) & 0xf) + 1;
+    *buf++ = static_cast<char>(0xf0 | (high_bits >> 2));
+    *buf =
+        static_cast<char>(0x80 | static_cast<uint8_t>((high_bits & 0x3) << 4) |
+                          static_cast<uint8_t>((v >> 2) & 0xf));
+    return 2;
+  } else if (v < 0xe000 && s.saw_high_surrogate) {
+    *buf++ = static_cast<char>(0x80 | static_cast<uint8_t>(s.bits << 4) |
+                               static_cast<uint8_t>((v >> 6) & 0xf));
+    *buf = static_cast<char>(0x80 | (v & 0x3f));
+    s.saw_high_surrogate = false;
+    s.bits = 0;
+    return 2;
+  } else {
+    return static_cast<size_t>(-1);
+  }
+}
+
 }  // namespace strings_internal
 ABSL_NAMESPACE_END
 }  // namespace absl
diff --git a/third_party/abseil-cpp/absl/strings/internal/utf8.h b/third_party/abseil-cpp/absl/strings/internal/utf8.h
index 32fb109..f240408d 100644
--- a/third_party/abseil-cpp/absl/strings/internal/utf8.h
+++ b/third_party/abseil-cpp/absl/strings/internal/utf8.h
@@ -43,6 +43,20 @@
 enum { kMaxEncodedUTF8Size = 4 };
 size_t EncodeUTF8Char(char *buffer, char32_t utf8_char);
 
+struct ShiftState {
+  bool saw_high_surrogate = false;
+  uint8_t bits = 0;
+};
+
+// Converts `wc` from UTF-16 or UTF-32 to UTF-8 and writes to `buf`. `buf` is
+// assumed to have enough space for the output. `s` is used to carry state
+// between successive calls with a UTF-16 surrogate pair. Returns the number of
+// chars written, or `static_cast<size_t>(-1)` on failure.
+//
+// This is basically std::wcrtomb(), but always outputting UTF-8 instead of
+// respecting the current locale.
+size_t WideToUtf8(wchar_t wc, char *buf, ShiftState &s);
+
 }  // namespace strings_internal
 ABSL_NAMESPACE_END
 }  // namespace absl
diff --git a/third_party/abseil-cpp/absl/strings/internal/utf8_test.cc b/third_party/abseil-cpp/absl/strings/internal/utf8_test.cc
index 88dd5036..62322dd1 100644
--- a/third_party/abseil-cpp/absl/strings/internal/utf8_test.cc
+++ b/third_party/abseil-cpp/absl/strings/internal/utf8_test.cc
@@ -14,14 +14,29 @@
 
 #include "absl/strings/internal/utf8.h"
 
+#include <cstddef>
 #include <cstdint>
+#include <cstring>
+#include <string>
+#include <type_traits>
 #include <utility>
+#include <vector>
 
+#include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include "absl/base/port.h"
+#include "absl/strings/string_view.h"
 
 namespace {
 
+using ::absl::strings_internal::kMaxEncodedUTF8Size;
+using ::absl::strings_internal::ShiftState;
+using ::absl::strings_internal::WideToUtf8;
+using ::testing::StartsWith;
+using ::testing::TestParamInfo;
+using ::testing::TestWithParam;
+using ::testing::ValuesIn;
+
 #if !defined(__cpp_char8_t)
 #if defined(__clang__)
 #pragma clang diagnostic push
@@ -33,12 +48,12 @@
                                               {0x00010000, u8"\U00010000"},
                                               {0x0000FFFF, u8"\U0000FFFF"},
                                               {0x0010FFFD, u8"\U0010FFFD"}};
-  for (auto &test : tests) {
+  for (auto& test : tests) {
     char buf0[7] = {'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00'};
     char buf1[7] = {'\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF'};
-    char *buf0_written =
+    char* buf0_written =
         &buf0[absl::strings_internal::EncodeUTF8Char(buf0, test.first)];
-    char *buf1_written =
+    char* buf1_written =
         &buf1[absl::strings_internal::EncodeUTF8Char(buf1, test.first)];
     int apparent_length = 7;
     while (buf0[apparent_length - 1] == '\x00' &&
@@ -63,4 +78,169 @@
 #endif
 #endif  // !defined(__cpp_char8_t)
 
+struct WideToUtf8TestCase {
+  std::string description;
+  wchar_t input;
+  std::string expected_utf8_str;
+  size_t expected_bytes_written;
+  ShiftState initial_state = {false, 0};
+  ShiftState expected_state = {false, 0};
+};
+
+std::vector<WideToUtf8TestCase> GetWideToUtf8TestCases() {
+  constexpr size_t kError = static_cast<size_t>(-1);
+  std::vector<WideToUtf8TestCase> cases = {
+      {"ASCII_A", L'A', "A", 1},
+      {"NullChar", L'\0', std::string("\0", 1), 1},
+      {"ASCII_Max_7F", L'\x7F', "\x7F", 1},
+
+      {"TwoByte_Min_80", L'\u0080', "\xC2\x80", 2},
+      {"PoundSign_A3", L'\u00A3', "\xC2\xA3", 2},
+      {"TwoByte_Max_7FF", L'\u07FF', "\xDF\xBF", 2},
+
+      {"ThreeByte_Min_800", L'\u0800', "\xE0\xA0\x80", 3},
+      {"EuroSign_20AC", L'\u20AC', "\xE2\x82\xAC", 3},
+      {"BMP_MaxBeforeSurrogates_D7FF", L'\uD7FF', "\xED\x9F\xBF", 3},
+      {"BMP_FFFF", L'\uFFFF', "\xEF\xBF\xBF", 3},
+
+      {"IsolatedHighSurr_D800", L'\xD800', "\xF0\x90", 2, {true, 0}, {true, 0}},
+      {"IsolatedHighSurr_DBFF", L'\xDBFF', "\xF4\x8F", 2, {true, 3}, {true, 3}},
+
+      {"LowSurr_DC00_after_HighD800", L'\xDC00', "\x80\x80", 2, {true, 0}, {}},
+      {"LowSurr_DFFD_after_HighDBFF", L'\xDFFD', "\xBF\xBD", 2, {true, 3}, {}},
+      {"LowSurr_DC00_with_InitialState_saw_high_bits_1",
+       L'\xDC00',
+       "\x90\x80",
+       2,
+       {true, 1},
+       {}},
+
+      // Final state = initial on error.
+      {"Error_IsolatedLowSurr_DC00_NoPriorHigh", L'\xDC00', "", kError, {}, {}},
+      {"Error_IsolatedLowSurr_DFFF_NoPriorHigh", L'\xDFFF', "", kError, {}, {}},
+
+#if (defined(WCHAR_MAX) && WCHAR_MAX > 0xFFFF)
+      {"DirectSupplementaryChars_U10000", static_cast<wchar_t>(0x10000),
+       "\xF0\x90\x80\x80", 4},
+      {"DirectSupplementaryChars_U10FFFD", static_cast<wchar_t>(0x10FFFD),
+       "\xF4\x8F\xBF\xBD", 4},
+#endif
+  };
+
+  wchar_t minus_one = static_cast<wchar_t>(-1);
+  if constexpr (sizeof(wchar_t) == 2) {
+    cases.push_back({"WChar_MinusOne_as_FFFF", minus_one, "\xEF\xBF\xBF", 3});
+  } else {
+    cases.push_back(
+        {"Error_WChar_MinusOne_as_FFFFFFFF", minus_one, "", kError, {}, {}});
+  }
+
+  if constexpr (sizeof(wchar_t) >= 4) {
+#ifdef WCHAR_MAX
+    if (static_cast<uintmax_t>(WCHAR_MAX) >= 0x110000UL) {
+      cases.push_back({"Error_OutOfRange_110000",
+                       static_cast<wchar_t>(0x110000UL),
+                       "",
+                       kError,
+                       {},
+                       {}});
+    }
+#else
+    cases.push_back({"Error_OutOfRange_110000_fallback",
+                     static_cast<wchar_t>(0x110000UL),
+                     "",
+                     kError,
+                     {},
+                     {}});
+#endif
+  }
+  return cases;
+}
+
+class WideToUtf8ParamTest : public TestWithParam<WideToUtf8TestCase> {};
+
+TEST_P(WideToUtf8ParamTest, SingleCharConversion) {
+  const auto& test_case = GetParam();
+  ShiftState state = test_case.initial_state;
+  constexpr char kFillChar = '\xAB';
+  std::string buffer(32, kFillChar);
+
+  size_t bytes_written = WideToUtf8(test_case.input, buffer.data(), state);
+
+  EXPECT_EQ(bytes_written, test_case.expected_bytes_written);
+  EXPECT_THAT(buffer, StartsWith(test_case.expected_utf8_str));
+
+  // The remaining bytes should be unchanged.
+  ASSERT_LT(test_case.expected_utf8_str.length(), buffer.size());
+  EXPECT_EQ(buffer[test_case.expected_utf8_str.length()], kFillChar);
+
+  EXPECT_EQ(state.saw_high_surrogate,
+            test_case.expected_state.saw_high_surrogate);
+  EXPECT_EQ(state.bits, test_case.expected_state.bits);
+}
+
+INSTANTIATE_TEST_SUITE_P(WideCharToUtf8Conversion, WideToUtf8ParamTest,
+                         ValuesIn(GetWideToUtf8TestCases()),
+                         [](auto info) { return info.param.description; });
+
+// Comprehensive test string for validating wchar_t to UTF-8 conversion.
+// This string is designed to cover a variety of Unicode character types and
+// sequences:
+// 1. Basic ASCII characters (within names, numbers, and spacing).
+// 2. Common 2-byte UTF-8 sequences:
+//    - Accented Latin characters (e.g., 'á' in "Holá").
+//    - Hebrew text with combining vowel points (e.g., "שָׁלוֹם").
+// 3. Common 3-byte UTF-8 sequences:
+//    - Currency symbols (e.g., '€').
+//    - CJK characters (e.g., "你好", "中").
+//    - Components of complex emojis like the Zero Width Joiner (ZWJ) and
+//      Heart symbol.
+// 4. Various 4-byte UTF-8 sequences (representing Supplementary Plane
+// characters):
+//    - An emoji with a skin tone modifier ("👍🏻").
+//    - A flag emoji composed of regional indicators ("🇺🇸").
+//    - A complex ZWJ emoji sequence ("👩‍❤️‍💋‍👨") combining
+//      SP characters (👩, 💋, 👨) with BMP characters (ZWJ and ❤️).
+//    - These are critical for testing the correct handling of surrogate pairs
+//      when wchar_t is 2 bytes (e.g., on Windows).
+// The goal is to ensure accurate conversion across a diverse set of
+// characters.
+//
+// clang-format off
+#define WIDE_STRING_LITERAL L"Holá €1 你好 שָׁלוֹם 👍🏻🇺🇸👩‍❤️‍💋‍👨 中"
+#define UTF8_STRING_LITERAL u8"Holá €1 你好 שָׁלוֹם 👍🏻🇺🇸👩‍❤️‍💋‍👨 中"
+// clang-format on
+
+absl::string_view GetUtf8TestString() {
+  // `u8""` forces UTF-8 encoding; MSVC will default to e.g. CP1252 (and warn)
+  // without it. However, the resulting character type differs between pre-C++20
+  // (`char`) and C++20 (`char8_t`). So deduce the right character type for all
+  // C++ versions, init it with UTF-8, then `memcpy()` to get the result as a
+  // `char*`
+  static absl::string_view kUtf8TestString = [] {
+    using ConstChar8T = std::remove_reference_t<decltype(*u8"a")>;
+    constexpr ConstChar8T kOutputUtf8[] = UTF8_STRING_LITERAL;
+    static char output[sizeof kOutputUtf8];
+    std::memcpy(output, kOutputUtf8, sizeof kOutputUtf8);
+    return output;
+  }();
+
+  return kUtf8TestString;
+}
+
+TEST(WideToUtf8, FullString) {
+  std::string buffer(kMaxEncodedUTF8Size * sizeof(WIDE_STRING_LITERAL), '\0');
+  char* buffer_ptr = buffer.data();
+
+  ShiftState state;
+  for (const wchar_t wc : WIDE_STRING_LITERAL) {
+    buffer_ptr += WideToUtf8(wc, buffer_ptr, state);
+  }
+
+  EXPECT_THAT(buffer, StartsWith(GetUtf8TestString()));
+}
+
+#undef WIDE_STRING_LITERAL
+#undef UTF8_STRING_LITERAL
+
 }  // namespace
diff --git a/third_party/abseil-cpp/symbols_arm64_dbg.def b/third_party/abseil-cpp/symbols_arm64_dbg.def
index 6d2aa0d..14e46ace 100644
--- a/third_party/abseil-cpp/symbols_arm64_dbg.def
+++ b/third_party/abseil-cpp/symbols_arm64_dbg.def
@@ -2072,6 +2072,7 @@
     ??0ScopedEnable@SchedulingGuard@base_internal@absl@@QEAA@XZ
     ??0ScopedMinLogLevel@log_internal@absl@@QEAA@W4LogSeverityAtLeast@2@@Z
     ??0ScopedStderrThreshold@absl@@QEAA@W4LogSeverityAtLeast@1@@Z
+    ??0ShiftState@strings_internal@absl@@QEAA@XZ
     ??0SpinLock@base_internal@absl@@QEAA@W4SchedulingMode@12@@Z
     ??0SpinLock@base_internal@absl@@QEAA@XZ
     ??0SpinLockHolder@base_internal@absl@@QEAA@PEAVSpinLock@12@@Z
@@ -4290,6 +4291,7 @@
     ?WebSafeBase64Escape@absl@@YA?AV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$basic_string_view@DU?$char_traits@D@__Cr@std@@@34@@Z
     ?WebSafeBase64Escape@absl@@YAXV?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@PEAV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@34@@Z
     ?WebSafeBase64Unescape@absl@@YA_NV?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@PEAV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@34@@Z
+    ?WideToUtf8@strings_internal@absl@@YA_K_WPEADAEAUShiftState@12@@Z
     ?WithMetadataFrom@LogMessage@log_internal@absl@@QEAAAEAV123@AEBVLogEntry@3@@Z
     ?WithPerror@LogMessage@log_internal@absl@@QEAAAEAV123@XZ
     ?WithThreadID@LogMessage@log_internal@absl@@QEAAAEAV123@I@Z
@@ -5388,6 +5390,7 @@
     ?slot_array@HeapOrSoo@container_internal@absl@@QEBA?ATMaybeInitializedPtr@23@XZ
     ?slot_offset@RawHashSetLayout@container_internal@absl@@QEBA_KXZ
     ?slots_union@CommonFields@container_internal@absl@@QEBA?ATMaybeInitializedPtr@23@XZ
+    ?soo_capacity@PolicyFunctions@container_internal@absl@@QEBAEXZ
     ?soo_data@CommonFields@container_internal@absl@@QEAAPEAXXZ
     ?soo_data@CommonFields@container_internal@absl@@QEBAPEBXXZ
     ?soo_iterator@?$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@@AEAA?AViterator@123@XZ
diff --git a/third_party/abseil-cpp/symbols_arm64_rel.def b/third_party/abseil-cpp/symbols_arm64_rel.def
index 47331ae7..c6ad2fa 100644
--- a/third_party/abseil-cpp/symbols_arm64_rel.def
+++ b/third_party/abseil-cpp/symbols_arm64_rel.def
@@ -1270,6 +1270,7 @@
     ?WebSafeBase64Escape@absl@@YA?AV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$basic_string_view@DU?$char_traits@D@__Cr@std@@@34@@Z
     ?WebSafeBase64Escape@absl@@YAXV?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@PEAV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@34@@Z
     ?WebSafeBase64Unescape@absl@@YA_NV?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@PEAV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@34@@Z
+    ?WideToUtf8@strings_internal@absl@@YA_K_WPEADAEAUShiftState@12@@Z
     ?WithMetadataFrom@LogMessage@log_internal@absl@@QEAAAEAV123@AEBVLogEntry@3@@Z
     ?WithPerror@LogMessage@log_internal@absl@@QEAAAEAV123@XZ
     ?WithThreadID@LogMessage@log_internal@absl@@QEAAAEAV123@I@Z
diff --git a/third_party/abseil-cpp/symbols_x64_dbg.def b/third_party/abseil-cpp/symbols_x64_dbg.def
index c863925..db9f1cc 100644
--- a/third_party/abseil-cpp/symbols_x64_dbg.def
+++ b/third_party/abseil-cpp/symbols_x64_dbg.def
@@ -2074,6 +2074,7 @@
     ??0ScopedEnable@SchedulingGuard@base_internal@absl@@QEAA@XZ
     ??0ScopedMinLogLevel@log_internal@absl@@QEAA@W4LogSeverityAtLeast@2@@Z
     ??0ScopedStderrThreshold@absl@@QEAA@W4LogSeverityAtLeast@1@@Z
+    ??0ShiftState@strings_internal@absl@@QEAA@XZ
     ??0SpinLock@base_internal@absl@@QEAA@W4SchedulingMode@12@@Z
     ??0SpinLock@base_internal@absl@@QEAA@XZ
     ??0SpinLockHolder@base_internal@absl@@QEAA@PEAVSpinLock@12@@Z
@@ -4294,6 +4295,7 @@
     ?WebSafeBase64Escape@absl@@YA?AV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$basic_string_view@DU?$char_traits@D@__Cr@std@@@34@@Z
     ?WebSafeBase64Escape@absl@@YAXV?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@PEAV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@34@@Z
     ?WebSafeBase64Unescape@absl@@YA_NV?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@PEAV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@34@@Z
+    ?WideToUtf8@strings_internal@absl@@YA_K_WPEADAEAUShiftState@12@@Z
     ?WithMetadataFrom@LogMessage@log_internal@absl@@QEAAAEAV123@AEBVLogEntry@3@@Z
     ?WithPerror@LogMessage@log_internal@absl@@QEAAAEAV123@XZ
     ?WithThreadID@LogMessage@log_internal@absl@@QEAAAEAV123@I@Z
@@ -5393,6 +5395,7 @@
     ?slot_array@HeapOrSoo@container_internal@absl@@QEBA?ATMaybeInitializedPtr@23@XZ
     ?slot_offset@RawHashSetLayout@container_internal@absl@@QEBA_KXZ
     ?slots_union@CommonFields@container_internal@absl@@QEBA?ATMaybeInitializedPtr@23@XZ
+    ?soo_capacity@PolicyFunctions@container_internal@absl@@QEBAEXZ
     ?soo_data@CommonFields@container_internal@absl@@QEAAPEAXXZ
     ?soo_data@CommonFields@container_internal@absl@@QEBAPEBXXZ
     ?soo_iterator@?$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@@AEAA?AViterator@123@XZ
diff --git a/third_party/abseil-cpp/symbols_x64_rel.def b/third_party/abseil-cpp/symbols_x64_rel.def
index 4e01aa9..05c9783c 100644
--- a/third_party/abseil-cpp/symbols_x64_rel.def
+++ b/third_party/abseil-cpp/symbols_x64_rel.def
@@ -1270,6 +1270,7 @@
     ?WebSafeBase64Escape@absl@@YA?AV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$basic_string_view@DU?$char_traits@D@__Cr@std@@@34@@Z
     ?WebSafeBase64Escape@absl@@YAXV?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@PEAV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@34@@Z
     ?WebSafeBase64Unescape@absl@@YA_NV?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@PEAV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@34@@Z
+    ?WideToUtf8@strings_internal@absl@@YA_K_WPEADAEAUShiftState@12@@Z
     ?WithMetadataFrom@LogMessage@log_internal@absl@@QEAAAEAV123@AEBVLogEntry@3@@Z
     ?WithPerror@LogMessage@log_internal@absl@@QEAAAEAV123@XZ
     ?WithThreadID@LogMessage@log_internal@absl@@QEAAAEAV123@I@Z
diff --git a/third_party/abseil-cpp/symbols_x64_rel_asan.def b/third_party/abseil-cpp/symbols_x64_rel_asan.def
index 00127bfc..456c01a 100644
--- a/third_party/abseil-cpp/symbols_x64_rel_asan.def
+++ b/third_party/abseil-cpp/symbols_x64_rel_asan.def
@@ -1307,6 +1307,7 @@
     ?WebSafeBase64Escape@absl@@YA?AV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$basic_string_view@DU?$char_traits@D@__Cr@std@@@34@@Z
     ?WebSafeBase64Escape@absl@@YAXV?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@PEAV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@34@@Z
     ?WebSafeBase64Unescape@absl@@YA_NV?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@PEAV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@34@@Z
+    ?WideToUtf8@strings_internal@absl@@YA_K_WPEADAEAUShiftState@12@@Z
     ?WithMetadataFrom@LogMessage@log_internal@absl@@QEAAAEAV123@AEBVLogEntry@3@@Z
     ?WithPerror@LogMessage@log_internal@absl@@QEAAAEAV123@XZ
     ?WithThreadID@LogMessage@log_internal@absl@@QEAAAEAV123@I@Z
diff --git a/third_party/abseil-cpp/symbols_x86_dbg.def b/third_party/abseil-cpp/symbols_x86_dbg.def
index 83347e5..3aa6864e 100644
--- a/third_party/abseil-cpp/symbols_x86_dbg.def
+++ b/third_party/abseil-cpp/symbols_x86_dbg.def
@@ -2072,6 +2072,7 @@
     ??0ScopedEnable@SchedulingGuard@base_internal@absl@@QAE@XZ
     ??0ScopedMinLogLevel@log_internal@absl@@QAE@W4LogSeverityAtLeast@2@@Z
     ??0ScopedStderrThreshold@absl@@QAE@W4LogSeverityAtLeast@1@@Z
+    ??0ShiftState@strings_internal@absl@@QAE@XZ
     ??0SpinLock@base_internal@absl@@QAE@W4SchedulingMode@12@@Z
     ??0SpinLock@base_internal@absl@@QAE@XZ
     ??0SpinLockHolder@base_internal@absl@@QAE@PAVSpinLock@12@@Z
@@ -4292,6 +4293,7 @@
     ?WebSafeBase64Escape@absl@@YA?AV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$basic_string_view@DU?$char_traits@D@__Cr@std@@@34@@Z
     ?WebSafeBase64Escape@absl@@YAXV?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@PAV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@34@@Z
     ?WebSafeBase64Unescape@absl@@YA_NV?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@PAV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@34@@Z
+    ?WideToUtf8@strings_internal@absl@@YAI_WPADAAUShiftState@12@@Z
     ?WithMetadataFrom@LogMessage@log_internal@absl@@QAEAAV123@ABVLogEntry@3@@Z
     ?WithPerror@LogMessage@log_internal@absl@@QAEAAV123@XZ
     ?WithThreadID@LogMessage@log_internal@absl@@QAEAAV123@I@Z
@@ -5391,6 +5393,7 @@
     ?slot_array@HeapOrSoo@container_internal@absl@@QBE?ATMaybeInitializedPtr@23@XZ
     ?slot_offset@RawHashSetLayout@container_internal@absl@@QBEIXZ
     ?slots_union@CommonFields@container_internal@absl@@QBE?ATMaybeInitializedPtr@23@XZ
+    ?soo_capacity@PolicyFunctions@container_internal@absl@@QBEEXZ
     ?soo_data@CommonFields@container_internal@absl@@QAEPAXXZ
     ?soo_data@CommonFields@container_internal@absl@@QBEPBXXZ
     ?soo_iterator@?$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@@AAE?AViterator@123@XZ
diff --git a/third_party/abseil-cpp/symbols_x86_rel.def b/third_party/abseil-cpp/symbols_x86_rel.def
index f5a620f..bb4bb38 100644
--- a/third_party/abseil-cpp/symbols_x86_rel.def
+++ b/third_party/abseil-cpp/symbols_x86_rel.def
@@ -1278,6 +1278,7 @@
     ?WebSafeBase64Escape@absl@@YA?AV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$basic_string_view@DU?$char_traits@D@__Cr@std@@@34@@Z
     ?WebSafeBase64Escape@absl@@YAXV?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@PAV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@34@@Z
     ?WebSafeBase64Unescape@absl@@YA_NV?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@PAV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@34@@Z
+    ?WideToUtf8@strings_internal@absl@@YAI_WPADAAUShiftState@12@@Z
     ?WithMetadataFrom@LogMessage@log_internal@absl@@QAEAAV123@ABVLogEntry@3@@Z
     ?WithPerror@LogMessage@log_internal@absl@@QAEAAV123@XZ
     ?WithThreadID@LogMessage@log_internal@absl@@QAEAAV123@I@Z
diff --git a/third_party/angle b/third_party/angle
index 1c7fa5f..8357b6a 160000
--- a/third_party/angle
+++ b/third_party/angle
@@ -1 +1 @@
-Subproject commit 1c7fa5f847f236cc758f58838a9f5f958c1de0de
+Subproject commit 8357b6a24cae20d24058cfef3277066c4993a1ae
diff --git a/third_party/blink/public/mojom/lcp_critical_path_predictor/lcp_critical_path_predictor.mojom b/third_party/blink/public/mojom/lcp_critical_path_predictor/lcp_critical_path_predictor.mojom
index 07a2a64..fa5b976 100644
--- a/third_party/blink/public/mojom/lcp_critical_path_predictor/lcp_critical_path_predictor.mojom
+++ b/third_party/blink/public/mojom/lcp_critical_path_predictor/lcp_critical_path_predictor.mojom
@@ -71,6 +71,11 @@
   // See third_party/blink/renderer/core/lcp_critical_path_predictor/element_locator.proto
   array<mojo_base.mojom.ByteString> lcp_element_locators;
 
+  // `lcp_element_locators_all` has all type of elements where
+  // the types in `lcp_element_locators` depends
+  // kLCPCriticalPathPredictorRecordedLcpElementTypes flag.
+  array<mojo_base.mojom.ByteString> lcp_element_locators_all;
+
   // Script URLs that influence LCP, learned from past loads.
   array<url.mojom.Url> lcp_influencer_scripts;
 
diff --git a/third_party/blink/public/mojom/quota/quota_types.mojom b/third_party/blink/public/mojom/quota/quota_types.mojom
index d6a982e..dbe6011 100644
--- a/third_party/blink/public/mojom/quota/quota_types.mojom
+++ b/third_party/blink/public/mojom/quota/quota_types.mojom
@@ -4,22 +4,6 @@
 
 module blink.mojom;
 
-// Values are persisted to disk.
-// Do not renumber or delete existing values, to avoid data loss.
-// Eventually `kTemporary` will be the only supported quota storage type, but
-// since all enum values may have been persisted to disk, we cannot remove any
-// enum values until all other values have been deprecated.
-enum StorageType {
-  kTemporary = 0,
-  // TODO(https://crbug.com/1175113): Remove this type.
-  kDeprecatedPersistent = 1,
-  // TODO(https://crbug.com/1286964): Deprecate and remove this type.
-  kSyncable = 2,
-  // TODO(https://crbug.com/1095844): Remove this type.
-  kDeprecatedQuotaNotManaged = 3,
-  kUnknown = 4,
-};
-
 // The durability policy to apply to a single StorageBucket. The values are
 // persisted to the quota DB and must not be changed.
 enum BucketDurability {
diff --git a/third_party/blink/public/mojom/use_counter/metrics/webdx_feature.mojom b/third_party/blink/public/mojom/use_counter/metrics/webdx_feature.mojom
index 1b3e0e5..977d5580 100644
--- a/third_party/blink/public/mojom/use_counter/metrics/webdx_feature.mojom
+++ b/third_party/blink/public/mojom/use_counter/metrics/webdx_feature.mojom
@@ -390,6 +390,7 @@
   kWindowManagement = 332,
   kRequestclose = 333,
   kDRAFT_Uint8ArrayToFromBase64AndHex = 334,
+  kLineBreak = 335,
 
   // Add new features immediately above this line. Don't change assigned
   // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/blink/renderer/core/animation/css_dynamic_range_limit_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_dynamic_range_limit_interpolation_type.cc
index cdc91738..0311b80cd 100644
--- a/third_party/blink/renderer/core/animation/css_dynamic_range_limit_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_dynamic_range_limit_interpolation_type.cc
@@ -63,6 +63,10 @@
     ConversionCheckers& conversion_checkers) const {
   // TODO(crbug.com/40946458): Relative units should not be resolved here, but
   // that requires InterpolableDynamicRangeLimit to store calc-expressions.
+  // TODO(crbug.com/415626999): Create a TreeCountingChecker for sibling-index()
+  // and sibling-count() if necessary.
+  // TODO(crbug.com/415572412): Create a LengthUnitsChecker for relative units
+  // if necessary.
   return ConvertDynamicRangeLimit(
       StyleBuilderConverter::ConvertDynamicRangeLimit(state, value));
 }
diff --git a/third_party/blink/renderer/core/animation/css_font_palette_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_font_palette_interpolation_type.cc
index 543f6ee9..4316394 100644
--- a/third_party/blink/renderer/core/animation/css_font_palette_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_font_palette_interpolation_type.cc
@@ -71,6 +71,10 @@
     ConversionCheckers& conversion_checkers) const {
   // TODO(40946458): Don't resolve anything here, rewrite to
   // interpolate unresolved palettes.
+  // TODO(crbug.com/415626999): Create a TreeCountingChecker for sibling-index()
+  // and sibling-count() if necessary.
+  // TODO(crbug.com/415572412): Create a LengthUnitsChecker for relative units
+  // if necessary.
   return ConvertFontPalette(StyleBuilderConverterBase::ConvertFontPalette(
       state.CssToLengthConversionData(), value));
 }
diff --git a/third_party/blink/renderer/core/animation/css_font_variation_settings_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_font_variation_settings_interpolation_type.cc
index 0a9d491..84d8e145 100644
--- a/third_party/blink/renderer/core/animation/css_font_variation_settings_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_font_variation_settings_interpolation_type.cc
@@ -144,6 +144,10 @@
     const CSSValue& value,
     const StyleResolverState& state,
     ConversionCheckers&) const {
+  // TODO(crbug.com/415626999): Create a TreeCountingChecker for sibling-index()
+  // and sibling-count() if necessary.
+  // TODO(crbug.com/415572412): Create a LengthUnitsChecker for relative units
+  // if necessary.
   scoped_refptr<FontVariationSettings> settings =
       StyleBuilderConverter::ConvertFontVariationSettings(state, value);
   return ConvertFontVariationSettings(settings.get());
diff --git a/third_party/blink/renderer/core/animation/css_font_weight_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_font_weight_interpolation_type.cc
index 34b7468..f57ccfe 100644
--- a/third_party/blink/renderer/core/animation/css_font_weight_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_font_weight_interpolation_type.cc
@@ -75,6 +75,10 @@
   }
   // TODO(40946458): Should do a proper interpolation here instead of converting
   // relative units first.
+  // TODO(crbug.com/415626999): Create a TreeCountingChecker for sibling-index()
+  // and sibling-count() if necessary.
+  // TODO(crbug.com/415572412): Create a LengthUnitsChecker for relative units
+  // if necessary.
   return CreateFontWeightValue(StyleBuilderConverterBase::ConvertFontWeight(
       state.CssToLengthConversionData(), value, inherited_font_weight));
 }
diff --git a/third_party/blink/renderer/core/animation/css_rotate_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_rotate_interpolation_type.cc
index 3a6559a..68f1da7 100644
--- a/third_party/blink/renderer/core/animation/css_rotate_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_rotate_interpolation_type.cc
@@ -222,6 +222,10 @@
   // TODO(crbug.com/328182246): we should not use the resolved angle
   // here, but doing it for now, since proper fix would require
   // rewriting Quaternion and Rotation to have unresolved versions.
+  // TODO(crbug.com/415626999): Create a TreeCountingChecker for sibling-index()
+  // and sibling-count() if necessary.
+  // TODO(crbug.com/415572412): Create a LengthUnitsChecker for relative units
+  // if necessary.
   return ConvertRotation(
       OptionalRotation(StyleBuilderConverter::ConvertRotation(
           CSSToLengthConversionData(&state.GetElement()), value)));
diff --git a/third_party/blink/renderer/core/animation/css_scale_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_scale_interpolation_type.cc
index a4ed5454..d24dfa7 100644
--- a/third_party/blink/renderer/core/animation/css_scale_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_scale_interpolation_type.cc
@@ -25,6 +25,10 @@
     const CSSValue& value,
     const CSSLengthResolver& length_resolver) {
   const auto& primitive_value = To<CSSPrimitiveValue>(value);
+  // TODO(crbug.com/415626999): Create a TreeCountingChecker for sibling-index()
+  // and sibling-count() if necessary.
+  // TODO(crbug.com/415572412): Create a LengthUnitsChecker for relative units
+  // if necessary.
   // TODO(crbug.com/41494232): Don't resolve it here, once we can divide units.
   // The problem now is when we end up with kNumber for neutral keyframe
   // and kPercentage for non-neutral keyframe, we have to sum number and
diff --git a/third_party/blink/renderer/core/animation/css_transform_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_transform_interpolation_type.cc
index 468d80f..9c4bff4a 100644
--- a/third_party/blink/renderer/core/animation/css_transform_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_transform_interpolation_type.cc
@@ -118,6 +118,8 @@
             LengthUnitsChecker::MaybeCreate(types, state)) {
       conversion_checkers.push_back(length_units_checker);
     }
+    // TODO(crbug.com/415626999): Create a TreeCountingChecker for
+    // sibling-index() and sibling-count() if necessary.
   }
 
   return InterpolationValue(InterpolableTransformList::ConvertCSSValue(
diff --git a/third_party/blink/renderer/core/css/css_image_value.cc b/third_party/blink/renderer/core/css/css_image_value.cc
index fe40a11d..52d347ab 100644
--- a/third_party/blink/renderer/core/css/css_image_value.cc
+++ b/third_party/blink/renderer/core/css/css_image_value.cc
@@ -167,13 +167,6 @@
   return UrlData().IsLocal(document);
 }
 
-CSSImageValue* CSSImageValue::ComputedCSSValueMaybeLocal() const {
-  if (UrlData().UnresolvedUrl().StartsWith('#')) {
-    return Clone();
-  }
-  return ComputedCSSValue();
-}
-
 AtomicString CSSImageValue::NormalizedFragmentIdentifier() const {
   // Always use KURL's FragmentIdentifier to ensure that we're handling the
   // fragment in a consistent manner.
diff --git a/third_party/blink/renderer/core/css/css_image_value.h b/third_party/blink/renderer/core/css/css_image_value.h
index 41fa958b..d2188598f 100644
--- a/third_party/blink/renderer/core/css/css_image_value.h
+++ b/third_party/blink/renderer/core/css/css_image_value.h
@@ -68,10 +68,9 @@
   bool Equals(const CSSImageValue&) const;
 
   CSSImageValue* ComputedCSSValue() const {
-    return MakeGarbageCollected<CSSImageValue>(*UrlData().MakeAbsolute(),
+    return MakeGarbageCollected<CSSImageValue>(*UrlData().MakeComputed(),
                                                cached_image_.Get());
   }
-  CSSImageValue* ComputedCSSValueMaybeLocal() const;
 
   CSSImageValue* Clone() const {
     return MakeGarbageCollected<CSSImageValue>(*UrlData().MakeWithoutReferrer(),
diff --git a/third_party/blink/renderer/core/css/css_url_data.cc b/third_party/blink/renderer/core/css/css_url_data.cc
index 573db63b..2efe98f 100644
--- a/third_party/blink/renderer/core/css/css_url_data.cc
+++ b/third_party/blink/renderer/core/css/css_url_data.cc
@@ -35,9 +35,9 @@
     : relative_url_(unresolved_url),
       absolute_url_(resolved_url.GetString()),
       referrer_(referrer),
+      is_local_(unresolved_url.StartsWith('#')),
       is_from_origin_clean_style_sheet_(is_from_origin_clean_style_sheet),
       is_ad_related_(is_ad_related),
-      is_local_(unresolved_url.StartsWith('#')),
       potentially_dangling_markup_(resolved_url.PotentiallyDanglingMarkup()) {}
 
 CSSUrlData::CSSUrlData(const AtomicString& resolved_url)
@@ -82,8 +82,8 @@
   return true;
 }
 
-const CSSUrlData* CSSUrlData::MakeAbsolute() const {
-  if (relative_url_.empty()) {
+const CSSUrlData* CSSUrlData::MakeComputed() const {
+  if (relative_url_.empty() || is_local_ || absolute_url_.empty()) {
     return this;
   }
   return MakeGarbageCollected<CSSUrlData>(
diff --git a/third_party/blink/renderer/core/css/css_url_data.h b/third_party/blink/renderer/core/css/css_url_data.h
index f3a7ed5..db94c0b 100644
--- a/third_party/blink/renderer/core/css/css_url_data.h
+++ b/third_party/blink/renderer/core/css/css_url_data.h
@@ -56,8 +56,8 @@
   // Document. Returns true if the resolved URL changed, otherwise false.
   bool ReResolveUrl(const Document&) const;
 
-  // Returns an absolutized copy of this URL data (suitable for computed value).
-  const CSSUrlData* MakeAbsolute() const;
+  // Returns a copy of this URL data suitable for computed value.
+  const CSSUrlData* MakeComputed() const;
 
   // Returns a copy where the unresolved URL has been resolved against
   // `base_url` (using `charset` encoding if valid).
@@ -101,6 +101,9 @@
   mutable AtomicString absolute_url_;
   const Referrer referrer_;
 
+  // The 'local url flag': https://drafts.csswg.org/css-values/#local-urls
+  const bool is_local_;
+
   // Whether the stylesheet that requested this image is origin-clean:
   // https://drafts.csswg.org/cssom-1/#concept-css-style-sheet-origin-clean-flag
   const bool is_from_origin_clean_style_sheet_;
@@ -108,8 +111,6 @@
   // Whether this was created by an ad-related CSSParserContext.
   const bool is_ad_related_;
 
-  const bool is_local_;
-
   // The url passed into the constructor had the PotentiallyDanglingMarkup flag
   // set. That information needs to be passed on to the fetch code to block such
   // resources from loading.
diff --git a/third_party/blink/renderer/core/editing/build.gni b/third_party/blink/renderer/core/editing/build.gni
index 0645e5e..aa7e993 100644
--- a/third_party/blink/renderer/core/editing/build.gni
+++ b/third_party/blink/renderer/core/editing/build.gni
@@ -379,6 +379,7 @@
   "commands/editing_command_test.cc",
   "commands/editing_commands_utilities_test.cc",
   "commands/insert_incremental_text_command_test.cc",
+  "commands/insert_line_break_command_test.cc",
   "commands/insert_list_command_test.cc",
   "commands/insert_paragraph_separator_command_test.cc",
   "commands/insert_text_command_test.cc",
diff --git a/third_party/blink/renderer/core/editing/commands/delete_selection_command.cc b/third_party/blink/renderer/core/editing/commands/delete_selection_command.cc
index 3db63d2..7c202910 100644
--- a/third_party/blink/renderer/core/editing/commands/delete_selection_command.cc
+++ b/third_party/blink/renderer/core/editing/commands/delete_selection_command.cc
@@ -812,14 +812,10 @@
         upstream_start_.AnchorNode()->IsDescendantOf(
             downstream_end_.AnchorNode());
 
-    bool end_node_is_selected_from_first_position = false;
-    if (RuntimeEnabledFeatures::
-            RemoveNodeHavingChildrenIfFullySelectedEnabled()) {
-      end_node_is_selected_from_first_position =
-          ComparePositions(upstream_start_,
-                           Position::FirstPositionInNode(
-                               *downstream_end_.AnchorNode())) <= 0;
-    }
+    bool end_node_is_selected_from_first_position =
+        ComparePositions(
+            upstream_start_,
+            Position::FirstPositionInNode(*downstream_end_.AnchorNode())) <= 0;
 
     // The selection to delete spans more than one node.
     Node* node(start_node);
@@ -856,15 +852,12 @@
       bool is_node_fully_selected =
           downstream_end_.AtLastEditingPositionForNode() &&
           !CanHaveChildrenForEditing(downstream_end_.AnchorNode());
-      if (RuntimeEnabledFeatures::
-              RemoveNodeHavingChildrenIfFullySelectedEnabled()) {
-        // Even though `downstream_end_` has children, it can be fully selected.
-        // Update `is_node_fully_selected` if the selection includes the first
-        // position of the node.
-        if (!is_node_fully_selected &&
-            downstream_end_.AtLastEditingPositionForNode()) {
-          is_node_fully_selected = end_node_is_selected_from_first_position;
-        }
+      // Even though `downstream_end_` has children, it can be fully selected.
+      // Update `is_node_fully_selected` if the selection includes the first
+      // position of the node.
+      if (!is_node_fully_selected &&
+          downstream_end_.AtLastEditingPositionForNode()) {
+        is_node_fully_selected = end_node_is_selected_from_first_position;
       }
       if (is_node_fully_selected) {
         // The node itself is fully selected, not just its contents.  Delete it.
@@ -956,15 +949,12 @@
   if (upstream_start_ == downstream_end_)
     return;
 
-  if (RuntimeEnabledFeatures::
-          RemoveNodeHavingChildrenIfFullySelectedEnabled()) {
-    // It can be the same position even though `upstream_start_` and
-    // `downstream_end_` are not identical.
-    // Compare them using ParentAnchoredEquivalent().
-    if (upstream_start_.ParentAnchoredEquivalent() ==
-        downstream_end_.ParentAnchoredEquivalent()) {
-      return;
-    }
+  // It can be the same position even though `upstream_start_` and
+  // `downstream_end_` are not identical.
+  // Compare them using ParentAnchoredEquivalent().
+  if (upstream_start_.ParentAnchoredEquivalent() ==
+      downstream_end_.ParentAnchoredEquivalent()) {
+    return;
   }
 
   GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kEditing);
diff --git a/third_party/blink/renderer/core/editing/commands/insert_line_break_command.cc b/third_party/blink/renderer/core/editing/commands/insert_line_break_command.cc
index ba8d700..b08af54 100644
--- a/third_party/blink/renderer/core/editing/commands/insert_line_break_command.cc
+++ b/third_party/blink/renderer/core/editing/commands/insert_line_break_command.cc
@@ -36,6 +36,7 @@
 #include "third_party/blink/renderer/core/editing/visible_position.h"
 #include "third_party/blink/renderer/core/editing/visible_units.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/core/html/forms/html_text_area_element.h"
 #include "third_party/blink/renderer/core/html/forms/text_control_element.h"
 #include "third_party/blink/renderer/core/html/html_br_element.h"
 #include "third_party/blink/renderer/core/html/html_element.h"
@@ -62,6 +63,10 @@
   // the input element, and in that case we need to check the input element's
   // parent's layoutObject.
   Position p(insertion_pos.ParentAnchoredEquivalent());
+  if (auto* text_control = EnclosingTextControl(p)) {
+    return RuntimeEnabledFeatures::TextareaLineEndingsAsBrEnabled() &&
+           IsA<HTMLTextAreaElement>(text_control);
+  }
   return IsRichlyEditablePosition(p) && p.AnchorNode()->GetLayoutObject() &&
          p.AnchorNode()->GetLayoutObject()->Style()->ShouldCollapseBreaks();
 }
diff --git a/third_party/blink/renderer/core/editing/commands/insert_line_break_command.h b/third_party/blink/renderer/core/editing/commands/insert_line_break_command.h
index 1488054..6491ca4 100644
--- a/third_party/blink/renderer/core/editing/commands/insert_line_break_command.h
+++ b/third_party/blink/renderer/core/editing/commands/insert_line_break_command.h
@@ -26,11 +26,12 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_EDITING_COMMANDS_INSERT_LINE_BREAK_COMMAND_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_EDITING_COMMANDS_INSERT_LINE_BREAK_COMMAND_H_
 
+#include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/editing/commands/composite_edit_command.h"
 
 namespace blink {
 
-class InsertLineBreakCommand final : public CompositeEditCommand {
+class CORE_EXPORT InsertLineBreakCommand final : public CompositeEditCommand {
  public:
   explicit InsertLineBreakCommand(Document&);
 
diff --git a/third_party/blink/renderer/core/editing/commands/insert_line_break_command_test.cc b/third_party/blink/renderer/core/editing/commands/insert_line_break_command_test.cc
new file mode 100644
index 0000000..e955b6e2
--- /dev/null
+++ b/third_party/blink/renderer/core/editing/commands/insert_line_break_command_test.cc
@@ -0,0 +1,40 @@
+// 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/editing/commands/insert_line_break_command.h"
+
+#include "third_party/blink/renderer/core/editing/frame_selection.h"
+#include "third_party/blink/renderer/core/editing/selection_template.h"
+#include "third_party/blink/renderer/core/editing/testing/editing_test_base.h"
+#include "third_party/blink/renderer/core/editing/visible_selection.h"
+#include "third_party/blink/renderer/core/html/forms/text_control_element.h"
+
+namespace blink {
+
+class InsertLineBreakCommandTest : public EditingTestBase {};
+
+TEST_F(InsertLineBreakCommandTest, InsertToTextArea) {
+  SetBodyContent("<textarea>foobar</textarea>");
+  auto* field = To<TextControlElement>(QuerySelector("textarea"));
+  field->setSelectionEnd(3);
+  field->setSelectionStart(3);
+  field->Focus();
+
+  auto& command = *MakeGarbageCollected<InsertLineBreakCommand>(GetDocument());
+
+  EXPECT_TRUE(command.Apply());
+  if (RuntimeEnabledFeatures::TextareaLineEndingsAsBrEnabled()) {
+    EXPECT_EQ(
+        "<textarea><div>foo<br>|bar</div></textarea>",
+        GetSelectionTextInFlatTreeFromBody(
+            Selection().ComputeVisibleSelectionInFlatTree().AsSelection()));
+  } else {
+    EXPECT_EQ(
+        "<textarea><div>foo\n|bar</div></textarea>",
+        GetSelectionTextInFlatTreeFromBody(
+            Selection().ComputeVisibleSelectionInFlatTree().AsSelection()));
+  }
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/editing/commands/replace_selection_command.cc b/third_party/blink/renderer/core/editing/commands/replace_selection_command.cc
index f65781dc..d2dc9be4 100644
--- a/third_party/blink/renderer/core/editing/commands/replace_selection_command.cc
+++ b/third_party/blink/renderer/core/editing/commands/replace_selection_command.cc
@@ -54,6 +54,7 @@
 #include "third_party/blink/renderer/core/frame/web_feature.h"
 #include "third_party/blink/renderer/core/html/forms/html_input_element.h"
 #include "third_party/blink/renderer/core/html/forms/html_select_element.h"
+#include "third_party/blink/renderer/core/html/forms/html_text_area_element.h"
 #include "third_party/blink/renderer/core/html/html_base_element.h"
 #include "third_party/blink/renderer/core/html/html_body_element.h"
 #include "third_party/blink/renderer/core/html/html_br_element.h"
@@ -95,6 +96,8 @@
   Node* FirstChild() const;
   Node* LastChild() const;
 
+  String TrivialReplacementText() const { return trivial_text_; }
+
   bool IsEmpty() const;
 
   bool HasInterchangeNewlineAtStart() const {
@@ -108,6 +111,8 @@
   void RemoveNodePreservingChildren(ContainerNode*);
 
  private:
+  void UpdateFragmentForTextArea();
+  void UpdateTrivialReplacementText();
   HTMLElement* InsertFragmentForTestRendering(Element* root_editable_element);
   void RemoveUnrenderedNodes(ContainerNode*);
   void RestoreAndRemoveTestRenderingNodesToFragment(Element*);
@@ -117,6 +122,7 @@
 
   Document* document_;
   DocumentFragment* fragment_;
+  String trivial_text_;
   bool has_interchange_newline_at_start_;
   bool has_interchange_newline_at_end_;
 };
@@ -226,9 +232,14 @@
             selection.ToNormalizedEphemeralRange(), event->GetText());
         RemoveInterchangeNodes(fragment_);
       }
+      UpdateTrivialReplacementText();
+      if (IsA<HTMLTextAreaElement>(EnclosingTextControl(editable_root))) {
+        UpdateFragmentForTextArea();
+      }
       return;
     }
   }
+  UpdateTrivialReplacementText();
 
   HTMLElement* holder = InsertFragmentForTestRendering(editable_root);
   if (!holder) {
@@ -272,6 +283,52 @@
   }
 }
 
+void ReplacementFragment::UpdateFragmentForTextArea() {
+  if (!RuntimeEnabledFeatures::TextareaLineEndingsAsBrEnabled()) {
+    return;
+  }
+  DocumentFragment* new_fragment = nullptr;
+  Node* next = nullptr;
+  for (Node* node = fragment_->firstChild(); node; node = next) {
+    // We need to get nextSibling before moving `node`.
+    next = node->nextSibling();
+    if (!node->IsTextNode()) {
+      if (new_fragment) {
+        new_fragment->AppendChild(node);
+      }
+      continue;
+    }
+    String value = node->textContent();
+    if (value.find('\n') == kNotFound) {
+      if (new_fragment) {
+        new_fragment->AppendChild(node);
+      }
+      continue;
+    }
+    if (!new_fragment) {
+      new_fragment = document_->createDocumentFragment();
+      Node* inner_next = nullptr;
+      for (Node* inner_node = fragment_->firstChild(); inner_node != node;
+           inner_node = inner_next) {
+        inner_next = inner_node->nextSibling();
+        new_fragment->AppendChild(inner_node);
+      }
+    }
+    TextControlElement::AppendTextOrBr(value, *new_fragment);
+  }
+  if (new_fragment) {
+    fragment_ = new_fragment;
+  }
+}
+
+void ReplacementFragment::UpdateTrivialReplacementText() {
+  if (!FirstChild() || FirstChild() != LastChild() ||
+      !FirstChild()->IsTextNode()) {
+    return;
+  }
+  trivial_text_ = To<Text>(FirstChild())->data();
+}
+
 bool ReplacementFragment::IsEmpty() const {
   return (!fragment_ || !fragment_->HasChildren()) &&
          !has_interchange_newline_at_start_ && !has_interchange_newline_at_end_;
@@ -2130,13 +2187,13 @@
 bool ReplaceSelectionCommand::PerformTrivialReplace(
     const ReplacementFragment& fragment,
     EditingState* editing_state) {
+  // Save the text to set event data for input events.
+  input_event_data_ = fragment.TrivialReplacementText();
+
   if (!fragment.FirstChild() || fragment.FirstChild() != fragment.LastChild() ||
       !fragment.FirstChild()->IsTextNode())
     return false;
 
-  // Save the text to set event data for input events.
-  input_event_data_ = To<Text>(fragment.FirstChild())->data();
-
   // FIXME: Would be nice to handle smart replace in the fast path.
   if (smart_replace_ || fragment.HasInterchangeNewlineAtStart() ||
       fragment.HasInterchangeNewlineAtEnd())
diff --git a/third_party/blink/renderer/core/editing/commands/replace_selection_command_test.cc b/third_party/blink/renderer/core/editing/commands/replace_selection_command_test.cc
index 5f733e3..1bf7849 100644
--- a/third_party/blink/renderer/core/editing/commands/replace_selection_command_test.cc
+++ b/third_party/blink/renderer/core/editing/commands/replace_selection_command_test.cc
@@ -83,81 +83,6 @@
       << "'bar' should have been inserted";
 }
 
-TEST_F(ReplaceSelectionCommandTest,
-       PasteNonEditableSpanInBetweenEditableAndNonEditable) {
-  Selection().SetSelection(
-      SetSelectionTextToBody(
-          "<div contenteditable=\"true\">Editable<span "
-          "contenteditable=\"false\">Non-Editable</span>|Editable</div>"),
-      SetSelectionOptions());
-
-  Element* span_element = QuerySelector("span");
-  DocumentFragment* fragment = GetDocument().createDocumentFragment();
-  fragment->ParseHTML("<span contenteditable=\"false\">ToPaste</span>",
-                      span_element);
-
-  ReplaceSelectionCommand::CommandOptions options = 0;
-  auto* command = MakeGarbageCollected<ReplaceSelectionCommand>(
-      GetDocument(), fragment, options);
-
-  EXPECT_TRUE(command->Apply()) << "the replace command should have succeeded";
-  String expected_string;
-  String assert_comment;
-  if (RuntimeEnabledFeatures::
-          PartialCompletionNotAllowedInMoveParagraphsEnabled()) {
-    expected_string =
-        "<div contenteditable=\"true\">Editable<span "
-        "contenteditable=\"false\">Non-Editable</span><span "
-        "contenteditable=\"false\">ToPaste</span>Editable</div>";
-    assert_comment = "span should have been inserted without losing any data";
-  } else {
-    expected_string =
-        "<div contenteditable=\"true\">Editable<span "
-        "contenteditable=\"false\">Non-Editable</span><span "
-        "contenteditable=\"false\">ToPaste</span></div>";
-    assert_comment = "span would be incorrectly inserted with data loss";
-  }
-  EXPECT_EQ(expected_string, GetDocument().body()->innerHTML())
-      << assert_comment;
-}
-
-TEST_F(ReplaceSelectionCommandTest, PasteNonEditableSpanInEditableArea) {
-  Selection().SetSelection(
-      SetSelectionTextToBody(
-          "<div contenteditable=\"true\">Editable<span "
-          "contenteditable=\"false\">Non-Editable</span>Edit|able</div>"),
-      SetSelectionOptions());
-
-  Element* span_element = QuerySelector("span");
-  DocumentFragment* fragment = GetDocument().createDocumentFragment();
-  fragment->ParseHTML("<span contenteditable=\"false\">ToPaste</span>",
-                      span_element);
-
-  ReplaceSelectionCommand::CommandOptions options = 0;
-  auto* command = MakeGarbageCollected<ReplaceSelectionCommand>(
-      GetDocument(), fragment, options);
-
-  EXPECT_TRUE(command->Apply()) << "the replace command should have succeeded";
-  String expected_string;
-  String assert_comment;
-  if (RuntimeEnabledFeatures::
-          PartialCompletionNotAllowedInMoveParagraphsEnabled()) {
-    expected_string =
-        "<div contenteditable=\"true\">Editable<span "
-        "contenteditable=\"false\">Non-Editable</span>Edit<span "
-        "contenteditable=\"false\">ToPaste</span>able</div>";
-    assert_comment = "span should have been inserted without duplication";
-  } else {
-    expected_string =
-        "<div contenteditable=\"true\">Editable<span "
-        "contenteditable=\"false\">Non-Editable</span>Edit<span "
-        "contenteditable=\"false\">ToPaste</span>ToPasteable</div>";
-    assert_comment = "span would be incorrectly inserted with duplication";
-  }
-  EXPECT_EQ(expected_string, GetDocument().body()->innerHTML())
-      << assert_comment;
-}
-
 // Helper function to set autosizing multipliers on a document.
 bool SetTextAutosizingMultiplier(Document* document, float multiplier) {
   bool multiplier_set = false;
@@ -365,4 +290,30 @@
       "</div></strong>",
       GetSelectionTextFromBody());
 }
+
+TEST_F(ReplaceSelectionCommandTest, InsertLineFeedsToTextArea) {
+  SetBodyContent("<textarea></textarea>");
+  Element* field = QuerySelector("textarea");
+  field->Focus();
+  DocumentFragment& fragment = *GetDocument().createDocumentFragment();
+  fragment.appendChild(Text::Create(GetDocument(), "\nfoo\n"));
+
+  auto& command = *MakeGarbageCollected<ReplaceSelectionCommand>(
+      GetDocument(), &fragment, /* options */ 0, InputEvent::InputType::kNone);
+
+  EXPECT_TRUE(command.Apply());
+  if (RuntimeEnabledFeatures::TextareaLineEndingsAsBrEnabled()) {
+    EXPECT_EQ(
+        "<textarea><div><br>foo|<br>"
+        "<br id=\"textarea-placeholder-break\"></div></textarea>",
+        GetSelectionTextInFlatTreeFromBody(
+            Selection().ComputeVisibleSelectionInFlatTree().AsSelection()));
+  } else {
+    EXPECT_EQ(
+        "<textarea><div>\nfoo|\n<br></div></textarea>",
+        GetSelectionTextInFlatTreeFromBody(
+            Selection().ComputeVisibleSelectionInFlatTree().AsSelection()));
+  }
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/editing/visible_units_paragraph.cc b/third_party/blink/renderer/core/editing/visible_units_paragraph.cc
index bf7bb7b..504d699d 100644
--- a/third_party/blink/renderer/core/editing/visible_units_paragraph.cc
+++ b/third_party/blink/renderer/core/editing/visible_units_paragraph.cc
@@ -292,11 +292,6 @@
     }
   }
 
-  // If start node is non-editable and we have our candidate same as start node
-  // return the last position in start node.
-  if (!start_node_is_editable && candidate_node == start_node) {
-    candidate_type = PositionAnchorType::kAfterAnchor;
-  }
   if (candidate_type == PositionAnchorType::kOffsetInAnchor)
     return PositionTemplate<Strategy>(candidate_node, candidate_offset);
 
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
index dd0c441..61fd7ff4 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
@@ -3377,7 +3377,8 @@
     return;
   }
 
-  lcpp->set_lcp_element_locators(hint->lcp_element_locators);
+  lcpp->set_lcp_element_locators(hint->lcp_element_locators,
+                                 hint->lcp_element_locators_all);
 
   HashSet<KURL> lcp_influencer_scripts;
   for (auto& url : hint->lcp_influencer_scripts) {
diff --git a/third_party/blink/renderer/core/html/forms/html_input_element.cc b/third_party/blink/renderer/core/html/forms/html_input_element.cc
index e8c7506..b86614ad 100644
--- a/third_party/blink/renderer/core/html/forms/html_input_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_input_element.cc
@@ -70,6 +70,7 @@
 #include "third_party/blink/renderer/core/html/forms/html_data_list_options_collection.h"
 #include "third_party/blink/renderer/core/html/forms/html_form_element.h"
 #include "third_party/blink/renderer/core/html/forms/html_option_element.h"
+#include "third_party/blink/renderer/core/html/forms/html_select_element.h"
 #include "third_party/blink/renderer/core/html/forms/input_type.h"
 #include "third_party/blink/renderer/core/html/forms/radio_button_group_scope.h"
 #include "third_party/blink/renderer/core/html/forms/search_input_type.h"
@@ -160,6 +161,7 @@
   visitor->Trace(input_type_view_);
   visitor->Trace(list_attribute_target_observer_);
   visitor->Trace(image_loader_);
+  visitor->Trace(first_ancestor_select_);
   TextControlElement::Trace(visitor);
 }
 
@@ -2474,4 +2476,19 @@
   }
 }
 
+bool HTMLInputElement::IsFirstTextInputInAncestorSelect() const {
+  if (!RuntimeEnabledFeatures::SelectAccessibilityReparentInputEnabled() ||
+      !first_ancestor_select_) {
+    return false;
+  }
+  return first_ancestor_select_->FirstDescendantTextInput() == this;
+}
+
+HTMLSelectElement* HTMLInputElement::FirstAncestorSelectElement() const {
+  if (!RuntimeEnabledFeatures::SelectAccessibilityReparentInputEnabled()) {
+    return nullptr;
+  }
+  return first_ancestor_select_;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/forms/html_input_element.h b/third_party/blink/renderer/core/html/forms/html_input_element.h
index cb03e1e8..9c3b0ce 100644
--- a/third_party/blink/renderer/core/html/forms/html_input_element.h
+++ b/third_party/blink/renderer/core/html/forms/html_input_element.h
@@ -383,6 +383,18 @@
 
   void SetFocused(bool is_focused, mojom::blink::FocusType) override;
 
+  // These methods are used to determine what the nearest ancestor <select>
+  // element is and whether this is the first <input> in tree order within that
+  // <select>. These are populated lazily by the select element's
+  // MutationObserver and are not guaranteed to be correct all of the time
+  // since that MutationObserver only runs when the select element has base
+  // appearance.
+  bool IsFirstTextInputInAncestorSelect() const;
+  HTMLSelectElement* FirstAncestorSelectElement() const;
+  void SetFirstAncestorSelectElement(HTMLSelectElement* select) {
+    first_ancestor_select_ = select;
+  }
+
  protected:
   void DefaultEventHandler(Event&) override;
   bool IsInnerEditorValueEmpty() const final;
@@ -514,6 +526,7 @@
   // element lives on.
   Member<HTMLImageLoader> image_loader_;
   Member<ListAttributeTargetObserver> list_attribute_target_observer_;
+  Member<HTMLSelectElement> first_ancestor_select_;
 
   FRIEND_TEST_ALL_PREFIXES(HTMLInputElementTest, RadioKeyDownDCHECKFailure);
 };
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 7445a9f..3aa6f80 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
@@ -730,6 +730,10 @@
       if (key == keywords::kArrowUp) {
         if (auto* previous_option = options.PreviousFocusableOption(*this)) {
           previous_option->Focus(focus_params);
+        } else if (RuntimeEnabledFeatures::
+                       SelectAccessibilityReparentInputEnabled() &&
+                   select->FirstDescendantTextInput()) {
+          select->FirstDescendantTextInput()->Focus(focus_params);
         }
         event.SetDefaultHandled();
         return;
diff --git a/third_party/blink/renderer/core/html/forms/html_option_element.h b/third_party/blink/renderer/core/html/forms/html_option_element.h
index d85c610..1211ba8 100644
--- a/third_party/blink/renderer/core/html/forms/html_option_element.h
+++ b/third_party/blink/renderer/core/html/forms/html_option_element.h
@@ -129,9 +129,10 @@
   // an HTMLOptionElement.
   static bool IsLabelContainerElement(const Element& element);
 
+  bool IsKeyboardFocusableSlow(UpdateBehavior update_behavior) const override;
+
  private:
   FocusableState SupportsFocus(UpdateBehavior update_behavior) const override;
-  bool IsKeyboardFocusableSlow(UpdateBehavior update_behavior) const override;
   bool MatchesDefaultPseudoClass() const override;
   bool MatchesEnabledPseudoClass() const override;
   void ParseAttribute(const AttributeModificationParams&) override;
diff --git a/third_party/blink/renderer/core/html/forms/html_select_element.cc b/third_party/blink/renderer/core/html/forms/html_select_element.cc
index fbcd3a2..4ffde71d 100644
--- a/third_party/blink/renderer/core/html/forms/html_select_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_select_element.cc
@@ -130,11 +130,25 @@
       if (record->type() == "childList") {
         CheckAddedNodes(record);
         CheckRemovedNodes(record);
-      } else if ((record->type() == "attributes") &&
-                 (record->attributeName() == html_names::kTabindexAttr ||
-                  record->attributeName() ==
-                      html_names::kContenteditableAttr)) {
-        AddDescendantDisallowedErrorToNode(*record->target());
+      } else if (record->type() == "attributes") {
+        if (record->attributeName() == html_names::kTabindexAttr ||
+            record->attributeName() == html_names::kContenteditableAttr) {
+          AddDescendantDisallowedErrorToNode(*record->target());
+        } else if (RuntimeEnabledFeatures::
+                       SelectAccessibilityReparentInputEnabled() &&
+                   record->attributeName() == html_names::kTypeAttr) {
+          if (auto* input = DynamicTo<HTMLInputElement>(record->target())) {
+            if (input->IsTextField()) {
+              select_->AddDescendantTextInput(input);
+            } else {
+              select_->RemoveDescendantTextInput(input);
+              // If the type attribute was changed in a way that makes the
+              // <input> no longer an allowed descendant, then we should emit an
+              // error.
+              AddDescendantDisallowedErrorToNode(*input);
+            }
+          }
+        }
       }
     }
   }
@@ -157,6 +171,7 @@
       if (IsWhitespaceOrEmpty(*descendant)) {
         continue;
       }
+      MaybeAddDescendantTextInput(descendant);
       AddDescendantDisallowedErrorToNode(*descendant);
       // Check the added node's descendants, if any.
       TraverseNodeDescendants(descendant);
@@ -173,6 +188,7 @@
       if (IsWhitespaceOrEmpty(*descendant)) {
         continue;
       }
+      MaybeRemoveDescendantTextInput(descendant);
       if (!IsAllowedInteractiveElement(*descendant)) {
         select_->DecreaseContentModelViolationCount();
       }
@@ -180,6 +196,7 @@
       for (Node* nested_descendant = NodeTraversal::FirstWithin(*descendant);
            nested_descendant; nested_descendant = NodeTraversal::Next(
                                   *nested_descendant, descendant)) {
+        MaybeRemoveDescendantTextInput(descendant);
         if (!IsWhitespaceOrEmpty(*nested_descendant) &&
             !IsAllowedInteractiveElement(*nested_descendant)) {
           select_->DecreaseContentModelViolationCount();
@@ -192,11 +209,30 @@
     for (Node* descendant = NodeTraversal::FirstWithin(*node); descendant;
          descendant = NodeTraversal::Next(*descendant, node)) {
       if (!IsWhitespaceOrEmpty(*descendant)) {
+        MaybeAddDescendantTextInput(descendant);
         AddDescendantDisallowedErrorToNode(*descendant);
       }
     }
   }
 
+  void MaybeAddDescendantTextInput(Node* node) {
+    if (RuntimeEnabledFeatures::SelectAccessibilityReparentInputEnabled()) {
+      if (auto* input = DynamicTo<HTMLInputElement>(node);
+          input && input->IsTextField()) {
+        select_->AddDescendantTextInput(input);
+      }
+    }
+  }
+
+  void MaybeRemoveDescendantTextInput(Node* node) {
+    if (RuntimeEnabledFeatures::SelectAccessibilityReparentInputEnabled()) {
+      if (auto* input = DynamicTo<HTMLInputElement>(node);
+          input && input->IsTextField()) {
+        select_->RemoveDescendantTextInput(input);
+      }
+    }
+  }
+
   void AddDescendantDisallowedErrorToNode(Node& node) {
     SelectElementAccessibilityIssueReason issue_reason = CheckForIssue(node);
     if (issue_reason != SelectElementAccessibilityIssueReason::kValidChild) {
@@ -282,7 +318,7 @@
            "navigating by keyboard or using assistive technology.";
   }
 
-  bool IsAllowedInteractiveElement(const Node& node) {
+  bool IsAllowedInteractiveElement(Node& node) {
     if (IsA<HTMLButtonElement>(node)) {
       // The <button> must have a parent (not being inserted as a child of
       // `HTMLSelectedContentElement`) and must be the first child of the
@@ -291,6 +327,20 @@
       return parent && IsA<HTMLSelectElement>(*parent) &&
              !ElementTraversal::PreviousSibling(node);
     }
+    if (RuntimeEnabledFeatures::SelectAccessibilityReparentInputEnabled()) {
+      // <select>s are allowed to have one <input> before the options. We should
+      // probably find a way to figure out if the <input> is actually placed
+      // before the <option>s or not.
+
+      if (auto* input = DynamicTo<HTMLInputElement>(node)) {
+        if (input->IsTextField()) {
+          select_->AddDescendantTextInput(input);
+        }
+        if (input == select_->FirstDescendantTextInput()) {
+          return true;
+        }
+      }
+    }
     // If the node isn't a <button> but it is an interactive element, we return
     // false as interactive elements are disallowed.
     return !IsInteractiveElement(node);
@@ -399,6 +449,13 @@
   }
 
   bool IsAllowedDescendantOfSelect(const Node& descendant, const Node& parent) {
+    if (RuntimeEnabledFeatures::SelectAccessibilityReparentInputEnabled()) {
+      // <select>s are allowed to have one text <input>, although it should be
+      // placed before any of the <option>s.
+      if (select_->FirstDescendantTextInput() == descendant) {
+        return true;
+      }
+    }
     // <button> has to be the first direct descendant of the <select>.
     return (IsA<HTMLButtonElement>(descendant) &&
             IsA<HTMLSelectElement>(parent) &&
@@ -1876,6 +1933,7 @@
   visitor->Trace(last_on_change_option_);
   visitor->Trace(suggested_option_);
   visitor->Trace(descendant_selectedcontents_);
+  visitor->Trace(descendant_text_inputs_);
   visitor->Trace(select_type_);
   visitor->Trace(descendants_observer_);
   HTMLFormControlElementWithState::Trace(visitor);
@@ -2297,6 +2355,31 @@
   }
 }
 
+void HTMLSelectElement::AddDescendantTextInput(HTMLInputElement* input) {
+  CHECK(RuntimeEnabledFeatures::SelectAccessibilityReparentInputEnabled());
+  CHECK(input->IsTextField());
+  descendant_text_inputs_.Add(input);
+  input->SetFirstAncestorSelectElement(this);
+}
+
+void HTMLSelectElement::RemoveDescendantTextInput(HTMLInputElement* input) {
+  CHECK(RuntimeEnabledFeatures::SelectAccessibilityReparentInputEnabled());
+  descendant_text_inputs_.Remove(input);
+  input->SetFirstAncestorSelectElement(nullptr);
+}
+
+HTMLInputElement* HTMLSelectElement::FirstDescendantTextInput() const {
+  if (descendant_text_inputs_.IsEmpty()) {
+    return nullptr;
+  }
+  HTMLInputElement* first_input = *descendant_text_inputs_.begin();
+  if (!first_input->isConnected() || !first_input->IsTextField() ||
+      Traversal<HTMLSelectElement>::FirstAncestor(*first_input) != this) {
+    return nullptr;
+  }
+  return first_input;
+}
+
 HTMLSelectElement::SelectAutofillPreviewElement*
 HTMLSelectElement::GetAutofillPreviewElement() const {
   return select_type_->GetAutofillPreviewElement();
diff --git a/third_party/blink/renderer/core/html/forms/html_select_element.h b/third_party/blink/renderer/core/html/forms/html_select_element.h
index adf2175..3ab56757 100644
--- a/third_party/blink/renderer/core/html/forms/html_select_element.h
+++ b/third_party/blink/renderer/core/html/forms/html_select_element.h
@@ -33,6 +33,7 @@
 #include "third_party/blink/renderer/core/dom/events/simulated_click_options.h"
 #include "third_party/blink/renderer/core/dom/tree_ordered_list.h"
 #include "third_party/blink/renderer/core/html/forms/html_form_control_element_with_state.h"
+#include "third_party/blink/renderer/core/html/forms/html_input_element.h"
 #include "third_party/blink/renderer/core/html/forms/html_selected_content_element.h"
 #include "third_party/blink/renderer/core/html/forms/option_list.h"
 #include "third_party/blink/renderer/core/html/forms/type_ahead.h"
@@ -317,6 +318,13 @@
   void SelectedContentElementRemoved(
       HTMLSelectedContentElement* selectedcontent);
 
+  // These methods are used to track all descendant <input>s elements of this
+  // <select>. This is only used for customizable select and is populated by
+  // this select's MutationObserver.
+  void AddDescendantTextInput(HTMLInputElement* input);
+  void RemoveDescendantTextInput(HTMLInputElement* input);
+  HTMLInputElement* FirstDescendantTextInput() const;
+
   // This will only return an element if IsAppearanceBase(). The element
   // is a popover inside the UA shadowroot which is used to show the user a
   // preview of what is going to be autofilled. This should only be called if
@@ -434,6 +442,7 @@
   Member<HTMLOptionElement> last_on_change_option_;
   Member<HTMLOptionElement> suggested_option_;
   TreeOrderedList<HTMLSelectedContentElement> descendant_selectedcontents_;
+  TreeOrderedList<HTMLInputElement> descendant_text_inputs_;
   bool uses_menu_list_ = true;
   bool is_multiple_;
   mutable bool should_recalc_list_items_;
diff --git a/third_party/blink/renderer/core/html/forms/option_list.cc b/third_party/blink/renderer/core/html/forms/option_list.cc
index 153467d4..76fdd36f 100644
--- a/third_party/blink/renderer/core/html/forms/option_list.cc
+++ b/third_party/blink/renderer/core/html/forms/option_list.cc
@@ -171,4 +171,17 @@
   }
 }
 
+HTMLOptionElement* OptionList::FirstKeyboardFocusableOption() {
+  if (Empty()) {
+    return nullptr;
+  }
+  for (OptionListIterator it = begin(); it; ++it) {
+    if (it->IsKeyboardFocusableSlow(
+            Element::UpdateBehavior::kAssertNoLayoutUpdates)) {
+      return &*it;
+    }
+  }
+  return nullptr;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/forms/option_list.h b/third_party/blink/renderer/core/html/forms/option_list.h
index 7684a4d..ae42194 100644
--- a/third_party/blink/renderer/core/html/forms/option_list.h
+++ b/third_party/blink/renderer/core/html/forms/option_list.h
@@ -100,6 +100,11 @@
     return FindFocusableOption(option, /*forward*/ false, inclusive);
   }
 
+  // This method calls IsKeyboardFocusableSlow with
+  // Element::UpdateBehavior::kAssertNoLayoutUpdates on each option until it
+  // finds a focusable one, then returns it.
+  HTMLOptionElement* FirstKeyboardFocusableOption();
+
  private:
   HTMLOptionElement* FindFocusableOption(HTMLOptionElement& option,
                                          bool forward,
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 5b8c7ef4c..f81577523 100644
--- a/third_party/blink/renderer/core/html/forms/select_type.cc
+++ b/third_party/blink/renderer/core/html/forms/select_type.cc
@@ -148,17 +148,26 @@
           control->Focus(FocusParams(FocusTrigger::kScript));
         }
       } else {
-        HTMLOptionElement* option_to_focus = select->SelectedOption();
-        if (!option_to_focus || !option_to_focus->IsFocusable()) {
-          for (auto& option : select->GetOptionList()) {
-            if (option.IsFocusable()) {
-              option_to_focus = &option;
-              break;
+        HTMLElement* element_to_focus = nullptr;
+        if (auto* input = select->FirstDescendantTextInput();
+            input &&
+            RuntimeEnabledFeatures::SelectAccessibilityReparentInputEnabled()) {
+          // If there is a filter input at the top of the picker, then that
+          // should be focused instead of options when opening.
+          element_to_focus = input;
+        } else {
+          element_to_focus = select->SelectedOption();
+          if (!element_to_focus || !element_to_focus->IsFocusable()) {
+            for (auto& option : select->GetOptionList()) {
+              if (option.IsFocusable()) {
+                element_to_focus = &option;
+                break;
+              }
             }
           }
         }
-        if (option_to_focus) {
-          option_to_focus->Focus(FocusParams(FocusTrigger::kScript));
+        if (element_to_focus) {
+          element_to_focus->Focus(FocusParams(FocusTrigger::kScript));
         }
       }
       if (AXObjectCache* cache =
diff --git a/third_party/blink/renderer/core/html/forms/text_control_element.cc b/third_party/blink/renderer/core/html/forms/text_control_element.cc
index 3da7c098..0a82b714 100644
--- a/third_party/blink/renderer/core/html/forms/text_control_element.cc
+++ b/third_party/blink/renderer/core/html/forms/text_control_element.cc
@@ -928,25 +928,7 @@
   } else {
     inner_editor->RemoveChildren();
     // For <textarea>, \n is replaced with <br>.
-    wtf_size_t start = 0;
-    wtf_size_t i = 0;
-    while (start < value.length()) {
-      i = value.find('\n', start);
-      if (i == WTF::kNotFound) {
-        inner_editor->AppendChild(
-            Text::Create(GetDocument(), value.Substring(start)));
-        break;
-      }
-      if (start != i) {
-        // Append [start, i).
-        inner_editor->AppendChild(
-            Text::Create(GetDocument(), value.Substring(start, i - start)));
-      }
-      // Append a BR.
-      inner_editor->AppendChild(
-          MakeGarbageCollected<HTMLBRElement>(GetDocument()));
-      start = i + 1;
-    }
+    AppendTextOrBr(value, *inner_editor);
   }
 
   // Add a placeholder <br> so that we can put the caret at the next line of
@@ -959,6 +941,27 @@
   }
 }
 
+void TextControlElement::AppendTextOrBr(const String& value,
+                                        ContainerNode& container) {
+  Document& doc = container.GetDocument();
+  wtf_size_t start = 0;
+  while (start < value.length()) {
+    wtf_size_t i = value.find('\n', start);
+    if (i == WTF::kNotFound) {
+      container.AppendChild(Text::Create(doc, value.Substring(start)));
+      break;
+    }
+    if (start != i) {
+      // Append [start, i).
+      container.AppendChild(
+          Text::Create(doc, value.Substring(start, i - start)));
+    }
+    // Append a BR.
+    container.AppendChild(MakeGarbageCollected<HTMLBRElement>(doc));
+    start = i + 1;
+  }
+}
+
 String TextControlElement::InnerEditorValue() const {
   DCHECK(!OpenShadowRoot());
   HTMLElement* inner_editor = InnerEditorElement();
diff --git a/third_party/blink/renderer/core/html/forms/text_control_element.h b/third_party/blink/renderer/core/html/forms/text_control_element.h
index 89b4899e..e8c18e4 100644
--- a/third_party/blink/renderer/core/html/forms/text_control_element.h
+++ b/third_party/blink/renderer/core/html/forms/text_control_element.h
@@ -145,6 +145,7 @@
   bool LastChangeWasUserEdit() const;
 
   virtual void SetInnerEditorValue(const String&);
+  static void AppendTextOrBr(const String& value, ContainerNode& container);
   String InnerEditorValue() const;
   Node* CreatePlaceholderBreakElement() const;
   // Returns true if the specified node was created by
diff --git a/third_party/blink/renderer/core/html/forms/text_field_input_type.cc b/third_party/blink/renderer/core/html/forms/text_field_input_type.cc
index d495897..d63317b 100644
--- a/third_party/blink/renderer/core/html/forms/text_field_input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/text_field_input_type.cc
@@ -43,6 +43,8 @@
 #include "third_party/blink/renderer/core/events/text_event.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/html/forms/html_input_element.h"
+#include "third_party/blink/renderer/core/html/forms/html_option_element.h"
+#include "third_party/blink/renderer/core/html/forms/html_select_element.h"
 #include "third_party/blink/renderer/core/html/forms/text_control_inner_elements.h"
 #include "third_party/blink/renderer/core/html/shadow/shadow_element_names.h"
 #include "third_party/blink/renderer/core/html_names.h"
@@ -216,6 +218,20 @@
 void TextFieldInputType::HandleKeydownEvent(KeyboardEvent& event) {
   if (!GetElement().IsFocused())
     return;
+
+  if (RuntimeEnabledFeatures::SelectAccessibilityReparentInputEnabled()) {
+    if (auto* select = GetElement().FirstAncestorSelectElement()) {
+      if (AtomicString(event.key()) == keywords::kArrowDown) {
+        if (auto* option =
+                select->GetOptionList().FirstKeyboardFocusableOption()) {
+          option->Focus();
+          event.SetDefaultHandled();
+          return;
+        }
+      }
+    }
+  }
+
   if (ChromeClient* chrome_client = GetChromeClient()) {
     chrome_client->HandleKeyboardEventOnTextField(GetElement(), event);
     return;
diff --git a/third_party/blink/renderer/core/html/html_link_element.cc b/third_party/blink/renderer/core/html/html_link_element.cc
index 1e63ed3..a9c094f 100644
--- a/third_party/blink/renderer/core/html/html_link_element.cc
+++ b/third_party/blink/renderer/core/html/html_link_element.cc
@@ -28,6 +28,7 @@
 #include <utility>
 
 #include "base/numerics/safe_conversions.h"
+#include "base/trace_event/typed_macros.h"
 #include "third_party/blink/public/platform/task_type.h"
 #include "third_party/blink/public/platform/web_icon_sizes_parser.h"
 #include "third_party/blink/public/platform/web_prescient_networking.h"
@@ -102,6 +103,20 @@
       UseCounter::Count(&GetDocument(), WebFeature::kLinkRelFacilitatedPayment);
       MaybeHandlePaymentLink();
     }
+    if (rel_attribute_.IsPreconnect()) {
+      TRACE_EVENT_INSTANT(
+          TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "LinkPreconnect",
+          "data", [&](perfetto::TracedValue context) {
+            auto dict = std::move(context).WriteDictionary();
+            if (GetDocument().GetFrame()) {
+              dict.Add("frame",
+                       GetDocument().GetFrame()->GetFrameIdForTracing());
+            }
+            dict.Add("node_id", GetDomNodeId());
+            const KURL& url = GetNonEmptyURLAttribute(html_names::kHrefAttr);
+            dict.Add("url", url.GetString());
+          });
+    }
     rel_list_->DidUpdateAttributeValue(params.old_value, value);
     Process();
   } else if (name == html_names::kBlockingAttr) {
diff --git a/third_party/blink/renderer/core/layout/block_break_token.cc b/third_party/blink/renderer/core/layout/block_break_token.cc
index bcd2440..4186808 100644
--- a/third_party/blink/renderer/core/layout/block_break_token.cc
+++ b/third_party/blink/renderer/core/layout/block_break_token.cc
@@ -8,6 +8,7 @@
 #include "third_party/blink/renderer/core/layout/inline/inline_break_token.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/heap/member.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/wtf/size_assertions.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
 
@@ -146,7 +147,8 @@
   string_builder.Append(ConsumedBlockSize().ToString());
   string_builder.Append("px");
 
-  if (ConsumedBlockSizeForLegacy() != ConsumedBlockSize()) {
+  if (!RuntimeEnabledFeatures::LayoutBoxVisualLocationEnabled() &&
+      ConsumedBlockSizeForLegacy() != ConsumedBlockSize()) {
     string_builder.Append(" legacy consumed:");
     string_builder.Append(ConsumedBlockSizeForLegacy().ToString());
     string_builder.Append("px");
diff --git a/third_party/blink/renderer/core/layout/block_break_token.h b/third_party/blink/renderer/core/layout/block_break_token.h
index edb4cd0..5ab3639 100644
--- a/third_party/blink/renderer/core/layout/block_break_token.h
+++ b/third_party/blink/renderer/core/layout/block_break_token.h
@@ -11,6 +11,7 @@
 #include "third_party/blink/renderer/core/layout/block_break_token_data.h"
 #include "third_party/blink/renderer/core/layout/break_token.h"
 #include "third_party/blink/renderer/platform/geometry/layout_unit.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/wtf/casting.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 
@@ -87,6 +88,7 @@
   // size when used for legacy. This difference is represented by
   // |consumed_block_size_legacy_adjustment_|.
   LayoutUnit ConsumedBlockSizeForLegacy() const {
+    DCHECK(!RuntimeEnabledFeatures::LayoutBoxVisualLocationEnabled());
 #if DCHECK_IS_ON()
     DCHECK(!is_repeated_actual_break_);
 #endif
diff --git a/third_party/blink/renderer/core/layout/box_fragment_builder.h b/third_party/blink/renderer/core/layout/box_fragment_builder.h
index 73da82dc..f203201 100644
--- a/third_party/blink/renderer/core/layout/box_fragment_builder.h
+++ b/third_party/blink/renderer/core/layout/box_fragment_builder.h
@@ -29,6 +29,7 @@
 #include "third_party/blink/renderer/core/layout/table/table_borders.h"
 #include "third_party/blink/renderer/core/layout/table/table_fragment_data.h"
 #include "third_party/blink/renderer/core/style/computed_style_constants.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
 
@@ -425,6 +426,7 @@
   // Set how much to adjust |consumed_block_size_| for legacy write-back. See
   // BlockBreakToken::ConsumedBlockSizeForLegacy() for more details.
   void SetConsumedBlockSizeLegacyAdjustment(LayoutUnit adjustment) {
+    DCHECK(!RuntimeEnabledFeatures::LayoutBoxVisualLocationEnabled());
     EnsureBreakTokenData()->consumed_block_size_legacy_adjustment = adjustment;
   }
 
diff --git a/third_party/blink/renderer/core/layout/fragment_builder.h b/third_party/blink/renderer/core/layout/fragment_builder.h
index 30951d46..1bd8fb5 100644
--- a/third_party/blink/renderer/core/layout/fragment_builder.h
+++ b/third_party/blink/renderer/core/layout/fragment_builder.h
@@ -37,6 +37,7 @@
  public:
   ~FragmentBuilder() {
     // Clear collections so the backing gets promptly freed, and reused.
+    children_.clear();
     oof_positioned_candidates_.clear();
     oof_positioned_fragmentainer_descendants_.clear();
     oof_positioned_descendants_.clear();
diff --git a/third_party/blink/renderer/core/layout/fragmentation_utils.cc b/third_party/blink/renderer/core/layout/fragmentation_utils.cc
index d94eaa98..b2ee945 100644
--- a/third_party/blink/renderer/core/layout/fragmentation_utils.cc
+++ b/third_party/blink/renderer/core/layout/fragmentation_utils.cc
@@ -844,22 +844,25 @@
     consumed_block_size += fragmentainer_capacity;
     builder->SetConsumedBlockSize(consumed_block_size);
 
-    // We clamp the fragmentainer block size from 0 to 1 for legacy write-back
-    // if there is content that overflows the zero-height fragmentainer.
-    // Set the consumed block size adjustment for legacy if this results
-    // in a different consumed block size than is used for NG layout.
-    LayoutUnit consumed_block_size_for_legacy =
-        previous_break_token
-            ? previous_break_token->ConsumedBlockSizeForLegacy()
-            : LayoutUnit();
-    LayoutUnit legacy_fragmentainer_block_size =
-        (builder->IntrinsicBlockSize() > LayoutUnit()) ? fragmentainer_capacity
-                                                       : block_size;
-    LayoutUnit consumed_block_size_legacy_adjustment =
-        consumed_block_size_for_legacy + legacy_fragmentainer_block_size -
-        consumed_block_size;
-    builder->SetConsumedBlockSizeLegacyAdjustment(
-        consumed_block_size_legacy_adjustment);
+    if (!RuntimeEnabledFeatures::LayoutBoxVisualLocationEnabled()) {
+      // We clamp the fragmentainer block size from 0 to 1 for legacy write-back
+      // if there is content that overflows the zero-height fragmentainer.  Set
+      // the consumed block size adjustment for legacy if this results in a
+      // different consumed block size than is used for NG layout.
+      LayoutUnit consumed_block_size_for_legacy =
+          previous_break_token
+              ? previous_break_token->ConsumedBlockSizeForLegacy()
+              : LayoutUnit();
+      LayoutUnit legacy_fragmentainer_block_size =
+          (builder->IntrinsicBlockSize() > LayoutUnit())
+              ? fragmentainer_capacity
+              : block_size;
+      LayoutUnit consumed_block_size_legacy_adjustment =
+          consumed_block_size_for_legacy + legacy_fragmentainer_block_size -
+          consumed_block_size;
+      builder->SetConsumedBlockSizeLegacyAdjustment(
+          consumed_block_size_legacy_adjustment);
+    }
 
     if (previous_break_token && previous_break_token->MonolithicOverflow()) {
       // Add pages as long as there's monolithic overflow that requires it.
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc
index d294664..a739462 100644
--- a/third_party/blink/renderer/core/layout/layout_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -3865,7 +3865,7 @@
     } else {
       DCHECK(previous_break_token);
       size.block_size = fragment_logical_size.block_size +
-                        previous_break_token->ConsumedBlockSizeForLegacy();
+                        previous_break_token->ConsumedBlockSize();
     }
     previous_break_token = physical_fragment.GetBreakToken();
     // Continue in order to update logical height, unless this fragment is
diff --git a/third_party/blink/renderer/core/layout/out_of_flow_layout_part.cc b/third_party/blink/renderer/core/layout/out_of_flow_layout_part.cc
index 8d2a462..d90a4b4 100644
--- a/third_party/blink/renderer/core/layout/out_of_flow_layout_part.cc
+++ b/third_party/blink/renderer/core/layout/out_of_flow_layout_part.cc
@@ -3112,27 +3112,21 @@
 
 void OutOfFlowLayoutPart::SaveStaticPositionOnPaintLayer(
     LayoutBox* layout_box,
-    const LogicalStaticPosition& position) const {
+    LogicalStaticPosition position) const {
   const LayoutObject* parent =
       GetLayoutObjectForParentNode<const LayoutObject*>(layout_box);
   const LayoutObject* container = container_builder_->GetLayoutObject();
   if (parent == container ||
       (parent->IsLayoutInline() && parent->ContainingBlock() == container)) {
     DCHECK(layout_box->Layer());
-    layout_box->Layer()->SetStaticPositionFromNG(
-        ToStaticPositionForLegacy(position));
+    if (const auto* break_token = container_builder_->PreviousBreakToken()) {
+      // Include the block contribution from previous columns.
+      position.offset.block_offset += break_token->ConsumedBlockSize();
+    }
+    layout_box->Layer()->SetStaticPositionFromNG(position);
   }
 }
 
-LogicalStaticPosition OutOfFlowLayoutPart::ToStaticPositionForLegacy(
-    LogicalStaticPosition position) const {
-  // Legacy expects the static position to include the block contribution from
-  // previous columns.
-  if (const auto* break_token = container_builder_->PreviousBreakToken())
-    position.offset.block_offset += break_token->ConsumedBlockSizeForLegacy();
-  return position;
-}
-
 const PhysicalBoxFragment& OutOfFlowLayoutPart::GetChildFragment(
     wtf_size_t index) const {
   const LogicalFragmentLink& link = FragmentationContextChildren()[index];
diff --git a/third_party/blink/renderer/core/layout/out_of_flow_layout_part.h b/third_party/blink/renderer/core/layout/out_of_flow_layout_part.h
index 8f92acc..c7869f4 100644
--- a/third_party/blink/renderer/core/layout/out_of_flow_layout_part.h
+++ b/third_party/blink/renderer/core/layout/out_of_flow_layout_part.h
@@ -406,11 +406,8 @@
 
   // This saves the static-position for an OOF-positioned object into its
   // paint-layer.
-  void SaveStaticPositionOnPaintLayer(
-      LayoutBox* layout_box,
-      const LogicalStaticPosition& position) const;
-  LogicalStaticPosition ToStaticPositionForLegacy(
-      LogicalStaticPosition position) const;
+  void SaveStaticPositionOnPaintLayer(LayoutBox* layout_box,
+                                      LogicalStaticPosition position) const;
 
   const FragmentBuilder::ChildrenVector& FragmentationContextChildren() const {
     DCHECK(container_builder_->IsBlockFragmentationContextRoot());
diff --git a/third_party/blink/renderer/core/layout/physical_box_fragment.cc b/third_party/blink/renderer/core/layout/physical_box_fragment.cc
index 8a3f38f2..413576d0 100644
--- a/third_party/blink/renderer/core/layout/physical_box_fragment.cc
+++ b/third_party/blink/renderer/core/layout/physical_box_fragment.cc
@@ -347,10 +347,10 @@
   DCHECK(layout_object_->IsBoxModelObject());
   DCHECK(!builder->break_token_ || builder->break_token_->IsBlockType());
 
-  children_.ReserveInitialCapacity(builder->children_.size());
-  PhysicalSize size = Size();
   const WritingModeConverter converter(
-      {block_or_line_writing_mode, builder->Direction()}, size);
+      {block_or_line_writing_mode, builder->Direction()}, Size());
+
+  children_.ReserveInitialCapacity(builder->children_.size());
   for (auto& child : builder->children_) {
     children_.emplace_back(
         std::move(child.fragment),
diff --git a/third_party/blink/renderer/core/lcp_critical_path_predictor/lcp_critical_path_predictor.cc b/third_party/blink/renderer/core/lcp_critical_path_predictor/lcp_critical_path_predictor.cc
index be44281..bec2811 100644
--- a/third_party/blink/renderer/core/lcp_critical_path_predictor/lcp_critical_path_predictor.cc
+++ b/third_party/blink/renderer/core/lcp_critical_path_predictor/lcp_critical_path_predictor.cc
@@ -58,7 +58,8 @@
 }
 
 void LCPCriticalPathPredictor::set_lcp_element_locators(
-    const std::vector<std::string>& lcp_element_locator_strings) {
+    const std::vector<std::string>& lcp_element_locator_strings,
+    const std::vector<std::string>& lcp_element_locator_all_strings) {
   // Clear current set of locators before receiving replacements.
   lcp_element_locators_.clear();
   lcp_element_locator_strings_.clear();
@@ -80,6 +81,21 @@
     }
   }
   CHECK_EQ(lcp_element_locators_.size(), lcp_element_locator_strings_.size());
+
+  lcp_element_locator_all_strings_.clear();
+  lcp_element_locator_all_strings_.reserve(
+      base::checked_cast<wtf_size_t>(lcp_element_locator_all_strings.size()));
+  for (const std::string& serialized_locator :
+       lcp_element_locator_all_strings) {
+    bool result = ElementLocator().ParseFromString(serialized_locator);
+    if (!result) {
+      // This can happen when the host LCPP database is corrupted or we
+      // updated the ElementLocator schema in an incompatible way.
+      LOG(INFO) << "Ignoring an invalid lcp_element_locator hint.";
+    } else {
+      lcp_element_locator_all_strings_.push_back(std::move(serialized_locator));
+    }
+  }
 }
 
 void LCPCriticalPathPredictor::set_lcp_influencer_scripts(
@@ -183,7 +199,7 @@
     // set_lcp_element_locators(lcp_element_locator_strings).
     // See PredictLcpElementLocators() for the contents detail.
     const wtf_size_t predicted_lcp_index =
-        lcp_element_locator_strings_.Find(lcp_element_locator_string);
+        lcp_element_locator_all_strings_.Find(lcp_element_locator_string);
     if (predicted_lcp_index != kNotFound) {
       MayRunPredictedCallbacks(&lcp_element);
     }
@@ -363,7 +379,7 @@
   is_outermost_main_frame_document_loaded_ = true;
   // Call callbacks as fallback because we can not detect
   // which is lcp in the lcps before onload.
-  if (has_lcp_occurred_ || lcp_element_locators_.empty()) {
+  if (has_lcp_occurred_ || lcp_element_locator_all_strings_.empty()) {
     MayRunPredictedCallbacks(nullptr);
   }
 }
diff --git a/third_party/blink/renderer/core/lcp_critical_path_predictor/lcp_critical_path_predictor.h b/third_party/blink/renderer/core/lcp_critical_path_predictor/lcp_critical_path_predictor.h
index 759ac49..9a3a313 100644
--- a/third_party/blink/renderer/core/lcp_critical_path_predictor/lcp_critical_path_predictor.h
+++ b/third_party/blink/renderer/core/lcp_critical_path_predictor/lcp_critical_path_predictor.h
@@ -43,7 +43,8 @@
   bool HasAnyHintData() const;
 
   void set_lcp_element_locators(
-      const std::vector<std::string>& lcp_element_locator_strings);
+      const std::vector<std::string>& lcp_element_locator_strings,
+      const std::vector<std::string>& lcp_element_locator_all_strings);
 
   const Vector<ElementLocator>& lcp_element_locators() {
     return lcp_element_locators_;
@@ -102,6 +103,7 @@
 
   Vector<ElementLocator> lcp_element_locators_;
   Vector<std::string> lcp_element_locator_strings_;
+  Vector<std::string> lcp_element_locator_all_strings_;
   HashSet<KURL> lcp_influencer_scripts_;
   Vector<KURL> fetched_fonts_;
   Vector<url::Origin> preconnected_origins_;
diff --git a/third_party/blink/renderer/core/origin_trials/origin_trial_context.cc b/third_party/blink/renderer/core/origin_trials/origin_trial_context.cc
index a293858..7cca0ca 100644
--- a/third_party/blink/renderer/core/origin_trials/origin_trial_context.cc
+++ b/third_party/blink/renderer/core/origin_trials/origin_trial_context.cc
@@ -547,10 +547,6 @@
     return base::FeatureList::IsEnabled(features::kSoftNavigationDetection);
   }
 
-  if (trial_name == "FoldableAPIs") {
-    return base::FeatureList::IsEnabled(features::kViewportSegments);
-  }
-
   if (trial_name == "PermissionElement") {
     return base::FeatureList::IsEnabled(blink::features::kPermissionElement);
   }
diff --git a/third_party/blink/renderer/core/style/style_fetched_image.cc b/third_party/blink/renderer/core/style/style_fetched_image.cc
index 4c26612..d35ebd8 100644
--- a/third_party/blink/renderer/core/style/style_fetched_image.cc
+++ b/third_party/blink/renderer/core/style/style_fetched_image.cc
@@ -95,7 +95,7 @@
 
 CSSValue* StyleFetchedImage::CssValue() const {
   return MakeGarbageCollected<CSSImageValue>(
-      *url_data_->MakeAbsolute(), const_cast<StyleFetchedImage*>(this));
+      *url_data_->MakeComputed(), const_cast<StyleFetchedImage*>(this));
 }
 
 CSSValue* StyleFetchedImage::ComputedCSSValue(const ComputedStyle&,
diff --git a/third_party/blink/renderer/core/style/style_mask_source_image.cc b/third_party/blink/renderer/core/style/style_mask_source_image.cc
index 4eabef3..ae73f17e 100644
--- a/third_party/blink/renderer/core/style/style_mask_source_image.cc
+++ b/third_party/blink/renderer/core/style/style_mask_source_image.cc
@@ -36,7 +36,7 @@
     const ComputedStyle& style,
     bool allow_visited_style,
     CSSValuePhase value_phase) const {
-  return resource_css_value_->ComputedCSSValueMaybeLocal();
+  return resource_css_value_->ComputedCSSValue();
 }
 
 bool StyleMaskSourceImage::CanRender() const {
diff --git a/third_party/blink/renderer/core/svg/svg_transform_list.cc b/third_party/blink/renderer/core/svg/svg_transform_list.cc
index cac2c009..61d34ba 100644
--- a/third_party/blink/renderer/core/svg/svg_transform_list.cc
+++ b/third_party/blink/renderer/core/svg/svg_transform_list.cc
@@ -324,21 +324,27 @@
     return SVGTransformType::kUnknown;
 
   if (*ptr == 's') {
-    if (SkipToken(ptr, end, "skewX"))
+    if (UNSAFE_TODO(SkipToken(ptr, end, "skewX"))) {
       return SVGTransformType::kSkewx;
-    if (SkipToken(ptr, end, "skewY"))
+    }
+    if (UNSAFE_TODO(SkipToken(ptr, end, "skewY"))) {
       return SVGTransformType::kSkewy;
-    if (SkipToken(ptr, end, "scale"))
+    }
+    if (UNSAFE_TODO(SkipToken(ptr, end, "scale"))) {
       return SVGTransformType::kScale;
+    }
 
     return SVGTransformType::kUnknown;
   }
-  if (SkipToken(ptr, end, "translate"))
+  if (UNSAFE_TODO(SkipToken(ptr, end, "translate"))) {
     return SVGTransformType::kTranslate;
-  if (SkipToken(ptr, end, "rotate"))
+  }
+  if (UNSAFE_TODO(SkipToken(ptr, end, "rotate"))) {
     return SVGTransformType::kRotate;
-  if (SkipToken(ptr, end, "matrix"))
+  }
+  if (UNSAFE_TODO(SkipToken(ptr, end, "matrix"))) {
     return SVGTransformType::kMatrix;
+  }
 
   return SVGTransformType::kUnknown;
 }
diff --git a/third_party/blink/renderer/core/svg/svg_view_spec.cc b/third_party/blink/renderer/core/svg/svg_view_spec.cc
index b1ecf8b..42779d708 100644
--- a/third_party/blink/renderer/core/svg/svg_view_spec.cc
+++ b/third_party/blink/renderer/core/svg/svg_view_spec.cc
@@ -94,22 +94,27 @@
   DCHECK_LT(ptr, end);
   switch (*ptr) {
     case 'v':
-      if (SkipToken(ptr, end, "viewBox"))
+      if (UNSAFE_TODO(SkipToken(ptr, end, "viewBox"))) {
         return kViewBox;
-      if (SkipToken(ptr, end, "viewTarget"))
+      }
+      if (UNSAFE_TODO(SkipToken(ptr, end, "viewTarget"))) {
         return kViewTarget;
+      }
       break;
     case 'z':
-      if (SkipToken(ptr, end, "zoomAndPan"))
+      if (UNSAFE_TODO(SkipToken(ptr, end, "zoomAndPan"))) {
         return kZoomAndPan;
+      }
       break;
     case 'p':
-      if (SkipToken(ptr, end, "preserveAspectRatio"))
+      if (UNSAFE_TODO(SkipToken(ptr, end, "preserveAspectRatio"))) {
         return kPreserveAspectRatio;
+      }
       break;
     case 't':
-      if (SkipToken(ptr, end, "transform"))
+      if (UNSAFE_TODO(SkipToken(ptr, end, "transform"))) {
         return kTransform;
+      }
       break;
   }
   return kUnknown;
@@ -121,8 +126,9 @@
 bool SVGViewSpec::ParseViewSpecInternal(base::span<const CharType> chars) {
   const CharType* ptr = chars.data();
   const CharType* end = UNSAFE_TODO(ptr + chars.size());
-  if (!SkipToken(ptr, end, "svgView"))
+  if (!UNSAFE_TODO(SkipToken(ptr, end, "svgView"))) {
     return false;
+  }
 
   size_t position = ptr - chars.data();
   if (!SkipExactly<CharType>(chars, '(', position)) {
@@ -158,7 +164,7 @@
       }
       case kViewTarget: {
         // Ignore arguments.
-        SkipUntil<CharType>(ptr, end, ')');
+        UNSAFE_TODO(SkipUntil<CharType>(ptr, end, ')'));
         break;
       }
       case kZoomAndPan:
diff --git a/third_party/blink/renderer/core/svg/svg_zoom_and_pan.cc b/third_party/blink/renderer/core/svg/svg_zoom_and_pan.cc
index 8449ab8..b65147d 100644
--- a/third_party/blink/renderer/core/svg/svg_zoom_and_pan.cc
+++ b/third_party/blink/renderer/core/svg/svg_zoom_and_pan.cc
@@ -49,10 +49,12 @@
 template <typename CharType>
 static SVGZoomAndPanType ParseZoomAndPanInternal(const CharType*& start,
                                                  const CharType* end) {
-  if (SkipToken(start, end, "disable"))
+  if (UNSAFE_TODO(SkipToken(start, end, "disable"))) {
     return kSVGZoomAndPanDisable;
-  if (SkipToken(start, end, "magnify"))
+  }
+  if (UNSAFE_TODO(SkipToken(start, end, "magnify"))) {
     return kSVGZoomAndPanMagnify;
+  }
   return kSVGZoomAndPanUnknown;
 }
 
diff --git a/third_party/blink/renderer/core/url_pattern/url_pattern_component.cc b/third_party/blink/renderer/core/url_pattern/url_pattern_component.cc
index 0c2a89f..9263273 100644
--- a/third_party/blink/renderer/core/url_pattern/url_pattern_component.cc
+++ b/third_party/blink/renderer/core/url_pattern/url_pattern_component.cc
@@ -8,6 +8,7 @@
 #include <string_view>
 
 #include "base/metrics/histogram_functions.h"
+#include "base/notreached.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/strings/string_util.h"
 #include "components/url_pattern/url_pattern_util.h"
@@ -47,9 +48,12 @@
 }
 
 // Utility method to get the correct encoding callback for a given type.
-liburlpattern::EncodeCallback GetEncodeCallback(std::string_view pattern_utf8,
-                                                Component::Type type,
-                                                Component* protocol_component) {
+// `should_treat_as_standard_url` is used if and only if `type` equals
+// `kPathname`.
+liburlpattern::EncodeCallback GetEncodeCallback(
+    std::string_view pattern_utf8,
+    Component::Type type,
+    std::optional<bool> should_treat_as_standard_url) {
   switch (type) {
     case Component::Type::kProtocol:
       return ::url_pattern::ProtocolEncodeCallback;
@@ -73,26 +77,16 @@
       //
       //  https://url.spec.whatwg.org/#cannot-be-a-base-url-path-state
       //
-      // We prefer "standard" URL here by checking to see if the protocol
-      // pattern matches any of the known standard protocol strings.  So
-      // an exact pattern of `http` will match, but so will `http{s}?` and
-      // `*`.
-      //
-      // If the protocol pattern does not match any of the known standard URL
-      // protocols then we fall back to the "path" URL behavior.  This will
-      // normally be triggered by `data`, `javascript`, `about`, etc.  It
-      // will also be triggered for custom protocol strings.  We favor "path"
-      // behavior here because its better to under canonicalize since the
-      // developer can always manually canonicalize the pathname for a custom
-      // protocol.
-      //
-      // ShouldTreatAsStandardURL can by a bit expensive, so only do it if we
-      // actually have a pathname pattern to compile.
-      CHECK(protocol_component);
-      if (protocol_component->ShouldTreatAsStandardURL())
+      // In "path" URL cases, we fall back to the opaque pathname behavior.  We
+      // favor this behavior here because it is better to canonicalize less
+      // since developers can always manually canonicalize inputs for, e.g.,
+      // their custom protocols.
+      CHECK(should_treat_as_standard_url.has_value());
+      if (*should_treat_as_standard_url) {
         return ::url_pattern::StandardURLPathnameEncodeCallback;
-      else
+      } else {
         return ::url_pattern::PathURLPathnameEncodeCallback;
+      }
     case Component::Type::kSearch:
       return ::url_pattern::SearchEncodeCallback;
     case Component::Type::kHash:
@@ -102,10 +96,11 @@
 }
 
 // Utility method to get the correct liburlpattern parse options for a given
-// type.
+// type.  `should_treat_as_standard_url` is used if and only if `type` equals
+// `kPathname`.
 const liburlpattern::Options GetOptions(
     Component::Type type,
-    Component* protocol_component,
+    std::optional<bool> should_treat_as_standard_url,
     const URLPatternOptions& external_options) {
   using liburlpattern::Options;
 
@@ -126,10 +121,10 @@
     // Just like how we select a different encoding callback based on
     // whether we are treating the pattern string as a standard or
     // cannot-be-a-base URL, we must also choose the right liburlppatern
-    // options as well.  We should only use use the options that treat
-    // `/` specially if we are treating this a standard URL.
-    DCHECK(protocol_component);
-    if (protocol_component->ShouldTreatAsStandardURL()) {
+    // options as well.  We should only use the options that treat "/" specially
+    // if we are treating this a standard URL.
+    CHECK(should_treat_as_standard_url.has_value());
+    if (*should_treat_as_standard_url) {
       // Pathname patterns for "standard" URLs use a "/" delimiter controlling
       // how far a named group like ":bar" will match.  They also use "/" as an
       // automatic prefix before groups.
@@ -188,18 +183,24 @@
                               Component* protocol_component,
                               const URLPatternOptions& external_options,
                               ExceptionState& exception_state) {
-  StringView final_pattern = pattern.IsNull() ? "*" : pattern;
+  std::optional<bool> should_treat_as_standard_url =
+      protocol_component
+          ? std::optional(protocol_component->ShouldTreatAsStandardURL())
+          : std::nullopt;
   const liburlpattern::Options& options =
-      GetOptions(type, protocol_component, external_options);
+      GetOptions(type, should_treat_as_standard_url, external_options);
 
+  StringView final_pattern = pattern.IsNull() ? "*" : pattern;
   // Parse the pattern.
   // Lossy UTF8 conversion is fine given the input has come through a
   // USVString webidl argument.
   StringUTF8Adaptor utf8(final_pattern);
-  auto parse_result = liburlpattern::Parse(
-      utf8.AsStringView(),
-      GetEncodeCallback(utf8.AsStringView(), type, protocol_component),
-      options);
+
+  auto parse_result =
+      liburlpattern::Parse(utf8.AsStringView(),
+                           GetEncodeCallback(utf8.AsStringView(), type,
+                                             should_treat_as_standard_url),
+                           options);
   if (!parse_result.has_value()) {
     exception_state.ThrowTypeError(
         "Invalid " + TypeToString(type) + " pattern '" + final_pattern + "'. " +
diff --git a/third_party/blink/renderer/core/url_pattern/url_pattern_component.h b/third_party/blink/renderer/core/url_pattern/url_pattern_component.h
index e27382e1..350bef5 100644
--- a/third_party/blink/renderer/core/url_pattern/url_pattern_component.h
+++ b/third_party/blink/renderer/core/url_pattern/url_pattern_component.h
@@ -84,7 +84,14 @@
 
   // Method to determine if the URL associated with this component should be
   // treated as a "standard" URL like `https://foo` vs a "path" URL like
-  // `data:foo`.  This should only be called for kProtocol components.
+  // `data:foo`.  This should only be called for `kProtocol` components.
+  //
+  // This function checks if the protocol pattern matches any of the known
+  // standard protocol strings.  So an exact pattern of `http` will match, but
+  // so will `http{s}?` and `*`.  Typical non-standard protocols are `data`,
+  // `javascript`, `about`, and any other custom protocol strings.  The
+  // computation cost of this function may be a bit expensive for the first
+  // call, but the result is cached once computed.
   bool ShouldTreatAsStandardURL() const;
 
   // Returns if this component has at least one part that uses an ECMAScript
diff --git a/third_party/blink/renderer/core/workers/shared_worker_reporting_proxy.cc b/third_party/blink/renderer/core/workers/shared_worker_reporting_proxy.cc
index f6b6eb5f..b2fa6947 100644
--- a/third_party/blink/renderer/core/workers/shared_worker_reporting_proxy.cc
+++ b/third_party/blink/renderer/core/workers/shared_worker_reporting_proxy.cc
@@ -38,8 +38,8 @@
     std::unique_ptr<SourceLocation>,
     int exception_id) {
   DCHECK(!IsMainThread());
-  // TODO(nhiroki): Implement the "runtime script errors" algorithm in the HTML
-  // spec:
+  // TODO(crbug.com/412384494): Implement the "runtime script errors" algorithm
+  // in the HTML spec:
   // "For shared workers, if the error is still not handled afterwards, the
   // error may be reported to a developer console."
   // https://html.spec.whatwg.org/C/#runtime-script-errors-2
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 6889692..75c13500 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -555,6 +555,17 @@
       node->IsScrollButtonPseudoElement()) {
     return true;
   }
+
+  if (RuntimeEnabledFeatures::SelectAccessibilityReparentInputEnabled()) {
+    // The first descendant <input> in a <select> gets taken out of the listbox
+    // because it is not an <option>. It controls the listbox.
+    if (auto* input = DynamicTo<HTMLInputElement>(node)) {
+      if (input->IsFirstTextInputInAncestorSelect()) {
+        return true;
+      }
+    }
+  }
+
   return false;
 }
 
@@ -4448,6 +4459,22 @@
 }
 
 AXObject* AXNodeObject::ChooserPopup() const {
+  if (RuntimeEnabledFeatures::SelectAccessibilityReparentInputEnabled()) {
+    // The first input inside of a select filters the listbox, and therefore
+    // controls it.
+    if (auto* input = DynamicTo<HTMLInputElement>(GetNode())) {
+      if (input->IsTextField()) {
+        if (auto* select = input->FirstAncestorSelectElement()) {
+          if (auto* popover = select->PopoverForAppearanceBase()) {
+            if (auto* axobject = AXObjectCache().Get(popover)) {
+              return axobject;
+            }
+          }
+        }
+      }
+    }
+  }
+
   // When color & date chooser popups are visible, they can be found in the tree
   // as a group child of the <input> control itself.
   switch (native_role_) {
@@ -5926,6 +5953,19 @@
   }
 }
 
+void AXNodeObject::AddSelectChildren() {
+  auto* select = DynamicTo<HTMLSelectElement>(GetNode());
+  if (RuntimeEnabledFeatures::SelectAccessibilityReparentInputEnabled() &&
+      select) {
+    if (auto* input = select->FirstDescendantTextInput()) {
+      // Reparent the first descendant <input> element of this <select> to be
+      // adjacent to the listbox in the a11y tree.
+      AddNodeChild(input);
+    }
+  }
+  AddNodeChildren();
+}
+
 void AXNodeObject::AddOwnedChildren() {
   AXObjectVector owned_children;
   AXObjectCache().ValidatedAriaOwnedChildren(this, owned_children);
@@ -5975,7 +6015,9 @@
     AddValidationMessageChild();
   CHECK_ATTACHED();
 
-  if (HasValidHTMLTableStructureAndLayout()) {
+  if (IsA<HTMLSelectElement>(GetNode())) {
+    AddSelectChildren();
+  } else if (HasValidHTMLTableStructureAndLayout()) {
     AddTableChildren();
   } else if (GetNode() && GetNode()->IsScrollMarkerGroupPseudoElement()) {
     AddScrollMarkerGroupChildren();
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 c836238..da30c91 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.h
@@ -416,6 +416,7 @@
   void AddPopupChildren();
   bool HasValidHTMLTableStructureAndLayout() const;
   void AddTableChildren();
+  void AddSelectChildren();
   bool FindAllTableCellsWithRole(ax::mojom::blink::Role, AXObjectVector&) const;
   void AddValidationMessageChild();
   void AddAccessibleNodeChildren();
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.cc b/third_party/blink/renderer/modules/accessibility/ax_object.cc
index d844143..3fe2f18 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.cc
@@ -1008,6 +1008,18 @@
                : nullptr;
   }
 
+  if (RuntimeEnabledFeatures::SelectAccessibilityReparentInputEnabled()) {
+    if (auto* input = DynamicTo<HTMLInputElement>(node)) {
+      if (auto* select = input->FirstAncestorSelectElement()) {
+        if (input->IsFirstTextInputInAncestorSelect() && input->IsTextField()) {
+          // The first descendant <input> in a <select> is reparented to be a
+          // direct child of the <select> in the a11y tree.
+          return select;
+        }
+      }
+    }
+  }
+
   return CanComputeAsNaturalParent(parent) ? parent : nullptr;
 }
 
diff --git a/third_party/blink/renderer/modules/accessibility/ax_relation_cache.cc b/third_party/blink/renderer/modules/accessibility/ax_relation_cache.cc
index 4f63a2c..73efa1a 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_relation_cache.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_relation_cache.cc
@@ -294,7 +294,9 @@
           << "\n* Child: " << child << "\n* Actual parent: " << parent
           << "\n* Natural ax parent: " << object_cache_->Get(natural_parent)
           << "\n* Natural dom parent: " << natural_parent << " #"
-          << natural_parent->GetDomNodeId() << "\n* Owners to update:";
+          << natural_parent->GetDomNodeId()
+          << "\n* parent->GetNode(): " << parent->GetNode()
+          << "\n* Owners to update:";
       for (AXID id : owner_axids_to_update_) {
         msg << " " << id;
       }
diff --git a/third_party/blink/renderer/modules/ai/language_model.cc b/third_party/blink/renderer/modules/ai/language_model.cc
index a4ee52d..edae9ec 100644
--- a/third_party/blink/renderer/modules/ai/language_model.cc
+++ b/third_party/blink/renderer/modules/ai/language_model.cc
@@ -532,67 +532,32 @@
     const V8LanguageModelPromptInput* input,
     const LanguageModelPromptOptions* options,
     ExceptionState& exception_state) {
-  if (!script_state->ContextIsValid()) {
-    ThrowInvalidContextException(exception_state);
-    return ScriptPromise<IDLString>();
+  std::optional<ValidateAndProcessPromptInputResult> processed_input =
+      ValidateAndProcessPromptInput(script_state, input, options,
+                                    exception_state);
+  if (!processed_input.has_value()) {
+    return EmptyPromise();
   }
+  base::UmaHistogramEnumeration(AIMetrics::GetAIAPIUsageMetricName(
+                                    AIMetrics::AISessionType::kLanguageModel),
+                                AIMetrics::AIAPI::kSessionPrompt);
 
   ScriptPromiseResolver<IDLString>* resolver =
       MakeGarbageCollected<ScriptPromiseResolver<IDLString>>(script_state);
   auto promise = resolver->Promise();
 
-  // The API impl only accepts a string by default for now, more to come soon!
-  if (!input->IsString() &&
-      !RuntimeEnabledFeatures::AIPromptAPIMultimodalInputEnabled()) {
-    resolver->RejectWithTypeError("Input type not supported");
-    return promise;
-  }
-
-  auto prompts = BuildPrompts(input, script_state, exception_state,
-                              GetExecutionContext(), input_types_);
-  if (!prompts.has_value()) {
-    return promise;
-  }
-
-  base::UmaHistogramEnumeration(AIMetrics::GetAIAPIUsageMetricName(
-                                    AIMetrics::AISessionType::kLanguageModel),
-                                AIMetrics::AIAPI::kSessionPrompt);
-
-  // TODO(crbug.com/411470034): Aggregate other input type sizes for UMA.
-  if (input->IsString()) {
-    base::UmaHistogramCounts1M(
-        AIMetrics::GetAISessionRequestSizeMetricName(
-            AIMetrics::AISessionType::kLanguageModel),
-        int(input->GetAsString().CharactersSizeInBytes()));
-  }
-
-  if (!language_model_remote_) {
-    ThrowSessionDestroyedException(exception_state);
-    return promise;
-  }
-
-  AbortSignal* signal = options->getSignalOr(nullptr);
-  if (signal && signal->aborted()) {
-    resolver->Reject(signal->reason(script_state));
-    return promise;
-  }
-
-  on_device_model::mojom::blink::ResponseConstraintPtr constraint;
-  if (!ParseConstraint(script_state, options, exception_state, constraint)) {
-    // ParseConstraint will throw an exception when false is returned.
-    return promise;
-  }
-
   auto pending_remote = CreateModelExecutionResponder(
-      script_state, signal, resolver, task_runner_,
+      script_state, options->getSignalOr(nullptr), resolver, task_runner_,
       AIMetrics::AISessionType::kLanguageModel,
       WTF::BindOnce(&LanguageModel::OnResponseComplete,
                     WrapWeakPersistent(this)),
       WTF::BindRepeating(&LanguageModel::OnQuotaOverflow,
                          WrapWeakPersistent(this)));
-  language_model_remote_->Prompt(std::move(prompts).value(),
-                                 std::move(constraint),
-                                 std::move(pending_remote));
+
+  language_model_remote_->Prompt(
+      std::move(processed_input->processed_prompts),
+      std::move(processed_input->processed_constraint),
+      std::move(pending_remote));
   return promise;
 }
 
@@ -601,65 +566,82 @@
     const V8LanguageModelPromptInput* input,
     const LanguageModelPromptOptions* options,
     ExceptionState& exception_state) {
-  if (!script_state->ContextIsValid()) {
-    ThrowInvalidContextException(exception_state);
+  std::optional<ValidateAndProcessPromptInputResult> processed_input =
+      ValidateAndProcessPromptInput(script_state, input, options,
+                                    exception_state);
+  if (!processed_input.has_value()) {
     return nullptr;
   }
+  base::UmaHistogramEnumeration(AIMetrics::GetAIAPIUsageMetricName(
+                                    AIMetrics::AISessionType::kLanguageModel),
+                                AIMetrics::AIAPI::kSessionPromptStreaming);
 
-  // The API impl only accepts a string by default for now, more to come soon!
+  auto [stream, remote] = CreateModelExecutionStreamingResponder(
+      script_state, options->getSignalOr(nullptr), task_runner_,
+      AIMetrics::AISessionType::kLanguageModel,
+      WTF::BindOnce(&LanguageModel::OnResponseComplete,
+                    WrapWeakPersistent(this)),
+      WTF::BindRepeating(&LanguageModel::OnQuotaOverflow,
+                         WrapWeakPersistent(this)));
+
+  language_model_remote_->Prompt(
+      std::move(processed_input->processed_prompts),
+      std::move(processed_input->processed_constraint), std::move(remote));
+
+  return stream;
+}
+
+std::optional<LanguageModel::ValidateAndProcessPromptInputResult>
+LanguageModel::ValidateAndProcessPromptInput(
+    ScriptState* script_state,
+    const V8LanguageModelPromptInput* input,
+    const LanguageModelPromptOptions* options,
+    ExceptionState& exception_state) {
+  if (!script_state->ContextIsValid()) {
+    ThrowInvalidContextException(exception_state);
+    return std::nullopt;
+  }
+
   if (!input->IsString() &&
       !RuntimeEnabledFeatures::AIPromptAPIMultimodalInputEnabled()) {
     exception_state.ThrowTypeError("Input type not supported");
-    return nullptr;
+    return std::nullopt;
+  }
+
+  AbortSignal* signal = options->getSignalOr(nullptr);
+  if (HandleAbortSignal(signal, script_state, exception_state)) {
+    return std::nullopt;
+  }
+
+  on_device_model::mojom::blink::ResponseConstraintPtr constraint;
+  if (!ParseConstraint(script_state, options, exception_state, constraint)) {
+    // ParseConstraint will throw an exception when false is returned.
+    return std::nullopt;
   }
 
   auto prompts = BuildPrompts(input, script_state, exception_state,
                               GetExecutionContext(), input_types_);
   if (!prompts.has_value()) {
-    return nullptr;
+    return std::nullopt;
   }
 
-  base::UmaHistogramEnumeration(AIMetrics::GetAIAPIUsageMetricName(
-                                    AIMetrics::AISessionType::kLanguageModel),
-                                AIMetrics::AIAPI::kSessionPromptStreaming);
-
   // TODO(crbug.com/411470034): Aggregate other input type sizes for UMA.
   if (input->IsString()) {
     base::UmaHistogramCounts1M(
         AIMetrics::GetAISessionRequestSizeMetricName(
             AIMetrics::AISessionType::kLanguageModel),
-        int(input->GetAsString().CharactersSizeInBytes()));
+        static_cast<int>(input->GetAsString().CharactersSizeInBytes()));
   }
 
   if (!language_model_remote_) {
     ThrowSessionDestroyedException(exception_state);
-    return nullptr;
+    return std::nullopt;
   }
 
-  AbortSignal* signal = options->getSignalOr(nullptr);
-  if (HandleAbortSignal(signal, script_state, exception_state)) {
-    return nullptr;
-  }
-
-  on_device_model::mojom::blink::ResponseConstraintPtr constraint;
-  if (!ParseConstraint(script_state, options, exception_state, constraint)) {
-    // ParseConstraint will throw an exception when false is returned.
-    return nullptr;
-  }
-
-  auto [readable_stream, pending_remote] =
-      CreateModelExecutionStreamingResponder(
-          script_state, signal, task_runner_,
-          AIMetrics::AISessionType::kLanguageModel,
-          WTF::BindOnce(&LanguageModel::OnResponseComplete,
-                        WrapWeakPersistent(this)),
-          WTF::BindRepeating(&LanguageModel::OnQuotaOverflow,
-                             WrapWeakPersistent(this)));
-
-  language_model_remote_->Prompt(std::move(prompts).value(),
-                                 std::move(constraint),
-                                 std::move(pending_remote));
-  return readable_stream;
+  return ValidateAndProcessPromptInputResult{
+      .processed_constraint = std::move(constraint),
+      .processed_prompts = std::move(prompts).value(),
+  };
 }
 
 ScriptPromise<LanguageModel> LanguageModel::clone(
diff --git a/third_party/blink/renderer/modules/ai/language_model.h b/third_party/blink/renderer/modules/ai/language_model.h
index 9ee2c4a..b3a7dae 100644
--- a/third_party/blink/renderer/modules/ai/language_model.h
+++ b/third_party/blink/renderer/modules/ai/language_model.h
@@ -94,6 +94,19 @@
       mojom::blink::ModelExecutionContextInfoPtr context_info);
   void OnQuotaOverflow();
 
+  struct ValidateAndProcessPromptInputResult {
+    on_device_model::mojom::blink::ResponseConstraintPtr processed_constraint;
+    WTF::Vector<mojom::blink::AILanguageModelPromptPtr> processed_prompts;
+  };
+
+  // Validates and processed prompt input and returns the processed results.
+  // Returns std::nullopt on failure.
+  std::optional<ValidateAndProcessPromptInputResult>
+  ValidateAndProcessPromptInput(ScriptState* script_state,
+                                const V8LanguageModelPromptInput* input,
+                                const LanguageModelPromptOptions* options,
+                                ExceptionState& exception_state);
+
   uint64_t input_usage_;
   uint64_t input_quota_ = 0;
   uint32_t top_k_ = 0;
diff --git a/third_party/blink/renderer/modules/webgpu/dawn_enum_conversions.cc b/third_party/blink/renderer/modules/webgpu/dawn_enum_conversions.cc
index b10cfed..20811c9e 100644
--- a/third_party/blink/renderer/modules/webgpu/dawn_enum_conversions.cc
+++ b/third_party/blink/renderer/modules/webgpu/dawn_enum_conversions.cc
@@ -695,6 +695,8 @@
       return wgpu::FeatureName::TextureCompressionETC2;
     case V8GPUFeatureName::Enum::kTextureCompressionAstc:
       return wgpu::FeatureName::TextureCompressionASTC;
+    case V8GPUFeatureName::Enum::kTextureCompressionAstcSliced3d:
+      return wgpu::FeatureName::TextureCompressionASTCSliced3D;
     case V8GPUFeatureName::Enum::kTimestampQuery:
       return wgpu::FeatureName::TimestampQuery;
     case V8GPUFeatureName::Enum::
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_adapter.cc b/third_party/blink/renderer/modules/webgpu/gpu_adapter.cc
index 83063f4..2f6c329 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_adapter.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu_adapter.cc
@@ -28,7 +28,6 @@
 
 namespace {
 
-// TODO(crbug.com/351564777): should be UNSAFE_BUFFER_USAGE
 GPUSupportedFeatures* MakeFeatureNameSet(wgpu::Adapter adapter) {
   GPUSupportedFeatures* features = MakeGarbageCollected<GPUSupportedFeatures>();
   DCHECK(features->FeatureNameSet().empty());
@@ -51,7 +50,6 @@
 
 }  // anonymous namespace
 
-// TODO(crbug.com/351564777): should be UNSAFE_BUFFER_USAGE
 GPUAdapter::GPUAdapter(
     GPU* gpu,
     wgpu::Adapter handle,
@@ -63,9 +61,8 @@
   wgpu::AdapterPropertiesSubgroups subgroupsProperties = {};
   *propertiesChain = &subgroupsProperties;
   propertiesChain = &(*propertiesChain)->nextInChain;
-  wgpu::AdapterPropertiesMemoryHeaps memoryHeapProperties = {};
   if (GetHandle().HasFeature(wgpu::FeatureName::AdapterPropertiesMemoryHeaps)) {
-    *propertiesChain = &memoryHeapProperties;
+    *propertiesChain = &memory_heaps_;
     propertiesChain = &(*propertiesChain)->nextInChain;
   }
   if (GetHandle().HasFeature(
@@ -108,10 +105,6 @@
   }
   description_ = String::FromUTF8(info.device);
   driver_ = String::FromUTF8(info.description);
-  for (size_t i = 0; i < memoryHeapProperties.heapCount; ++i) {
-    memory_heaps_.push_back(MakeGarbageCollected<GPUMemoryHeapInfo>(
-        UNSAFE_TODO(memoryHeapProperties.heapInfo[i])));
-  }
   if (supportsPropertiesD3D) {
     d3d_shader_model_ = d3dProperties.shaderModel;
   }
@@ -141,8 +134,13 @@
         is_fallback_adapter_, device_, description_, driver_,
         FromDawnEnum(backend_type_), FromDawnEnum(adapter_type_),
         d3d_shader_model_, vk_driver_version_, FromDawnEnum(power_preference_));
-    for (GPUMemoryHeapInfo* memory_heap : memory_heaps_) {
-      info->AppendMemoryHeapInfo(memory_heap);
+
+    // SAFETY: Required from caller
+    const auto memory_heaps_span =
+        UNSAFE_BUFFERS(base::span<const wgpu::MemoryHeapInfo>(
+            memory_heaps_.heapInfo, memory_heaps_.heapCount));
+    for (const auto& m : memory_heaps_span) {
+      info->AppendMemoryHeapInfo(MakeGarbageCollected<GPUMemoryHeapInfo>(m));
     }
   } else {
     info = MakeGarbageCollected<GPUAdapterInfo>(
@@ -354,7 +352,6 @@
   visitor->Trace(features_);
   visitor->Trace(limits_);
   visitor->Trace(info_);
-  visitor->Trace(memory_heaps_);
   ScriptWrappable::Trace(visitor);
 }
 
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_adapter.h b/third_party/blink/renderer/modules/webgpu/gpu_adapter.h
index e60b30c..83b5aed 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_adapter.h
+++ b/third_party/blink/renderer/modules/webgpu/gpu_adapter.h
@@ -23,7 +23,6 @@
 class GPURequestAdapterOptions;
 class GPUSupportedFeatures;
 class GPUSupportedLimits;
-class GPUMemoryHeapInfo;
 
 class GPUAdapter final : public ScriptWrappable, DawnObject<wgpu::Adapter> {
   DEFINE_WRAPPERTYPEINFO();
@@ -90,10 +89,10 @@
   uint32_t subgroup_min_size_;
   uint32_t subgroup_max_size_;
   String driver_;
-  HeapVector<Member<GPUMemoryHeapInfo>> memory_heaps_;
   std::optional<uint32_t> d3d_shader_model_;
   std::optional<uint32_t> vk_driver_version_;
   wgpu::PowerPreference power_preference_;
+  wgpu::AdapterPropertiesMemoryHeaps memory_heaps_ = {};
   wgpu::AdapterPropertiesSubgroupMatrixConfigs subgroup_matrix_configs_ = {};
 
   static constexpr int kMaxAllowedConsoleWarnings = 50;
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_supported_features.cc b/third_party/blink/renderer/modules/webgpu/gpu_supported_features.cc
index 91dde2a..c6dbae1 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_supported_features.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu_supported_features.cc
@@ -24,6 +24,8 @@
       return V8GPUFeatureName::Enum::kTextureCompressionEtc2;
     case wgpu::FeatureName::TextureCompressionASTC:
       return V8GPUFeatureName::Enum::kTextureCompressionAstc;
+    case wgpu::FeatureName::TextureCompressionASTCSliced3D:
+      return V8GPUFeatureName::Enum::kTextureCompressionAstcSliced3d;
     case wgpu::FeatureName::IndirectFirstInstance:
       return V8GPUFeatureName::Enum::kIndirectFirstInstance;
     case wgpu::FeatureName::DepthClipControl:
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_supported_features.idl b/third_party/blink/renderer/modules/webgpu/gpu_supported_features.idl
index 7e6f09cf..e558141 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_supported_features.idl
+++ b/third_party/blink/renderer/modules/webgpu/gpu_supported_features.idl
@@ -11,6 +11,7 @@
     "texture-compression-bc-sliced-3d",
     "texture-compression-etc2",
     "texture-compression-astc",
+    "texture-compression-astc-sliced-3d",
     "timestamp-query",
     "indirect-first-instance",
     "shader-f16",
diff --git a/third_party/blink/renderer/modules/xr/xr_cube_map.cc b/third_party/blink/renderer/modules/xr/xr_cube_map.cc
index c323a2e31..a1f74ce 100644
--- a/third_party/blink/renderer/modules/xr/xr_cube_map.cc
+++ b/third_party/blink/renderer/modules/xr/xr_cube_map.cc
@@ -33,19 +33,20 @@
   return static_cast<uint8_t>(255.0f * cs + 0.5f);
 }
 
-void Rgba16fToSrgba8(base::span<const device::RgbaTupleF16> input,
+void Rgba16fToSrgba8(base::span<const uint16_t> input,
                      base::span<uint8_t> output) {
-  DCHECK_EQ(input.size() * 4, output.size());
+  CHECK_EQ(input.size(), output.size());
+  CHECK_EQ(input.size() % 4, 0u);
 
   for (size_t i = 0; i < input.size(); ++i) {
-    const auto& in = input[i];
-    auto [out_pixel, rest] = output.split_at<4>();
-    out_pixel[0] = LinearToSrgb(HalfFloatToFloat(in.red()));
-    out_pixel[1] = LinearToSrgb(HalfFloatToFloat(in.green()));
-    out_pixel[2] = LinearToSrgb(HalfFloatToFloat(in.blue()));
-    // We won't support non-opaque alpha to make the conversion a bit faster.
-    out_pixel[3] = 255;
-    output = rest;
+    // Every fourth input element is the alpha channel, which should remain in
+    // standard space.
+    if ((i + 1) % 4 == 0) {
+      // We won't support non-opaque alpha to make the conversion a bit faster.
+      output[i] = 255;
+    } else {
+      output[i] = LinearToSrgb(HalfFloatToFloat(input[i]));
+    }
   }
 }
 
@@ -59,17 +60,20 @@
   static_assert(kNumComponentsPerPixel == 4,
                 "XRCubeMaps are expected to be in the RGBA16F format");
 
-  // Cube map sides must all be a power-of-two image
+  // Cube map sides must all be a power-of-two image. Note that we can skip the
+  // division by |kNumComponentsPerPixel| at this time, since it is *also* a
+  // power-of-two.
   bool valid = std::has_single_bit(cube_map.width_and_height);
-  const size_t expected_size =
-      cube_map.width_and_height * cube_map.width_and_height;
+  const size_t expected_size = cube_map.width_and_height *
+                               cube_map.width_and_height *
+                               kNumComponentsPerPixel;
   valid &= cube_map.positive_x.size() == expected_size;
   valid &= cube_map.negative_x.size() == expected_size;
   valid &= cube_map.positive_y.size() == expected_size;
   valid &= cube_map.negative_y.size() == expected_size;
   valid &= cube_map.positive_z.size() == expected_size;
   valid &= cube_map.negative_z.size() == expected_size;
-  DCHECK(valid);
+  CHECK(valid);
 
   width_and_height_ = cube_map.width_and_height;
   positive_x_ = cube_map.positive_x;
@@ -100,7 +104,7 @@
   gl->TexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
   gl->TexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 
-  const std::array<base::span<const device::RgbaTupleF16>, 6> cubemap_images = {
+  const std::array<base::span<const uint16_t>, 6> cubemap_images = {
       positive_x_, negative_x_, positive_y_,
       negative_y_, positive_z_, negative_z_,
   };
diff --git a/third_party/blink/renderer/modules/xr/xr_cube_map.h b/third_party/blink/renderer/modules/xr/xr_cube_map.h
index 01ef17b4..fd38578f 100644
--- a/third_party/blink/renderer/modules/xr/xr_cube_map.h
+++ b/third_party/blink/renderer/modules/xr/xr_cube_map.h
@@ -6,7 +6,6 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_CUBE_MAP_H_
 
 #include "base/types/pass_key.h"
-#include "device/vr/public/mojom/rgba_tuple_f16.h"
 #include "device/vr/public/mojom/vr_service.mojom-blink-forward.h"
 #include "third_party/blink/renderer/core/typed_arrays/dom_typed_array.h"
 
@@ -30,12 +29,12 @@
 
  private:
   uint32_t width_and_height_ = 0;
-  WTF::Vector<device::RgbaTupleF16> positive_x_;
-  WTF::Vector<device::RgbaTupleF16> negative_x_;
-  WTF::Vector<device::RgbaTupleF16> positive_y_;
-  WTF::Vector<device::RgbaTupleF16> negative_y_;
-  WTF::Vector<device::RgbaTupleF16> positive_z_;
-  WTF::Vector<device::RgbaTupleF16> negative_z_;
+  WTF::Vector<uint16_t> positive_x_;
+  WTF::Vector<uint16_t> negative_x_;
+  WTF::Vector<uint16_t> positive_y_;
+  WTF::Vector<uint16_t> negative_y_;
+  WTF::Vector<uint16_t> positive_z_;
+  WTF::Vector<uint16_t> negative_z_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
index 22468747..4488e31 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
@@ -399,6 +399,7 @@
     RasterInterface()->WritePixels(client_si->mailbox(), x, y,
                                    client_si->GetTextureTarget(),
                                    SkPixmap(orig_info, pixels, row_bytes));
+    resource()->GetSyncToken();
 
     // If the overdraw optimization kicked in, we need to indicate that the
     // pixels do not need to be cleared, otherwise the subsequent
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 7d2ae929..4d024f8 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -1067,7 +1067,7 @@
     {
       // Enables alignment properties for block and inline out-of-flow elements.
       name: "CSSAlignBlockAndInlineOutOfFlows",
-      status: "test",
+      status: "stable",
     },
     {
       // Remember the scroll offset of the default anchor when initially
@@ -2541,7 +2541,7 @@
     },
     {
       name: "IgnoreLetterSpacingInCursiveScripts",
-      status: "experimental",
+      status: "stable",
     },
     {
       name: "ImageDataPixelFormat",
@@ -2667,7 +2667,7 @@
     },
     {
       name: "IntegrityPolicyScript",
-      status: "experimental",
+      status: "stable",
       base_feature: "none",
       public: true,
     },
@@ -3815,12 +3815,6 @@
       name: "RemoveDataUrlInSvgUse",
       status: "stable",
     },
-    // Remove a node if it's selected fully even though it has children. See
-    // https://crbug.com/331074432
-    {
-      name: "RemoveNodeHavingChildrenIfFullySelected",
-      status: "stable",
-    },
     {
       // crbug.com/41047725
       name: "RemovePlaceholderBRForDisplayInline",
@@ -4169,6 +4163,14 @@
       base_feature: "none",
     },
     {
+      // Implements improved accessibility mappings for having an <input>
+      // inside of a <select> by reparenting the child <input> in the
+      // accessibility tree to be a sibling of the menu list popup.
+      name: "SelectAccessibilityReparentInput",
+      status: "test",
+      depends_on: ["CustomizableSelect"],
+    },
+    {
       // SelectAudioOutput API
       // https://chromestatus.com/feature/5164535504437248
       name: "SelectAudioOutput",
@@ -4920,7 +4922,6 @@
       // This flag uses the original DOM text for input when the password
       // echo is enabled to create the offset_map.
       name: "UseOriginalDomOffsetsForOffsetMap",
-      status: "stable",
     },
     {
       name: "UsePositionForPointInFlexibleBoxWithSingleChildElement",
@@ -5000,11 +5001,7 @@
     },
     {
       name: "ViewportSegments",
-      status: "experimental",
-      public: true,
-      base_feature_status: "enabled",
-      copied_from_base_feature_if: "overridden",
-      origin_trial_feature_name: "FoldableAPIs",
+      status: "stable",
     },
     {
       name: "ViewTransitionDisableSnapBrowserControlsOnHidden",
diff --git a/third_party/blink/renderer/platform/wtf/text/parsing_utilities.h b/third_party/blink/renderer/platform/wtf/text/parsing_utilities.h
index 2330aa9..534c42378 100644
--- a/third_party/blink/renderer/platform/wtf/text/parsing_utilities.h
+++ b/third_party/blink/renderer/platform/wtf/text/parsing_utilities.h
@@ -28,16 +28,12 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF 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
-
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_PARSING_UTILITIES_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_PARSING_UTILITIES_H_
 
 #include <string_view>
 
+#include "base/compiler_specific.h"
 #include "base/containers/span.h"
 
 namespace WTF {
@@ -53,15 +49,6 @@
   return false;
 }
 
-template <typename CharType, bool characterPredicate(CharType)>
-bool SkipExactly(const CharType*& position, const CharType* end) {
-  if (position < end && characterPredicate(*position)) {
-    ++position;
-    return true;
-  }
-  return false;
-}
-
 template <typename CharType, bool predicate(CharType)>
 bool SkipExactly(base::span<const CharType> chars, size_t& position) {
   if (position < chars.size() && predicate(chars[position])) {
@@ -71,16 +58,17 @@
   return false;
 }
 
+// Use a span version instead.
 template <typename CharType>
-bool SkipToken(const CharType*& position,
-               const CharType* end,
-               const char* token) {
+UNSAFE_BUFFER_USAGE bool SkipToken(const CharType*& position,
+                                   const CharType* end,
+                                   const char* token) {
   const CharType* current = position;
   while (current < end && *token) {
     if (*current != *token)
       return false;
-    ++current;
-    ++token;
+    UNSAFE_TODO(++current);
+    UNSAFE_TODO(++token);
   }
   if (*token)
     return false;
@@ -102,18 +90,13 @@
   return true;
 }
 
+// Use a span version instead.
 template <typename CharType>
-void SkipUntil(const CharType*& position,
-               const CharType* end,
-               CharType delimiter) {
+UNSAFE_BUFFER_USAGE void SkipUntil(const CharType*& position,
+                                   const CharType* end,
+                                   CharType delimiter) {
   while (position < end && *position != delimiter)
-    ++position;
-}
-
-template <typename CharType, bool characterPredicate(CharType)>
-void SkipUntil(const CharType*& position, const CharType* end) {
-  while (position < end && !characterPredicate(*position))
-    ++position;
+    UNSAFE_TODO(++position);
 }
 
 template <typename CharType, bool predicate(CharType)>
@@ -134,12 +117,6 @@
   return position;
 }
 
-template <typename CharType, bool characterPredicate(CharType)>
-void ReverseSkipWhile(const CharType*& position, const CharType* start) {
-  while (position >= start && characterPredicate(*position))
-    --position;
-}
-
 template <typename CharType, bool predicate(CharType)>
 [[nodiscard]] size_t ReverseSkipWhile(base::span<const CharType> chars,
                                       size_t position,
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 80c0a73f..aab7fff 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -2641,6 +2641,7 @@
 crbug.com/413411328 external/wpt/css/css-values/urls/referrer-policy/unsafe-url/url-image-referrerpolicy-same-origin.sub.html [ Failure ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/416129743 [ Mac15-arm64 ] external/wpt/css/css-flexbox/overflow-auto-005.html [ Failure ]
 crbug.com/415669416 [ Mac15 ] external/wpt/css/css-position/sticky/position-sticky-fractional-offset.html [ Failure ]
 crbug.com/415738357 [ Linux ] external/wpt/webdriver/tests/bidi/emulation/set_geolocation_override/error.py [ Failure ]
 crbug.com/415738357 [ Linux ] external/wpt/webdriver/tests/bidi/emulation/set_geolocation_override/invalid.py [ Failure ]
@@ -2833,7 +2834,7 @@
 crbug.com/392927987 [ Mac14 ] external/wpt/html/cross-origin-opener-policy/iframe-popup-same-origin-to-same-origin.https.html?5-6 [ Crash ]
 crbug.com/392927987 [ Mac15 ] external/wpt/html/cross-origin-opener-policy/iframe-popup-same-origin-to-same-origin.https.html?5-6 [ Crash ]
 crbug.com/392180059 [ Linux ] external/wpt/digital-credentials/disabled-by-permissions-policy.https.sub.html [ Failure Timeout ]
-crbug.com/391728750 [ Win11 ] external/wpt/ai/translator/translator-translate.tentative.https.window.html [ Skip Timeout ]
+crbug.com/391728750 [ Win11 ] external/wpt/ai/translator/translator.optional.https.window.html [ Skip Timeout ]
 crbug.com/390467419 [ Mac15-arm64 ] external/wpt/geolocation/enabled-by-permission-policy-attribute-redirect-on-load.https.sub.html [ Timeout ]
 crbug.com/390467419 [ Mac15 ] external/wpt/geolocation/enabled-by-permission-policy-attribute-redirect-on-load.https.sub.html [ Failure Timeout ]
 crbug.com/390467419 [ Mac14-arm64 ] external/wpt/geolocation/enabled-by-permission-policy-attribute-redirect-on-load.https.sub.html [ Timeout ]
@@ -6299,6 +6300,8 @@
 crbug.com/40146374 virtual/select-parser-relaxation-opt-out/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-picker-interactive-element-focus.tentative.html [ Failure ]
 crbug.com/40146374 virtual/select-parser-relaxation-opt-out/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-parsing.tentative.html [ Failure ]
 
+crbug.com/415205686 editing/input/selectall-in-input-with-text-security.html [ Crash Failure ]
+
 # Flaky on headless_shell_wpt_tests
 crbug.com/40146374 external/wpt/html/semantics/forms/the-select-element/customizable-select/select-option-images.tentative.html [ Failure Pass ]
 
@@ -9077,7 +9080,7 @@
 crbug.com/383946052 [ Linux ] external/wpt/screen-capture/tentative/getdisplaymedia-captured-surface-resolution.https.html [ Failure ]
 
 # Also failing on Linux ASAN
-crbug.com/391728750 [ Linux ] external/wpt/ai/translator/translator-translate.tentative.https.window.html [ Failure Timeout ]
+crbug.com/391728750 [ Linux ] external/wpt/ai/translator/translator.optional.https.window.html [ Failure Timeout ]
 
 #Gardener 2025-02-12
 crbug.com/392640451 [ Win11 ] editing/pasteboard/4947130.html [ Failure Pass ]
@@ -9214,3 +9217,6 @@
 crbug.com/415747471 [ Mac ] virtual/text-antialias/font-fallback.html [ Failure ]
 crbug.com/415747471 [ Mac ] virtual/text-antialias/complex-text-opacity.html [ Failure ]
 crbug.com/415747471 [ Mac ] svg/text/bidi-textlength.html [ Failure ]
+
+# Gardener 2025-05-07
+crbug.com/416123241 [ Linux ] fast/forms/select/customizable-select/disallowed-select-descendants-console-message.html [ Failure Pass ]
diff --git a/third_party/blink/web_tests/editing/pasteboard/copy-image-with-alt-text-expected.txt b/third_party/blink/web_tests/editing/pasteboard/copy-image-with-alt-text-expected.txt
index 5a0017a..d9af5d8 100644
--- a/third_party/blink/web_tests/editing/pasteboard/copy-image-with-alt-text-expected.txt
+++ b/third_party/blink/web_tests/editing/pasteboard/copy-image-with-alt-text-expected.txt
@@ -3,8 +3,8 @@
 Dump of markup 1:
 |   <shadow:root>
 |     <div>
-|       "Here is an emoticon [], some more text [], an empty alt tag [], no alt tag [] and two consecutive images [].
-"
+|       "Here is an emoticon [], some more text [], an empty alt tag [], no alt tag [] and two consecutive images []."
+|       <br>
 |       <br>
 |         id="textarea-placeholder-break"
 
@@ -18,8 +18,8 @@
 Dump of markup 3:
 |   <shadow:root>
 |     <div>
-|       "Here is an emoticon [:)], some more text [sample text], an empty alt tag [], no alt tag [] and two consecutive images [firstsecond].
-"
+|       "Here is an emoticon [:)], some more text [sample text], an empty alt tag [], no alt tag [] and two consecutive images [firstsecond]."
+|       <br>
 |       <br>
 |         id="textarea-placeholder-break"
 
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
index 8f74d0c..981d3b34 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -5870,6 +5870,13 @@
        {}
       ]
      ],
+     "root-replace-crash.html": [
+      "165eec41b6bc56db131cc7fc19f23701bc291b8c",
+      [
+       null,
+       {}
+      ]
+     ],
      "scoped": {
       "crashtests": {
        "shadow-dom.html": [
@@ -132348,6 +132355,19 @@
        {}
       ]
      ],
+     "font-variant-emoji-005.html": [
+      "0f5c2d7a98a615323b1adc0babb479fdb738537d",
+      [
+       null,
+       [
+        [
+         "/css/css-fonts/font-variant-emoji-005-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
      "font-variant-emoji-1.html": [
       "53cf539edd8b61a7f08f8b95ae83f4f7a5179308",
       [
@@ -134507,6 +134527,71 @@
         ],
         {}
        ]
+      ],
+      "grid-gap-decorations-029.html": [
+       "6da75548d94b5e17e1ad7ba1011662c42b818d83",
+       [
+        null,
+        [
+         [
+          "/css/css-gaps/grid/grid-gap-decorations-029-ref.html",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "grid-gap-decorations-030.html": [
+       "ef4507ef92638eeff660e67e2024bbce323c1aa8",
+       [
+        null,
+        [
+         [
+          "/css/css-gaps/grid/grid-gap-decorations-030-ref.html",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "grid-gap-decorations-031.html": [
+       "530b661bfe32c5af7687954f3f4b2cbdfa61380a",
+       [
+        null,
+        [
+         [
+          "/css/css-gaps/grid/grid-gap-decorations-031-ref.html",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "grid-gap-decorations-032.html": [
+       "ac2d38fdda0ec6a1f4a3b7b091d03c896e3e5e47",
+       [
+        null,
+        [
+         [
+          "/css/css-gaps/grid/grid-gap-decorations-032-ref.html",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "grid-gap-decorations-033.html": [
+       "47ef35cc3688b2fcd2329558b1d0a27880983b38",
+       [
+        null,
+        [
+         [
+          "/css/css-gaps/grid/grid-gap-decorations-033-ref.html",
+          "=="
+         ]
+        ],
+        {}
+       ]
       ]
      },
      "multicol": {
@@ -224560,6 +224645,35 @@
        {}
       ]
      ],
+     "css-scale-of-clip-path.html": [
+      "dda72c0d139979213741174da8c9572f1237bcc3",
+      [
+       null,
+       [
+        [
+         "/css/css-transforms/css-scale-of-clip-path-ref.html",
+         "=="
+        ]
+       ],
+       {
+        "fuzzy": [
+         [
+          null,
+          [
+           [
+            0,
+            40
+           ],
+           [
+            0,
+            3000
+           ]
+          ]
+         ]
+        ]
+       }
+      ]
+     ],
      "css-skew-001.html": [
       "f209834121bb00ff3542029d44ba1ce878b0dd77",
       [
@@ -319840,7 +319954,13 @@
      "OWNERS": [
       "ada9e7044f494a9db911e640fd69b83bdb5e854a",
       []
-     ]
+     ],
+     "resources": {
+      "util.js": [
+       "ad06086a123bccbe2c852dffd1a36ffde0613ada",
+       []
+      ]
+     }
     }
    },
    "ambient-light": {
@@ -342823,6 +342943,10 @@
       "633a94c4e51f32328bc68eeb74837aada6f4aff0",
       []
      ],
+     "font-variant-emoji-005-ref.html": [
+      "d4275b5c951c05ea270f1a2a474ff4ccaf9afc4a",
+      []
+     ],
      "font-variant-emoji-1-notref.html": [
       "bbf3654ccfcdd415fe5a28468e3329ba94ce63da",
       []
@@ -349941,6 +350065,26 @@
       "grid-gap-decorations-027-ref.html": [
        "7e0d9cbcd6ff64b17bc8fe57616a37ba29a9fd8e",
        []
+      ],
+      "grid-gap-decorations-029-ref.html": [
+       "35450e07ae5579707c94b4a0a58f29eb83d7f7fa",
+       []
+      ],
+      "grid-gap-decorations-030-ref.html": [
+       "d4953ae2ddda701324d350ccf25a113b8e67fc37",
+       []
+      ],
+      "grid-gap-decorations-031-ref.html": [
+       "0615305bc9f8de791f1c2061ad6d90039932b157",
+       []
+      ],
+      "grid-gap-decorations-032-ref.html": [
+       "f90e3dd68132078e2557f33b7e32605d278cd0eb",
+       []
+      ],
+      "grid-gap-decorations-033-ref.html": [
+       "e26143a6b93e9fdd53d4055234d89e40d98dc2af",
+       []
       ]
      },
      "multicol": {
@@ -368831,6 +368975,10 @@
       "54940566cd107ba443d3183dccecb505a811d14e",
       []
      ],
+     "css-scale-of-clip-path-ref.html": [
+      "d6fa480b0408ffc9c3687515e25a30ddc68f199f",
+      []
+     ],
      "css-skew-001-ref.html": [
       "70c256ebec653128a2d91555e817b54880666a35",
       []
@@ -380750,7 +380898,7 @@
       []
      ],
      "testdriver.md": [
-      "67757af63f2ed6af4026577dd35000c183509d44",
+      "e0fae529875ee89d5e8a2457cdab35e1866f663b",
       []
      ],
      "testharness-api.md": [
@@ -381429,6 +381577,10 @@
      }
     },
     "observable": {
+     "WEB_FEATURES.yml": [
+      "3e872791165e41402949da13fedd7a8780aaf660",
+      []
+     ],
      "tentative": {
       "observable-from.any-expected.txt": [
        "90faca59d59d4199fa430ee25c80f061257f089b",
@@ -387354,6 +387506,30 @@
       ]
      }
     },
+    "local-network-access": {
+     "META.yml": [
+      "4c5c6983ed0467f3ebca3182a6b5d095270f85d2",
+      []
+     ],
+     "README.md": [
+      "95066cdcd0ba6f2baed3cd02d67610420367b6df",
+      []
+     ],
+     "resources": {
+      "fetch-private.html": [
+       "ede16badf338dc50defad0251f83f8b02f1d3fec",
+       []
+      ],
+      "support.sub.js": [
+       "299f004ecc362e29202b80b68d8e47b1300b8604",
+       []
+      ],
+      "target.py": [
+       "eabcdd47517c8938d12cd08a0d66f0db2e518456",
+       []
+      ]
+     }
+    },
     "metadata": {
      "META.yml": [
       "85f0a7d2ee12616aa7a5698b346954829d8a8bb0",
@@ -419584,7 +419760,7 @@
       []
      ],
      "report-helper.js": [
-      "5b5438903de11c346ac3901a9de81a96e4446386",
+      "216da22eae8c0ccde223c70a110a7a892676c14f",
       []
      ],
      "report.py": [
@@ -420397,7 +420573,7 @@
      []
     ],
     "testdriver.js": [
-     "6e8410b7ea46064438c8d276274b6fbb689f81d2",
+     "992b9e3ab2ce0b63b94be2a5aa671b321f65e334",
      []
     ],
     "testdriver.js.headers": [
@@ -432997,11 +433173,11 @@
        ],
        "set_geolocation_override": {
         "__init__.py": [
-         "7861734b5087e3486ef0a03c2b8cc8364682c5fd",
+         "5e1fd1aa8637529a20b48356fe20e4f970b8428c",
          []
         ],
         "conftest.py": [
-         "a90895c74ed90c04b5f181af22c3bddec9ac132d",
+         "5fb9451f0a23821bbd7d25ac59efeb928f84b5ac",
          []
         ]
        }
@@ -466835,10 +467011,10 @@
      ]
     },
     "translator": {
-     "translator-bad-input.tentative.https.window.js": [
-      "53a184bfd196f6006e7df9c908518ba47deacda6",
+     "translator-bad-input.https.window.js": [
+      "db8905a61f660f8dc0aa7666c044794d22683799",
       [
-       "ai/translator/translator-bad-input.tentative.https.window.html",
+       "ai/translator/translator-bad-input.https.window.html",
        {
         "script_metadata": [
          [
@@ -466858,15 +467034,15 @@
        }
       ]
      ],
-     "translator-translate.tentative.https.window.js": [
-      "a8aad5e03e1f46db43a47966d66f7aecc526b255",
+     "translator.optional.https.window.js": [
+      "96eca09d28bd61ccc4d31b746216f1f252d0bf3b",
       [
-       "ai/translator/translator-translate.tentative.https.window.html",
+       "ai/translator/translator.optional.https.window.html",
        {
         "script_metadata": [
          [
           "title",
-          "Translate from English to Japanese"
+          "Translator Translate"
          ],
          [
           "global",
@@ -466887,6 +467063,10 @@
          [
           "script",
           "/resources/testdriver.js"
+         ],
+         [
+          "script",
+          "resources/util.js"
          ]
         ],
         "timeout": "long"
@@ -528768,6 +528948,13 @@
         {}
        ]
       ],
+      "sibling-index-keyframe-value-dynamic.html": [
+       "286e0d3d3e2eecdd091df74c8c47f738cb700dd1",
+       [
+        null,
+        {}
+       ]
+      ],
       "tree-scoped-sibling-function.html": [
        "979466bb7cf3d5e5c4e9a2681f02f969858953e6",
        [
@@ -532276,7 +532463,7 @@
       ]
      ],
      "getBoundingClientRect-newline.html": [
-      "40e29181d6fed001b641b0b0050cf21d6f3c589c",
+      "ce82b7237d003d79fe4333e5a67aa74b004bb11c",
       [
        null,
        {}
@@ -577968,6 +578155,17 @@
       ]
      ]
     },
+    "local-network-access": {
+     "fetch.tentative.https.html": [
+      "a01bad6a420d61e13188fda314232c6a31332623",
+      [
+       null,
+       {
+        "testdriver": true
+       }
+      ]
+     ]
+    },
     "metadata": {
      "audio-worklet.https.html": [
       "3b768ef0b5ddd2d13361629afd011e6987cb38d0",
@@ -648498,6 +648696,15 @@
           }
          ]
         ],
+        "select-input-keyboard-behavior.tentative.html": [
+         "bf3fbab9d18a4747f903d082a8c11d6ebad737aa",
+         [
+          null,
+          {
+           "testdriver": true
+          }
+         ]
+        ],
         "select-inside-top-layer.tentative.html": [
          "6d70f3aaf8d3dc23b2f5fb784c399991913dc938",
          [
@@ -663133,7 +663340,7 @@
       },
       "emulation": {
        "set_geolocation_override.https.html": [
-        "5cbcf546423c772a98fd3725531754c366ab7b91",
+        "e339f39dcd89aa311e91c744669bf2cd9aadc103",
         [
          null,
          {
@@ -711237,7 +711444,7 @@
      ]
     ],
     "innertext.tentative.html": [
-     "d40b6047924834ef09137fa975cbd2d142999bcb",
+     "7716488f25a08ea62635fceec679d97650d1f383",
      [
       null,
       {
@@ -711533,7 +711740,7 @@
      ]
     ],
     "text-lcp-followed-by-anim-image-softnav-lcp.tentative.html": [
-     "0615b513e61319d61bcbed3540aa1a5d47289cb6",
+     "b34a6e81a58333c61ccedb4e25551feba4893736",
      [
       null,
       {
@@ -711560,7 +711767,7 @@
      ]
     ],
     "visited-link.tentative.html": [
-     "0bb149f00e91fc2f0033a6f763543967ef542ab2",
+     "0bb31aaac1540b3d2b9f6a3157bb43d96e430a58",
      [
       null,
       {
@@ -727920,6 +728127,34 @@
       {}
      ]
     ],
+    "tentative": {
+     "integrity-policy": {
+      "parsing.https.html": [
+       "205854419a7d58871e39a5cfb3a86ccd38cd5492",
+       [
+        "subresource-integrity/tentative/integrity-policy/parsing.https.html?type=enforce",
+        {
+         "timeout": "long"
+        }
+       ],
+       [
+        "subresource-integrity/tentative/integrity-policy/parsing.https.html?type=report",
+        {
+         "timeout": "long"
+        }
+       ]
+      ],
+      "script.https.html": [
+       "783374db920a24ae32a3492d8cfe2edc5d8eb5d3",
+       [
+        null,
+        {
+         "timeout": "long"
+        }
+       ]
+      ]
+     }
+    },
     "unencoded-digest": {
      "tentative": {
       "fetch.any.js": [
@@ -834227,21 +834462,21 @@
       "emulation": {
        "set_geolocation_override": {
         "contexts.py": [
-         "8a0e43475da52609a362bf5f3e0853ceb2a9725f",
+         "068bb804e73bd06e2d901363280792d479cfb38f",
          [
           null,
           {}
          ]
         ],
         "coordinates.py": [
-         "b23354e9e42ef1bdd9d6684d11b782ce15f4eb8c",
+         "ea4fe643c3ecfc6e3609172706b51df17456e2e7",
          [
           null,
           {}
          ]
         ],
         "error.py": [
-         "ec905e384822b6af73e1ca68f21c93d5b6b0d6ed",
+         "27c23dc7811458feb7cd43c8a286c83809d9d2d0",
          [
           null,
           {}
@@ -834255,7 +834490,7 @@
          ]
         ],
         "user_contexts.py": [
-         "008dee5698de43a3371c1f7d697bb69014cf1b65",
+         "b3038bb59587b661e75945c5b8eeb4e943c72d9e",
          [
           null,
           {}
diff --git a/third_party/blink/web_tests/external/wpt/ai/translator/resources/util.js b/third_party/blink/web_tests/external/wpt/ai/translator/resources/util.js
new file mode 100644
index 0000000..ad06086
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/ai/translator/resources/util.js
@@ -0,0 +1,4 @@
+async function createTranslator(options) {
+  await test_driver.bless();
+  return await Translator.create(options);
+}
diff --git a/third_party/blink/web_tests/external/wpt/ai/translator/translator-bad-input.tentative.https.window.js b/third_party/blink/web_tests/external/wpt/ai/translator/translator-bad-input.https.window.js
similarity index 89%
rename from third_party/blink/web_tests/external/wpt/ai/translator/translator-bad-input.tentative.https.window.js
rename to third_party/blink/web_tests/external/wpt/ai/translator/translator-bad-input.https.window.js
index 53a184bf..db8905a6 100644
--- a/third_party/blink/web_tests/external/wpt/ai/translator/translator-bad-input.tentative.https.window.js
+++ b/third_party/blink/web_tests/external/wpt/ai/translator/translator-bad-input.https.window.js
@@ -8,8 +8,7 @@
 'use strict';
 
 promise_test(async t => {
-  await promise_rejects_js(
-      t, TypeError, Translator.create(/*empty options*/));
+  await promise_rejects_js(t, TypeError, Translator.create(/*empty options*/));
 }, 'Translator.create rejects with TypeError if no options are passed.');
 
 promise_test(async t => {
diff --git a/third_party/blink/web_tests/external/wpt/ai/translator/translator-translate.tentative.https.window.js b/third_party/blink/web_tests/external/wpt/ai/translator/translator.optional.https.window.js
similarity index 95%
rename from third_party/blink/web_tests/external/wpt/ai/translator/translator-translate.tentative.https.window.js
rename to third_party/blink/web_tests/external/wpt/ai/translator/translator.optional.https.window.js
index a8aad5e..96eca09d 100644
--- a/third_party/blink/web_tests/external/wpt/ai/translator/translator-translate.tentative.https.window.js
+++ b/third_party/blink/web_tests/external/wpt/ai/translator/translator.optional.https.window.js
@@ -1,21 +1,16 @@
-// META: title=Translate from English to Japanese
+// META: title=Translator Translate
 // META: global=window
 // META: timeout=long
 // META: script=../resources/util.js
 // META: script=../resources/language_codes.js
 // META: script=/resources/testdriver.js
+// META: script=resources/util.js
 //
 // Setting `timeout=long` as this test may require downloading the translation
 // library and the language models.
 
 'use strict';
 
-async function createTranslator(options) {
-  return await test_driver.bless('Create translator', async () => {
-    return await Translator.create(options);
-  });
-}
-
 promise_test(async t => {
   const languagePair = {sourceLanguage: 'en', targetLanguage: 'ja'};
 
@@ -141,7 +136,7 @@
   for (let i = 0; i < translatableStrings.length; i++) {
     assert_not_equals(translatedTranslatableString[i], translatableStrings[i]);
   }
-}, 'Translator.translate() echos non-translatable content');
+}, 'Translator.translate() echoes non-translatable content');
 
 promise_test(async t => {
   const translator =
diff --git a/third_party/blink/web_tests/external/wpt/css/css-fonts/font-variant-emoji-005-ref.html b/third_party/blink/web_tests/external/wpt/css/css-fonts/font-variant-emoji-005-ref.html
new file mode 100644
index 0000000..d4275b5c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-fonts/font-variant-emoji-005-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<meta charset="UTF-8" />
+<title>CSS Fonts reference</title>
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+
+<style>
+body { background: white; }
+p { font: 16px/3 serif; }
+span { color: white; font: 25px Ahem; }
+span span { color: initial; font-variant-emoji: emoji; }
+</style>
+
+<h4>Only lines 3 and 4 should show a keycap numeral:</h4>
+
+<p>1. text: <span>X</span></p>
+
+<p>2. unicode: <span>X</span></p>
+
+<p>3. emoji: <span>X<span>3&#xfe0f;&#x20e3;</span>X</span></p>
+
+<p>4. with VS16: <span>X<span>4&#xfe0f;&#x20e3;</span>X</span></p>
+
+<p>5. emoji, with VS15: <span>X</span></p>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-fonts/font-variant-emoji-005.html b/third_party/blink/web_tests/external/wpt/css/css-fonts/font-variant-emoji-005.html
new file mode 100644
index 0000000..0f5c2d7
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-fonts/font-variant-emoji-005.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="UTF-8" />
+<title>CSS Fonts: font-variant-emoji web font test</title>
+<link rel="help" href="https://www.w3.org/TR/css-fonts-4/#font-variant-emoji-prop" />
+<link rel="help" href="https://www.unicode.org/reports/tr51/tr51-25.html#Emoji_Properties_and_Data_Files" />
+<link rel="match" href="font-variant-emoji-005-ref.html"/>
+<meta name="assert" content="Digit-keycap sequences render as emoji if required, in preference to using the named font"/>
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+
+<style>
+body { background: white; }
+p { font: 16px/3 serif; }
+/* A color-emoji glyph in the span will be visible despite the white color;
+ * if color-emoji presentation is not used, the span will be invisible.
+ */
+span { color: white; font: 25px Ahem; }
+</style>
+
+<h4>Only lines 3 and 4 should show a keycap numeral:</h4>
+
+<p>1. text: <span style="font-variant-emoji: text">X1&#x20e3;X</span></p>
+
+<p>2. unicode: <span style="font-variant-emoji: unicode">X2&#x20e3;X</span></p>
+
+<p>3. emoji: <span style="font-variant-emoji: emoji">X3&#x20e3;X</span></p>
+
+<p>4. with VS16: <span style="font-variant-emoji: normal">X4&#xfe0f;&#x20e3;X</span></p>
+
+<p>5. emoji, with VS15: <span style="font-variant-emoji: emoji">X5&#xfe0e;&#x20e3;X</span></p>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/urls/fragment-only-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-values/urls/fragment-only-expected.txt
deleted file mode 100644
index e79da1e..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-values/urls/fragment-only-expected.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] empty URL: inline-unquoted
-  assert_equals: expected "url(\\"#foo\\")" but got "url(\\"http://web-platform.test:8001/css/css-values/urls/fragment-only.html#foo\\")"
-[FAIL] empty URL: inline-quoted
-  assert_equals: expected "url(\\"#foo\\")" but got "url(\\"http://web-platform.test:8001/css/css-values/urls/fragment-only.html#foo\\")"
-[FAIL] empty URL: external-unquoted
-  assert_equals: expected "url(\\"#foo\\")" but got "url(\\"http://web-platform.test:8001/css/css-values/urls/support/fragment-only-urls.css#foo\\")"
-[FAIL] empty URL: external-quoted
-  assert_equals: expected "url(\\"#foo\\")" but got "url(\\"http://web-platform.test:8001/css/css-values/urls/support/fragment-only-urls.css#foo\\")"
-[FAIL] empty URL: external-variable
-  assert_equals: expected "url(\\"#foo\\")" but got "url(\\"http://web-platform.test:8001/css/css-values/urls/support/fragment-only-urls.css#foo\\")"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/css/css-view-transitions/root-replace-crash.html b/third_party/blink/web_tests/external/wpt/css/css-view-transitions/root-replace-crash.html
new file mode 100644
index 0000000..165eec4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-view-transitions/root-replace-crash.html
@@ -0,0 +1,15 @@
+<html>
+<head>
+  <script>
+    document.addEventListener("DOMContentLoaded", async () => {
+      const tt = document.createElementNS("http://www.w3.org/1999/xhtml", "tt")
+      tt.popover = "manual"
+      const viewTransition = document.startViewTransition(undefined)
+      await viewTransition.updateCallbackDone
+      for (let i = 0; i < 2; i++) {
+        document.replaceChild(tt, document.childNodes[(2324524876 % document.childNodes.length)])
+      }
+    })
+  </script>
+  <head>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/cssom-view/getBoundingClientRect-newline.html b/third_party/blink/web_tests/external/wpt/css/cssom-view/getBoundingClientRect-newline.html
index 40e2918..ce82b723 100644
--- a/third_party/blink/web_tests/external/wpt/css/cssom-view/getBoundingClientRect-newline.html
+++ b/third_party/blink/web_tests/external/wpt/css/cssom-view/getBoundingClientRect-newline.html
@@ -5,17 +5,14 @@
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <style>
-div {
+div[contenteditable] {
   white-space: pre;
-  font-family: Ahem;
-  font-size: 10px;
-  line-height: 1;
+  font: 10px/1 Ahem;
   width: 10ch;
 }
 </style>
 <body>
 <div contenteditable></div>
-</body>
 <script>
 function getBoundingClientRect(node, offset) {
   const range = document.createRange();
@@ -37,3 +34,4 @@
   assert_less_than(rect6.y, rect7.y);
 }, 'Range.getBoundingClientRect() should return the first position of the next line when the collapsed range is a newline character');
 </script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/docs/writing-tests/testdriver.md b/third_party/blink/web_tests/external/wpt/docs/writing-tests/testdriver.md
index 67757af..e0fae52 100644
--- a/third_party/blink/web_tests/external/wpt/docs/writing-tests/testdriver.md
+++ b/third_party/blink/web_tests/external/wpt/docs/writing-tests/testdriver.md
@@ -313,3 +313,11 @@
 .. js:autofunction:: test_driver.bidi.bluetooth.simulate_preconnected_peripheral
 .. js:autofunction:: test_driver.bidi.bluetooth.request_device_prompt_updated
 ```
+
+### Emulation ###
+
+Emulation of browser APIs via [WebDriver BiDi Emulation](https://www.w3.org/TR/webdriver-bidi/#module-emulation).
+
+```eval_rst
+.. js:autofunction:: test_driver.bidi.emulation.set_geolocation_override
+```
diff --git a/third_party/blink/web_tests/external/wpt/dom/observable/WEB_FEATURES.yml b/third_party/blink/web_tests/external/wpt/dom/observable/WEB_FEATURES.yml
new file mode 100644
index 0000000..3e872791
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/dom/observable/WEB_FEATURES.yml
@@ -0,0 +1,3 @@
+features:
+- name: observable
+  files: "**"
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-input-keyboard-behavior.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-input-keyboard-behavior.tentative.html
new file mode 100644
index 0000000..bf3fbab9
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-input-keyboard-behavior.tentative.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://issues.chromium.org/issues/402429384">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+
+<style>
+select, ::picker(select) {
+  appearance: base-select;
+}
+</style>
+
+<select>
+  <div id=inputwrapper></div>
+  <option class=one>one</option>
+  <option class=two>two</option>
+</select>
+
+<script>
+const select = document.querySelector('select');
+const optionOne = select.querySelector('option.one');
+const optionTwo = select.querySelector('option.two');
+const input = document.createElement('input');
+document.getElementById('inputwrapper').appendChild(input);
+
+function pressKey(key) {
+  return (new test_driver.Actions()
+    .keyDown(key)
+    .keyUp(key))
+    .send();
+}
+const spaceKey = '\uE00D';
+const arrowDown = '\uE015';
+const arrowUp = '\uE013';
+
+promise_test(async () => {
+  assert_equals(getComputedStyle(select).appearance, 'base-select',
+    'appearance: base-select must be supported for this test to run.');
+
+  select.focus();
+  await pressKey(spaceKey);
+  assert_true(select.matches(':open'),
+    'select should be open after pressing space.');
+  assert_equals(document.activeElement, input,
+    'input should be focused after opening select.');
+
+  await pressKey(arrowDown);
+  assert_equals(document.activeElement, optionOne,
+    'first option should be focused after arrow down from input.');
+
+  await pressKey(arrowUp);
+  assert_equals(document.activeElement, input,
+    'input should be focused after arrow up from first option.');
+}, 'Keyboard behavior of <input> in <select>.');
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/infrastructure/testdriver/bidi/emulation/set_geolocation_override.https.html b/third_party/blink/web_tests/external/wpt/infrastructure/testdriver/bidi/emulation/set_geolocation_override.https.html
index 5cbcf54..e339f39d 100644
--- a/third_party/blink/web_tests/external/wpt/infrastructure/testdriver/bidi/emulation/set_geolocation_override.https.html
+++ b/third_party/blink/web_tests/external/wpt/infrastructure/testdriver/bidi/emulation/set_geolocation_override.https.html
@@ -13,10 +13,6 @@
             descriptor: {name: "geolocation"},
             state: "granted",
         });
-
-        // Ensure any previously set geolocation emulations are cleared.
-        await test_driver.bidi.emulation.set_geolocation_override(
-            {coordinates: null});
     });
 
     /** Get the current geolocation or error */
@@ -24,13 +20,13 @@
         return new Promise(
             resolve => window.navigator.geolocation.getCurrentPosition(
                 position => resolve(position.coords.toJSON()),
-                error => resolve({code: error.code, message: error.message}),
+                error => resolve({code: error.code}),
                 // Fail fast if geolocation is not available.
                 {timeout: 500}
             ))
     }
 
-    /** Asserts that relevant coordinate properties match */
+    /** Asserts that coordinate or error matches the expected value */
     function assert_coordinates_match(actual, expected) {
         for (const key in expected) {
             assert_equals(actual[key], expected[key],
@@ -49,6 +45,11 @@
     };
 
     promise_test(async (t) => {
+        t.add_cleanup(async () => {
+            await test_driver.bidi.emulation.set_geolocation_override(
+                {coordinates: null});
+        });
+
         // Get the initial geolocation (might be error).
         const initial_coords = await get_current_geolocation();
 
@@ -66,6 +67,28 @@
         await test_driver.bidi.emulation.set_geolocation_override(
             {coordinates: null});
         // Assert coordinates are set to the original value.
-        assert_coordinates_match(await get_current_geolocation(), initial_coords);
+        assert_coordinates_match(await get_current_geolocation(),
+            initial_coords);
     }, "emulate geolocation and clear override");
+
+    promise_test(async (t) => {
+        // Emulate position unavailable.
+        await test_driver.bidi.emulation.set_geolocation_override({
+            error: {type: 'positionUnavailable'},
+        });
+
+        // Get the geolocation after setting the override.
+        const error = await get_current_geolocation();
+
+        assert_equals(error.code, 2,
+            `Geolocation should be unavailable with code POSITION_UNAVAILABLE = 2`);
+    }, "emulate geolocation position unavailable");
+
+    promise_test(async (t) => {
+        assert_throws_js(Error,
+            () => test_driver.bidi.emulation.set_geolocation_override({
+                error: {type: 'positionUnavailable'},
+                coordinates: SOME_COORDINATES
+            }), "Setting both `coordinates` and `error` should fail");
+    }, "cannot set `coordinates` and `error` simultaneously");
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/resources/testdriver.js b/third_party/blink/web_tests/external/wpt/resources/testdriver.js
index 6e8410b7..992b9e3 100644
--- a/third_party/blink/web_tests/external/wpt/resources/testdriver.js
+++ b/third_party/blink/web_tests/external/wpt/resources/testdriver.js
@@ -280,8 +280,14 @@
                  * @param {object} params - Parameters for the command.
                  * @param {null|object} params.coordinates - The optional
                  * geolocation coordinates to set. Matches the
-                 * `emulation.GeolocationCoordinates <https://w3c.github.io/webdriver-bidi/#type-emulation-GeolocationCoordinates>`_
-                 * value. If null or omitted, the emulation will be removed.
+                 * `emulation.GeolocationCoordinates <https://w3c.github.io/webdriver-bidi/#commands-emulationsetgeolocationoverride>`_
+                 * value. If null or omitted and the `params.error` is set, the
+                 * emulation will be removed. Mutually exclusive with
+                 * `params.error`.
+                 * @param {object} params.error - The optional
+                 * geolocation error to emulate. Matches the
+                 * `emulation.GeolocationPositionError <https://w3c.github.io/webdriver-bidi/#commands-emulationsetgeolocationoverride>`_
+                 * value. Mutually exclusive with `params.coordinates`.
                  * @param {null|Array.<(Context)>} [params.contexts] The
                  * optional contexts parameter specifies which browsing contexts
                  * to set the geolocation override on. It should be either an
diff --git a/third_party/blink/web_tests/external/wpt/speculation-rules/prefetch/resources/basic-service-worker.js b/third_party/blink/web_tests/external/wpt/speculation-rules/prefetch/resources/basic-service-worker.js
index 17fccd44..59d7d8b 100644
--- a/third_party/blink/web_tests/external/wpt/speculation-rules/prefetch/resources/basic-service-worker.js
+++ b/third_party/blink/web_tests/external/wpt/speculation-rules/prefetch/resources/basic-service-worker.js
@@ -32,6 +32,13 @@
 
     if (swOption === 'fetch-handler') {
       event.respondWith(fetch(event.request));
+    } else if (swOption === 'fetch-handler-synthetic') {
+      const finalUrl = new URL(event.request.url).searchParams.get('location');
+      if (finalUrl) {
+        event.respondWith(Response.redirect(finalUrl));
+      } else {
+        // Fallback to the network.
+      }
     } else if (swOption === 'fetch-handler-modify-url') {
       // The "Sec-Purpose: prefetch" header is dropped in fetch-handler-modify-*
       // cases in Step 33 of // https://fetch.spec.whatwg.org/#dom-request
diff --git a/third_party/blink/web_tests/external/wpt/speculation-rules/prefetch/resources/counting-executor.py b/third_party/blink/web_tests/external/wpt/speculation-rules/prefetch/resources/counting-executor.py
index cbcbc8e..3511fe49 100644
--- a/third_party/blink/web_tests/external/wpt/speculation-rules/prefetch/resources/counting-executor.py
+++ b/third_party/blink/web_tests/external/wpt/speculation-rules/prefetch/resources/counting-executor.py
@@ -21,6 +21,11 @@
   request_count["prefetch" if prefetch else "nonPrefetch"] += 1
   request.server.stash.put(uuid, request_count)
 
+  if b"location" in request.GET:
+    response.status = 302
+    response.headers.set(b"Location", request.GET[b"location"])
+    return
+
   response.content = template(
     request,
     open(os.path.join(os.path.dirname(__file__), "executor.sub.html"), "rb").read())
diff --git a/third_party/blink/web_tests/external/wpt/speculation-rules/prefetch/tentative/service-worker/redirect.sub.https.html b/third_party/blink/web_tests/external/wpt/speculation-rules/prefetch/tentative/service-worker/redirect.sub.https.html
new file mode 100644
index 0000000..f56f6bf
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/speculation-rules/prefetch/tentative/service-worker/redirect.sub.https.html
@@ -0,0 +1,122 @@
+<!DOCTYPE html>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="../../../resources/utils.js"></script>
+<script src="../../resources/utils.sub.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+
+<meta name="variant" content="?origin=same-site&sc=in-in&sw=fetch-handler">
+<meta name="variant" content="?origin=same-site&sc=in-in&sw=fetch-handler-to-fallback">
+<meta name="variant" content="?origin=same-site&sc=in-in&sw=fetch-handler-synthetic">
+<meta name="variant" content="?origin=same-site&sc=in-in&sw=no-fetch-handler">
+
+<meta name="variant" content="?origin=same-site&sc=in-out&sw=fetch-handler">
+<meta name="variant" content="?origin=same-site&sc=in-out&sw=fetch-handler-to-fallback">
+<meta name="variant" content="?origin=same-site&sc=in-out&sw=fetch-handler-synthetic">
+<meta name="variant" content="?origin=same-site&sc=in-out&sw=no-fetch-handler">
+
+<meta name="variant" content="?origin=same-site&sc=out-in&sw=fetch-handler">
+<meta name="variant" content="?origin=same-site&sc=out-in&sw=fetch-handler-to-fallback">
+<meta name="variant" content="?origin=same-site&sc=out-in&sw=no-fetch-handler">
+
+<script>
+setup(() => assertSpeculationRulesIsSupported());
+
+const originOption = new URL(location.href).searchParams.get('origin');
+const swOption = new URL(location.href).searchParams.get('sw');
+
+// We use a short name `sc` to avoid long file names https://crbug.com/40469687
+const scopeOption = new URL(location.href).searchParams.get('sc');
+
+promise_test(async t => {
+  const hostname = originOption === 'cross-site' ? '{{hosts[alt][www]}}'
+                                                 : undefined;
+  const win = await spawnWindow(t, { protocol: 'https', hostname: hostname });
+
+  const finalUrl = win.getExecutorURL({ executor: 'counting-executor.py', protocol: 'https', page: 2 });
+  const initialUrl = new URL('../../resources/counting-executor.py', location.href);
+  // Assign a different UUID to track the number of requests to server
+  // separately for the initialUrl and finalUrl.
+  initialUrl.searchParams.set('uuid', token());
+  initialUrl.searchParams.set('location', finalUrl);
+
+  const swUrl = new URL('../../resources/basic-service-worker.js?sw=' + swOption, location.href).href;
+
+  let sw1;
+  if (scopeOption === 'in-in' || scopeOption === 'in-out') {
+    // Register a SW for `initialUrl`.
+    const reg = await service_worker_unregister_and_register(
+        t, swUrl + '&sw1', initialUrl);
+    sw1 = reg.installing;
+    await wait_for_state(t, reg.installing, 'activated');
+  }
+
+  let sw2;
+  if (scopeOption === 'in-in' || scopeOption === 'out-in') {
+    // Register a SW for `finalUrl`.
+    const reg = await service_worker_unregister_and_register(
+        t, swUrl + '&sw2', finalUrl);
+    sw2 = reg.installing;
+    await wait_for_state(t, reg.installing, 'activated');
+  }
+
+  // Start speculation rules prefetch and navigate to the URL.
+  await win.forceSinglePrefetch(initialUrl);
+
+  await win.navigate(initialUrl, {expectedDestinationUrl: finalUrl});
+
+  const initialRequestCount = await (await fetch(initialUrl + '&check')).json();
+  const finalRequestCount = await (await fetch(finalUrl + '&check')).json();
+
+  const headers = await win.execute_script(() => {
+    return requestHeaders;
+  }, []);
+
+  const controllerUrl = await win.execute_script(() => {
+    return navigator.serviceWorker.controller ?
+           navigator.serviceWorker.controller.scriptURL :
+           undefined;
+  }, []);
+
+  if (sw2) {
+    // The navigated page should be controlled by the ServiceWorker,
+    // regardless of whether prefetch is performed/used.
+    assert_equals(controllerUrl, swUrl + '&sw2');
+  } else {
+    assert_equals(controllerUrl, undefined);
+  }
+
+  // In any cases prefetch + redirects + ServiceWorker fails and prefetched
+  // results aren't served.
+  assert_not_prefetched(headers, "Prefetched result should not be served.");
+
+  if (swOption === 'fetch-handler-synthetic') {
+    // Synthetic redirect response from ServiceWorker prevents the initial
+    // prefetch/non-prefetch requests to the server.
+    assert_equals(initialRequestCount.prefetch, 0,
+        'prefetch requests to initial URL should not be sent to the server.');
+    assert_equals(initialRequestCount.nonPrefetch, 0,
+        'a non-prefetch requests to initial URL should not be sent to the server.');
+  } else {
+    // Otherwise, the initial prefetch request (either with or without
+    // ServiceWorker interception) anyway reaches the server, and then
+    // re-requested as a non-prefetch request on navigation.
+    assert_equals(initialRequestCount.prefetch, 1,
+        'prefetch requests to initial URL should be sent to the server.');
+    assert_equals(initialRequestCount.nonPrefetch, 1,
+        'a non-prefetch requests to initial URL should be sent to the server.');
+  }
+
+  // Prefetch requests to the URL after redirects shouldn't be sent to server
+  // because prefetch should fail immediately on receiving redirects.
+  assert_equals(finalRequestCount.prefetch, 0,
+      'prefetch requests should not be sent to the server.');
+  // Instead, a single non-prefetch request should be sent.
+  assert_equals(finalRequestCount.nonPrefetch, 1,
+      'a non-prefetch request should be sent to the server.');
+
+}, "Prefetch with ServiceWorker (" + swOption + ")");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/emulation/set_geolocation_override/__init__.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/emulation/set_geolocation_override/__init__.py
index 7861734..5e1fd1a 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/emulation/set_geolocation_override/__init__.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/emulation/set_geolocation_override/__init__.py
@@ -1,28 +1 @@
-from webdriver.bidi.modules.script import ContextTarget
-
-from ... import remote_mapping_to_dict
-
-
 TEST_COORDINATES = {"latitude": 10, "longitude": 15, "accuracy": 0.5}
-
-
-async def get_current_geolocation(bidi_session, context):
-    # Per geolocation spec, the geolocation coordinates are returned
-    # only for an active browsing context. It might be required to
-    # re-activate the previously active tab in the test.
-    await bidi_session.browsing_context.activate(context=context["context"])
-
-    result = await bidi_session.script.call_function(
-        function_declaration="""() =>
-            new Promise(
-                resolve => window.navigator.geolocation.getCurrentPosition(
-                    position => resolve(position.coords.toJSON()),
-                    error => resolve({code: error.code}),
-                    {timeout: 500}
-            ))
-        """,
-        target=ContextTarget(context["context"]),
-        await_promise=True,
-    )
-
-    return remote_mapping_to_dict(result["value"])
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/emulation/set_geolocation_override/conftest.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/emulation/set_geolocation_override/conftest.py
index a90895c..5fb9451f 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/emulation/set_geolocation_override/conftest.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/emulation/set_geolocation_override/conftest.py
@@ -1,6 +1,35 @@
 import pytest_asyncio
 
-from ... import get_context_origin
+from webdriver.bidi.modules.script import ContextTarget
+
+from ... import get_context_origin, remote_mapping_to_dict
+
+
+@pytest_asyncio.fixture
+async def get_current_geolocation(bidi_session, configuration):
+    async def get_current_geolocation(context):
+        # Per geolocation spec, the geolocation coordinates are returned
+        # only for an active browsing context. It might be required to
+        # re-activate the previously active tab in the test.
+        await bidi_session.browsing_context.activate(context=context["context"])
+
+        result = await bidi_session.script.call_function(
+            function_declaration="""(multiplier) =>
+                new Promise(
+                    resolve => window.navigator.geolocation.getCurrentPosition(
+                        position => resolve(position.coords.toJSON()),
+                        error => resolve({code: error.code}),
+                        {timeout: 500 * multiplier}
+                ))
+            """,
+            arguments=[{"type": "number", "value": configuration["timeout_multiplier"]}],
+            target=ContextTarget(context["context"]),
+            await_promise=True,
+        )
+
+        return remote_mapping_to_dict(result["value"])
+
+    return get_current_geolocation
 
 
 @pytest_asyncio.fixture
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/emulation/set_geolocation_override/contexts.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/emulation/set_geolocation_override/contexts.py
index 8a0e434..068bb80 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/emulation/set_geolocation_override/contexts.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/emulation/set_geolocation_override/contexts.py
@@ -2,14 +2,14 @@
 
 from webdriver.bidi.modules.emulation import CoordinatesOptions
 
-from . import get_current_geolocation, TEST_COORDINATES
+from . import TEST_COORDINATES
 
 
 pytestmark = pytest.mark.asyncio
 
 
 async def test_contexts(
-    bidi_session, new_tab, top_context, url, set_geolocation_permission
+    bidi_session, new_tab, top_context, url, get_current_geolocation, set_geolocation_permission
 ):
     test_url = url("/common/blank.html")
     await bidi_session.browsing_context.navigate(
@@ -24,12 +24,10 @@
     )
     await set_geolocation_permission(new_tab)
 
-    default_coordinates = await get_current_geolocation(bidi_session, new_tab)
+    default_coordinates = await get_current_geolocation(new_tab)
 
     assert default_coordinates != TEST_COORDINATES
-    assert (
-        await get_current_geolocation(bidi_session, top_context) == default_coordinates
-    )
+    assert await get_current_geolocation(top_context) == default_coordinates
 
     # Set geolocation override.
     await bidi_session.emulation.set_geolocation_override(
@@ -41,24 +39,20 @@
         ),
     )
 
-    assert await get_current_geolocation(bidi_session, new_tab) == TEST_COORDINATES
-    assert (
-        await get_current_geolocation(bidi_session, top_context) == default_coordinates
-    )
+    assert await get_current_geolocation(new_tab) == TEST_COORDINATES
+    assert await get_current_geolocation(top_context) == default_coordinates
 
     # Reset geolocation override.
     await bidi_session.emulation.set_geolocation_override(
         contexts=[new_tab["context"]], coordinates=None
     )
 
-    assert await get_current_geolocation(bidi_session, new_tab) == default_coordinates
-    assert (
-        await get_current_geolocation(bidi_session, top_context) == default_coordinates
-    )
+    assert await get_current_geolocation(new_tab) == default_coordinates
+    assert await get_current_geolocation(top_context) == default_coordinates
 
 
 async def test_multiple_contexts(
-    bidi_session, new_tab, url, set_geolocation_permission
+    bidi_session, new_tab, url, get_current_geolocation, set_geolocation_permission
 ):
     new_context = await bidi_session.browsing_context.create(type_hint="tab")
     test_url = url("/common/blank.html")
@@ -74,12 +68,10 @@
     )
     await set_geolocation_permission(new_tab)
 
-    default_coordinates = await get_current_geolocation(bidi_session, new_tab)
+    default_coordinates = await get_current_geolocation(new_tab)
 
     assert default_coordinates != TEST_COORDINATES
-    assert (
-        await get_current_geolocation(bidi_session, new_context) == default_coordinates
-    )
+    assert await get_current_geolocation(new_context) == default_coordinates
 
     # Set geolocation override.
     await bidi_session.emulation.set_geolocation_override(
@@ -91,8 +83,8 @@
         ),
     )
 
-    assert await get_current_geolocation(bidi_session, new_tab) == TEST_COORDINATES
-    assert await get_current_geolocation(bidi_session, new_context) == TEST_COORDINATES
+    assert await get_current_geolocation(new_tab) == TEST_COORDINATES
+    assert await get_current_geolocation(new_context) == TEST_COORDINATES
 
     # Reset geolocation override.
     await bidi_session.emulation.set_geolocation_override(
@@ -101,7 +93,5 @@
 
     # The new coordinates can be different from the initial ones if the position
     # was not available at the beginning.
-    assert await get_current_geolocation(bidi_session, new_tab) != TEST_COORDINATES
-    assert (
-        await get_current_geolocation(bidi_session, new_context) != TEST_COORDINATES
-    )
+    assert await get_current_geolocation(new_tab) != TEST_COORDINATES
+    assert await get_current_geolocation(new_context) != TEST_COORDINATES
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/emulation/set_geolocation_override/coordinates.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/emulation/set_geolocation_override/coordinates.py
index b23354e..ea4fe64 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/emulation/set_geolocation_override/coordinates.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/emulation/set_geolocation_override/coordinates.py
@@ -4,7 +4,6 @@
 from webdriver.bidi.modules.script import ContextTarget
 
 from ... import remote_mapping_to_dict
-from . import get_current_geolocation
 
 
 pytestmark = pytest.mark.asyncio
@@ -22,7 +21,12 @@
     ],
 )
 async def test_get_current_position(
-    bidi_session, new_tab, url, set_geolocation_permission, test_coordinates
+    bidi_session,
+    new_tab,
+    url,
+    get_current_geolocation,
+    set_geolocation_permission,
+    test_coordinates,
 ):
     test_url = url("/common/blank.html")
     await bidi_session.browsing_context.navigate(
@@ -32,7 +36,7 @@
     )
     await set_geolocation_permission(new_tab)
 
-    default_coordinates = await get_current_geolocation(bidi_session, new_tab)
+    default_coordinates = await get_current_geolocation(new_tab)
 
     # Set default accuracy value.
     if "accuracy" not in test_coordinates:
@@ -44,7 +48,7 @@
         contexts=[new_tab["context"]], coordinates=test_coordinates
     )
 
-    assert await get_current_geolocation(bidi_session, new_tab) == test_coordinates
+    assert await get_current_geolocation(new_tab) == test_coordinates
 
 
 async def test_watch_position(
@@ -132,7 +136,7 @@
 
 
 async def test_persists_on_reload(
-    bidi_session, url, new_tab, set_geolocation_permission
+    bidi_session, url, new_tab, get_current_geolocation, set_geolocation_permission
 ):
     test_url = url("/common/blank.html")
     await bidi_session.browsing_context.navigate(
@@ -153,17 +157,17 @@
         ),
     )
 
-    assert await get_current_geolocation(bidi_session, new_tab) == test_coordinates
+    assert await get_current_geolocation(new_tab) == test_coordinates
 
     await bidi_session.browsing_context.reload(
         context=new_tab["context"], wait="complete"
     )
 
-    assert await get_current_geolocation(bidi_session, new_tab) == test_coordinates
+    assert await get_current_geolocation(new_tab) == test_coordinates
 
 
 async def test_persists_on_navigation(
-    bidi_session, url, new_tab, set_geolocation_permission
+    bidi_session, url, new_tab, get_current_geolocation, set_geolocation_permission
 ):
     test_url = url("/common/blank.html")
     await bidi_session.browsing_context.navigate(
@@ -184,7 +188,7 @@
         ),
     )
 
-    assert await get_current_geolocation(bidi_session, new_tab) == test_coordinates
+    assert await get_current_geolocation(new_tab) == test_coordinates
 
     await bidi_session.browsing_context.navigate(
         context=new_tab["context"],
@@ -192,4 +196,24 @@
         wait="complete",
     )
 
-    assert await get_current_geolocation(bidi_session, new_tab) == test_coordinates
+    assert await get_current_geolocation(new_tab) == test_coordinates
+
+
+async def test_reset_without_override(
+    bidi_session, new_tab, url, get_current_geolocation, set_geolocation_permission
+):
+    test_url = url("/common/blank.html")
+    await bidi_session.browsing_context.navigate(
+        context=new_tab["context"],
+        url=test_url,
+        wait="complete",
+    )
+    await set_geolocation_permission(new_tab)
+
+    default_coordinates = await get_current_geolocation(new_tab)
+
+    await bidi_session.emulation.set_geolocation_override(
+        contexts=[new_tab["context"]], coordinates=None
+    )
+
+    assert await get_current_geolocation(new_tab) == default_coordinates
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/emulation/set_geolocation_override/error.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/emulation/set_geolocation_override/error.py
index ec905e3..27c23dc7 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/emulation/set_geolocation_override/error.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/emulation/set_geolocation_override/error.py
@@ -3,7 +3,6 @@
 from webdriver.bidi.modules.script import ContextTarget
 
 from ... import remote_mapping_to_dict
-from . import get_current_geolocation
 
 pytestmark = pytest.mark.asyncio
 
@@ -12,7 +11,7 @@
 
 
 async def test_get_current_position(bidi_session, new_tab, url,
-        set_geolocation_permission):
+        get_current_geolocation, set_geolocation_permission):
     test_url = url("/common/blank.html")
     await bidi_session.browsing_context.navigate(
         context=new_tab["context"],
@@ -25,8 +24,7 @@
         contexts=[new_tab["context"]], error=ERROR
     )
 
-    assert await get_current_geolocation(bidi_session,
-                                         new_tab) == EXPECTED_ERROR
+    assert await get_current_geolocation(new_tab) == EXPECTED_ERROR
 
 
 async def test_watch_position(
@@ -72,7 +70,7 @@
 
 
 async def test_persists_on_reload(
-        bidi_session, url, new_tab, set_geolocation_permission
+        bidi_session, url, new_tab, get_current_geolocation, set_geolocation_permission
 ):
     test_url = url("/common/blank.html")
     await bidi_session.browsing_context.navigate(
@@ -88,19 +86,17 @@
         error=ERROR,
     )
 
-    assert await get_current_geolocation(bidi_session,
-                                         new_tab) == EXPECTED_ERROR
+    assert await get_current_geolocation(new_tab) == EXPECTED_ERROR
 
     await bidi_session.browsing_context.reload(
         context=new_tab["context"], wait="complete"
     )
 
-    assert await get_current_geolocation(bidi_session,
-                                         new_tab) == EXPECTED_ERROR
+    assert await get_current_geolocation(new_tab) == EXPECTED_ERROR
 
 
 async def test_persists_on_navigation(
-        bidi_session, url, new_tab, set_geolocation_permission
+        bidi_session, url, new_tab, get_current_geolocation, set_geolocation_permission
 ):
     test_url = url("/common/blank.html")
     await bidi_session.browsing_context.navigate(
@@ -116,8 +112,7 @@
         error=ERROR,
     )
 
-    assert await get_current_geolocation(bidi_session,
-                                         new_tab) == EXPECTED_ERROR
+    assert await get_current_geolocation(new_tab) == EXPECTED_ERROR
 
     await bidi_session.browsing_context.navigate(
         context=new_tab["context"],
@@ -125,5 +120,4 @@
         wait="complete",
     )
 
-    assert await get_current_geolocation(bidi_session,
-                                         new_tab) == EXPECTED_ERROR
+    assert await get_current_geolocation(new_tab) == EXPECTED_ERROR
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/emulation/set_geolocation_override/user_contexts.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/emulation/set_geolocation_override/user_contexts.py
index 008dee5..b3038bb5 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/emulation/set_geolocation_override/user_contexts.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/emulation/set_geolocation_override/user_contexts.py
@@ -2,14 +2,19 @@
 
 from webdriver.bidi.modules.emulation import CoordinatesOptions
 
-from . import get_current_geolocation, TEST_COORDINATES
+from . import TEST_COORDINATES
 
 
 pytestmark = pytest.mark.asyncio
 
 
 async def test_user_contexts(
-    bidi_session, url, create_user_context, new_tab, set_geolocation_permission
+    bidi_session,
+    url,
+    create_user_context,
+    new_tab,
+    get_current_geolocation,
+    set_geolocation_permission,
 ):
     user_context = await create_user_context()
     context_in_user_context_1 = await bidi_session.browsing_context.create(
@@ -29,12 +34,10 @@
     await set_geolocation_permission(new_tab)
     await set_geolocation_permission(new_tab, user_context)
 
-    default_coordinates = await get_current_geolocation(
-        bidi_session, context_in_user_context_1
-    )
+    default_coordinates = await get_current_geolocation(context_in_user_context_1)
 
     assert default_coordinates != TEST_COORDINATES
-    assert await get_current_geolocation(bidi_session, new_tab) == default_coordinates
+    assert await get_current_geolocation(new_tab) == default_coordinates
 
     # Set geolocation override.
     await bidi_session.emulation.set_geolocation_override(
@@ -46,11 +49,8 @@
         ),
     )
 
-    assert (
-        await get_current_geolocation(bidi_session, context_in_user_context_1)
-        == TEST_COORDINATES
-    )
-    assert await get_current_geolocation(bidi_session, new_tab) == default_coordinates
+    assert await get_current_geolocation(context_in_user_context_1) == TEST_COORDINATES
+    assert await get_current_geolocation(new_tab) == default_coordinates
 
     # Create a new context in the user context.
     context_in_user_context_2 = await bidi_session.browsing_context.create(
@@ -62,14 +62,16 @@
         wait="complete",
     )
 
-    assert (
-        await get_current_geolocation(bidi_session, context_in_user_context_2)
-        == TEST_COORDINATES
-    )
+    assert await get_current_geolocation(context_in_user_context_2) == TEST_COORDINATES
 
 
 async def test_set_to_default_user_context(
-    bidi_session, new_tab, create_user_context, url, set_geolocation_permission
+    bidi_session,
+    new_tab,
+    create_user_context,
+    url,
+    get_current_geolocation,
+    set_geolocation_permission,
 ):
     user_context = await create_user_context()
     context_in_user_context_1 = await bidi_session.browsing_context.create(
@@ -89,7 +91,7 @@
     await set_geolocation_permission(new_tab)
     await set_geolocation_permission(new_tab, user_context)
 
-    default_coordinates = await get_current_geolocation(bidi_session, new_tab)
+    default_coordinates = await get_current_geolocation(new_tab)
     assert default_coordinates != TEST_COORDINATES
 
     await bidi_session.emulation.set_geolocation_override(
@@ -103,10 +105,9 @@
 
     # Make sure that the geolocation changes are only applied to the context associated with default user context.
     assert (
-        await get_current_geolocation(bidi_session, context_in_user_context_1)
-        == default_coordinates
+        await get_current_geolocation(context_in_user_context_1) == default_coordinates
     )
-    assert await get_current_geolocation(bidi_session, new_tab) == TEST_COORDINATES
+    assert await get_current_geolocation(new_tab) == TEST_COORDINATES
 
     # Create a new context in the default context.
     context_in_default_context_2 = await bidi_session.browsing_context.create(
@@ -120,8 +121,7 @@
     )
 
     assert (
-        await get_current_geolocation(bidi_session, context_in_default_context_2)
-        == TEST_COORDINATES
+        await get_current_geolocation(context_in_default_context_2) == TEST_COORDINATES
     )
 
     # Reset geolocation override.
@@ -131,7 +131,11 @@
 
 
 async def test_set_to_multiple_user_contexts(
-    bidi_session, create_user_context, url, set_geolocation_permission
+    bidi_session,
+    create_user_context,
+    url,
+    get_current_geolocation,
+    set_geolocation_permission,
 ):
     user_context_1 = await create_user_context()
     context_in_user_context_1 = await bidi_session.browsing_context.create(
@@ -164,18 +168,17 @@
         ),
     )
 
-    assert (
-        await get_current_geolocation(bidi_session, context_in_user_context_1)
-        == TEST_COORDINATES
-    )
-    assert (
-        await get_current_geolocation(bidi_session, context_in_user_context_2)
-        == TEST_COORDINATES
-    )
+    assert await get_current_geolocation(context_in_user_context_1) == TEST_COORDINATES
+    assert await get_current_geolocation(context_in_user_context_2) == TEST_COORDINATES
 
 
 async def test_set_to_user_context_and_then_to_context(
-    bidi_session, create_user_context, url, new_tab, set_geolocation_permission
+    bidi_session,
+    create_user_context,
+    url,
+    new_tab,
+    get_current_geolocation,
+    set_geolocation_permission,
 ):
     user_context = await create_user_context()
     context_in_user_context_1 = await bidi_session.browsing_context.create(
@@ -195,9 +198,7 @@
     await set_geolocation_permission(new_tab)
     await set_geolocation_permission(new_tab, user_context)
 
-    default_coordinates = await get_current_geolocation(
-        bidi_session, context_in_user_context_1
-    )
+    default_coordinates = await get_current_geolocation(context_in_user_context_1)
 
     assert default_coordinates != TEST_COORDINATES
 
@@ -222,7 +223,7 @@
         ),
     )
     assert (
-        await get_current_geolocation(bidi_session, context_in_user_context_1)
+        await get_current_geolocation(context_in_user_context_1)
         == new_geolocation_coordinates
     )
 
@@ -232,7 +233,7 @@
 
     # Make sure that after reload the geolocation is still updated.
     assert (
-        await get_current_geolocation(bidi_session, context_in_user_context_1)
+        await get_current_geolocation(context_in_user_context_1)
         == new_geolocation_coordinates
     )
 
@@ -246,10 +247,7 @@
         wait="complete",
     )
     # Make sure that the geolocation override for the user context is applied.
-    assert (
-        await get_current_geolocation(bidi_session, context_in_user_context_2)
-        == TEST_COORDINATES
-    )
+    assert await get_current_geolocation(context_in_user_context_2) == TEST_COORDINATES
 
     await bidi_session.emulation.set_geolocation_override(
         contexts=[context_in_user_context_1["context"]],
@@ -258,6 +256,5 @@
 
     # Make sure that the geolocation override was reset.
     assert (
-        await get_current_geolocation(bidi_session, context_in_user_context_1)
-        == default_coordinates
+        await get_current_geolocation(context_in_user_context_1) == default_coordinates
     )
diff --git a/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/qdq_subgraph.https.any.js b/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/qdq_subgraph.https.any.js
index 3b59c3b..64f3776 100644
--- a/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/qdq_subgraph.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/qdq_subgraph.https.any.js
@@ -924,6 +924,89 @@
       }
     }
   },
+  {
+    'name': 'quantized elu',
+    'graph': {
+      'inputs': {
+        'input': {
+          'data': [
+            1.6811466217041016, 0.0479511022567749, 0.33355462551116943,
+            -0.1988269537687301, -0.0041167140007019, -0.0634240251779556,
+          ],
+          'descriptor': {shape: [2, 3], dataType: 'float32'},
+          'constant': false
+        },
+        'inputScale': {
+          'data': [0.003921568859368563],
+          'descriptor': {shape: [1], dataType: 'float32'},
+          'constant': true
+        },
+        'inputZeroPoint': {
+          'data': [0],
+          'descriptor': {shape: [1], dataType: 'int8'},
+          'constant': true
+        },
+        'outputScale': {
+          'data': [0.003921568859368563],
+          'descriptor': {shape: [1], dataType: 'float32'},
+          'constant': true
+        },
+        'outputZeroPoint': {
+          'data': [0],
+          'descriptor': {shape: [1], dataType: 'int8'},
+          'constant': true
+        },
+      },
+      'operators': [
+        {
+          'name': 'quantizeLinear',
+          'arguments': [
+            {'input': 'input'},
+            {'scale': 'inputScale', 'zeroPoint': 'inputZeroPoint'}
+          ],
+          'outputs': 'quantizedInput'
+        },
+        {
+          'name': 'dequantizeLinear',
+          'arguments': [
+            {'input': 'quantizedInput'},
+            {'scale': 'inputScale', 'zeroPoint': 'inputZeroPoint'}
+          ],
+          'outputs': 'dequantizedInput'
+        },
+        {
+          'name': 'elu',
+          'arguments': [{'input': 'dequantizedInput'}],
+          'outputs': 'eluOutput'
+        },
+        {
+          'name': 'quantizeLinear',
+          'arguments': [
+            {'input': 'eluOutput'},
+            {'scale': 'outputScale', 'zeroPoint': 'outputZeroPoint'}
+          ],
+          'outputs': 'quantizedeluOutput'
+        },
+        {
+          'name': 'dequantizeLinear',
+          'arguments': [
+            {'input': 'quantizedeluOutput'},
+            {'scale': 'outputScale', 'zeroPoint': 'outputZeroPoint'}
+          ],
+          'outputs': 'output'
+        }
+      ],
+      'expectedOutputs': {
+        'output': {
+          'data': [
+            0.49803924560546875, 0.0470588281750679, 0.3333333432674408,
+            -0.18039216101169586, -0.003921568859368563, -0.062745101749897,
+          ],
+          'descriptor': {shape: [2, 3], dataType: 'float32'}
+        }
+      }
+    }
+  },
 ];
 
 if (navigator.ml) {
diff --git a/third_party/blink/web_tests/http/tests/lcp_critical_path_predictor/prioritize_lcp_image.php b/third_party/blink/web_tests/http/tests/lcp_critical_path_predictor/prioritize_lcp_image.php
index db9f592c..34b93d9 100644
--- a/third_party/blink/web_tests/http/tests/lcp_critical_path_predictor/prioritize_lcp_image.php
+++ b/third_party/blink/web_tests/http/tests/lcp_critical_path_predictor/prioritize_lcp_image.php
@@ -18,19 +18,14 @@
     const hint_matched_img_priority = await internals.getInitialResourcePriority(url, document);
 
     assert_equals(hint_matched_img_priority, kVeryHigh);
-  }, "Ensure LCPP hinted images were loaded with VeryHigh priority.")
+  }, "Ensure LCPP hinted images were loaded with VeryHigh priority.");
 
   promise_test(async t => {
     const url = new URL('/resources/square100.png', location).toString();
     const hint_matched_img_priority = await internals.getInitialResourcePriority(url, document);
 
     assert_equals(hint_matched_img_priority, kMedium);
-  }, "Ensure non-LCPP hinted images were loaded unaffected with Medium priority.")
-
-  promise_test(async t => {
-    const lcp_element = await internals.LCPPrediction(document);
-    assert_equals(lcp_element, "/#lcp_image");
-  }, "Ensure document::RunLCPPredictedCallbacks is called with LCP element locator.")
+  }, "Ensure non-LCPP hinted images were loaded unaffected with Medium priority.");
 </script>
 <img src="/resources/square.png" id="lcp_image">
 <img src="/resources/square100.png">
diff --git a/third_party/blink/web_tests/http/tests/lcp_critical_path_predictor/resources/common.js b/third_party/blink/web_tests/http/tests/lcp_critical_path_predictor/resources/common.js
index 8fc4da50..b8a049c 100644
--- a/third_party/blink/web_tests/http/tests/lcp_critical_path_predictor/resources/common.js
+++ b/third_party/blink/web_tests/http/tests/lcp_critical_path_predictor/resources/common.js
@@ -15,6 +15,7 @@
     const hint = new LCPCriticalPathPredictorNavigationTimeHint();
     // All fields are non-nullable.
     hint.lcpElementLocators = [];
+    hint.lcpElementLocatorsAll = [];
     hint.lcpInfluencerScripts = params.lcpInfluencerScripts || [];
     hint.fetchedFonts = params.fetchedFonts || [];
     hint.preconnectOrigins = params.preconnectOrigins || [];
@@ -36,6 +37,11 @@
       hint.lcpElementLocators.push(await getLCPBytes(proto));
     }
 
+    const lcp_locator_protos_all = params.lcp_locator_protos_all || [];
+    for (let proto of lcp_locator_protos_all) {
+      hint.lcpElementLocatorsAll.push(await getLCPBytes(proto));
+    }
+
     const web_test_control_host_remote =
         new NonAssociatedWebTestControlHostRemote();
     web_test_control_host_remote.$.bindNewPipeAndPassReceiver().bindInBrowser(
diff --git a/third_party/blink/web_tests/http/tests/lcpp_timing_predictor/timing_predictor_fallback_delayed_lcp.php b/third_party/blink/web_tests/http/tests/lcpp_timing_predictor/timing_predictor_fallback_delayed_lcp.php
index b5873e8..49e82ac 100644
--- a/third_party/blink/web_tests/http/tests/lcpp_timing_predictor/timing_predictor_fallback_delayed_lcp.php
+++ b/third_party/blink/web_tests/http/tests/lcpp_timing_predictor/timing_predictor_fallback_delayed_lcp.php
@@ -2,7 +2,8 @@
 <script src="/priorities/resources/common.js"></script>
 <script type=module>
 import {setupLCPTest} from "../lcp_critical_path_predictor/resources/common.js";
-await setupLCPTest(["lcp_image_id.pb"]);
+await setupLCPTest({
+ lcp_locator_protos_all :["lcp_image_id.pb"]});
 </script>
 <?php
 // Do not output the HTML below this PHP block until the test is reloaded with
diff --git a/third_party/blink/web_tests/http/tests/lcpp_timing_predictor/timing_predictor_hit_secondary.php b/third_party/blink/web_tests/http/tests/lcpp_timing_predictor/timing_predictor_hit_secondary.php
index 4a7b35e..2c03112a 100644
--- a/third_party/blink/web_tests/http/tests/lcpp_timing_predictor/timing_predictor_hit_secondary.php
+++ b/third_party/blink/web_tests/http/tests/lcpp_timing_predictor/timing_predictor_hit_secondary.php
@@ -2,7 +2,8 @@
 <script src="/priorities/resources/common.js"></script>
 <script type=module>
 import {setupLCPTest} from "../lcp_critical_path_predictor/resources/common.js";
-await setupLCPTest(["lcp_image_id.pb", "lcp_image_id_b.pb"]);
+await setupLCPTest({
+ lcp_locator_protos_all :["lcp_image_id.pb", "lcp_image_id_b.pb"]});
 </script>
 <?php
 // Do not output the HTML below this PHP block until the test is reloaded with
diff --git a/third_party/blink/web_tests/http/tests/origin_trials/webexposed/foldable-apis-origin-trial-interfaces.html b/third_party/blink/web_tests/http/tests/origin_trials/webexposed/foldable-apis-origin-trial-interfaces.html
deleted file mode 100644
index 2bf18d6..0000000
--- a/third_party/blink/web_tests/http/tests/origin_trials/webexposed/foldable-apis-origin-trial-interfaces.html
+++ /dev/null
@@ -1,26 +0,0 @@
-<!DOCTYPE html>
-<meta charset="utf-8">
-<!-- Generate token with the command:
-generate_token.py http://127.0.0.1:8000 FoldableAPIs --expire-timestamp=2000000000
--- -->
-<meta http-equiv="origin-trial" content="AwbXTS8rkMQ7E3eYhkhRbmpyRf7dkXHwlUm2DR9yt+a/4UrtCvY5QVthdGlpXnDWi/VlABqvrgXEFdASIzs2uQEAAABUeyJvcmlnaW4iOiAiaHR0cDovLzEyNy4wLjAuMTo4MDAwIiwgImZlYXR1cmUiOiAiRm9sZGFibGVBUElzIiwgImV4cGlyeSI6IDIwMDAwMDAwMDB9" />
-<title>Foldable APIs - interfaces exposed by origin trial</title>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/origin-trials-helper.js"></script>
-<script>
-test(t => {
-  OriginTrialsHelper.check_properties_exist(this,
-      {
-        'Navigator': ['devicePosture'],
-        'DevicePosture': ['type'],
-      },
-  );
-  OriginTrialsHelper.check_properties_exist(this,
-      {
-        'Viewport': ['segments'],
-      },
-  );
-}, "Foldables APIs related interfaces in Origin-Trial enabled document.");
-
-</script>
diff --git a/third_party/blink/web_tests/platform/android/external/wpt/serial/idlharness.https.any-expected.txt b/third_party/blink/web_tests/platform/android/external/wpt/serial/idlharness.https.any-expected.txt
index a03b903b..bf166f0d 100644
--- a/third_party/blink/web_tests/platform/android/external/wpt/serial/idlharness.https.any-expected.txt
+++ b/third_party/blink/web_tests/platform/android/external/wpt/serial/idlharness.https.any-expected.txt
@@ -1,75 +1,5 @@
 This is a testharness.js-based test.
-Found 36 FAIL, 0 TIMEOUT, 0 NOTRUN.
-[FAIL] Serial interface: existence and properties of interface object
-  assert_own_property: self does not have own property "Serial" expected property "Serial" missing
-[FAIL] Serial interface object length
-  assert_own_property: self does not have own property "Serial" expected property "Serial" missing
-[FAIL] Serial interface object name
-  assert_own_property: self does not have own property "Serial" expected property "Serial" missing
-[FAIL] Serial interface: existence and properties of interface prototype object
-  assert_own_property: self does not have own property "Serial" expected property "Serial" missing
-[FAIL] Serial interface: existence and properties of interface prototype object's "constructor" property
-  assert_own_property: self does not have own property "Serial" expected property "Serial" missing
-[FAIL] Serial interface: existence and properties of interface prototype object's @@unscopables property
-  assert_own_property: self does not have own property "Serial" expected property "Serial" missing
-[FAIL] Serial interface: attribute onconnect
-  assert_own_property: self does not have own property "Serial" expected property "Serial" missing
-[FAIL] Serial interface: attribute ondisconnect
-  assert_own_property: self does not have own property "Serial" expected property "Serial" missing
-[FAIL] Serial interface: operation getPorts()
-  assert_own_property: self does not have own property "Serial" expected property "Serial" missing
-[FAIL] Serial interface: operation requestPort(optional SerialPortRequestOptions)
-  assert_own_property: self does not have own property "Serial" expected property "Serial" missing
-[FAIL] Serial must be primary interface of navigator.serial
-  assert_equals: wrong typeof object expected "object" but got "undefined"
-[FAIL] Stringification of navigator.serial
-  assert_equals: wrong typeof object expected "object" but got "undefined"
-[FAIL] Serial interface: navigator.serial must inherit property "onconnect" with the proper type
-  assert_equals: wrong typeof object expected "object" but got "undefined"
-[FAIL] Serial interface: navigator.serial must inherit property "ondisconnect" with the proper type
-  assert_equals: wrong typeof object expected "object" but got "undefined"
-[FAIL] Serial interface: navigator.serial must inherit property "getPorts()" with the proper type
-  assert_equals: wrong typeof object expected "object" but got "undefined"
-[FAIL] Serial interface: navigator.serial must inherit property "requestPort(optional SerialPortRequestOptions)" with the proper type
-  assert_equals: wrong typeof object expected "object" but got "undefined"
-[FAIL] Serial interface: calling requestPort(optional SerialPortRequestOptions) on navigator.serial with too few arguments must throw TypeError
-  assert_equals: wrong typeof object expected "object" but got "undefined"
-[FAIL] SerialPort interface: existence and properties of interface object
-  assert_own_property: self does not have own property "SerialPort" expected property "SerialPort" missing
-[FAIL] SerialPort interface object length
-  assert_own_property: self does not have own property "SerialPort" expected property "SerialPort" missing
-[FAIL] SerialPort interface object name
-  assert_own_property: self does not have own property "SerialPort" expected property "SerialPort" missing
-[FAIL] SerialPort interface: existence and properties of interface prototype object
-  assert_own_property: self does not have own property "SerialPort" expected property "SerialPort" missing
-[FAIL] SerialPort interface: existence and properties of interface prototype object's "constructor" property
-  assert_own_property: self does not have own property "SerialPort" expected property "SerialPort" missing
-[FAIL] SerialPort interface: existence and properties of interface prototype object's @@unscopables property
-  assert_own_property: self does not have own property "SerialPort" expected property "SerialPort" missing
-[FAIL] SerialPort interface: attribute onconnect
-  assert_own_property: self does not have own property "SerialPort" expected property "SerialPort" missing
-[FAIL] SerialPort interface: attribute ondisconnect
-  assert_own_property: self does not have own property "SerialPort" expected property "SerialPort" missing
+Found 1 FAIL, 0 TIMEOUT, 0 NOTRUN.
 [FAIL] SerialPort interface: attribute connected
-  assert_own_property: self does not have own property "SerialPort" expected property "SerialPort" missing
-[FAIL] SerialPort interface: attribute readable
-  assert_own_property: self does not have own property "SerialPort" expected property "SerialPort" missing
-[FAIL] SerialPort interface: attribute writable
-  assert_own_property: self does not have own property "SerialPort" expected property "SerialPort" missing
-[FAIL] SerialPort interface: operation getInfo()
-  assert_own_property: self does not have own property "SerialPort" expected property "SerialPort" missing
-[FAIL] SerialPort interface: operation open(SerialOptions)
-  assert_own_property: self does not have own property "SerialPort" expected property "SerialPort" missing
-[FAIL] SerialPort interface: operation setSignals(optional SerialOutputSignals)
-  assert_own_property: self does not have own property "SerialPort" expected property "SerialPort" missing
-[FAIL] SerialPort interface: operation getSignals()
-  assert_own_property: self does not have own property "SerialPort" expected property "SerialPort" missing
-[FAIL] SerialPort interface: operation close()
-  assert_own_property: self does not have own property "SerialPort" expected property "SerialPort" missing
-[FAIL] SerialPort interface: operation forget()
-  assert_own_property: self does not have own property "SerialPort" expected property "SerialPort" missing
-[FAIL] Navigator interface: attribute serial
-  assert_true: The prototype object must have a property "serial" expected true got false
-[FAIL] Navigator interface: navigator must inherit property "serial" with the proper type
-  assert_inherits: property "serial" not found in prototype chain
+  assert_true: The prototype object must have a property "connected" expected true got false
 Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/android/external/wpt/serial/idlharness.https.any.worker-expected.txt b/third_party/blink/web_tests/platform/android/external/wpt/serial/idlharness.https.any.worker-expected.txt
index eb943727..bf166f0d 100644
--- a/third_party/blink/web_tests/platform/android/external/wpt/serial/idlharness.https.any.worker-expected.txt
+++ b/third_party/blink/web_tests/platform/android/external/wpt/serial/idlharness.https.any.worker-expected.txt
@@ -1,73 +1,5 @@
 This is a testharness.js-based test.
-Found 35 FAIL, 0 TIMEOUT, 0 NOTRUN.
-[FAIL] Serial interface: existence and properties of interface object
-  assert_own_property: self does not have own property "Serial" expected property "Serial" missing
-[FAIL] Serial interface object length
-  assert_own_property: self does not have own property "Serial" expected property "Serial" missing
-[FAIL] Serial interface object name
-  assert_own_property: self does not have own property "Serial" expected property "Serial" missing
-[FAIL] Serial interface: existence and properties of interface prototype object
-  assert_own_property: self does not have own property "Serial" expected property "Serial" missing
-[FAIL] Serial interface: existence and properties of interface prototype object's "constructor" property
-  assert_own_property: self does not have own property "Serial" expected property "Serial" missing
-[FAIL] Serial interface: existence and properties of interface prototype object's @@unscopables property
-  assert_own_property: self does not have own property "Serial" expected property "Serial" missing
-[FAIL] Serial interface: attribute onconnect
-  assert_own_property: self does not have own property "Serial" expected property "Serial" missing
-[FAIL] Serial interface: attribute ondisconnect
-  assert_own_property: self does not have own property "Serial" expected property "Serial" missing
-[FAIL] Serial interface: operation getPorts()
-  assert_own_property: self does not have own property "Serial" expected property "Serial" missing
-[FAIL] Serial interface: member requestPort
-  Cannot use 'in' operator to search for 'requestPort' in undefined
-[FAIL] Serial must be primary interface of navigator.serial
-  assert_equals: wrong typeof object expected "object" but got "undefined"
-[FAIL] Stringification of navigator.serial
-  assert_equals: wrong typeof object expected "object" but got "undefined"
-[FAIL] Serial interface: navigator.serial must inherit property "onconnect" with the proper type
-  assert_equals: wrong typeof object expected "object" but got "undefined"
-[FAIL] Serial interface: navigator.serial must inherit property "ondisconnect" with the proper type
-  assert_equals: wrong typeof object expected "object" but got "undefined"
-[FAIL] Serial interface: navigator.serial must inherit property "getPorts()" with the proper type
-  assert_equals: wrong typeof object expected "object" but got "undefined"
-[FAIL] Serial interface: navigator.serial must not have property "requestPort"
-  Cannot use 'in' operator to search for 'requestPort' in undefined
-[FAIL] SerialPort interface: existence and properties of interface object
-  assert_own_property: self does not have own property "SerialPort" expected property "SerialPort" missing
-[FAIL] SerialPort interface object length
-  assert_own_property: self does not have own property "SerialPort" expected property "SerialPort" missing
-[FAIL] SerialPort interface object name
-  assert_own_property: self does not have own property "SerialPort" expected property "SerialPort" missing
-[FAIL] SerialPort interface: existence and properties of interface prototype object
-  assert_own_property: self does not have own property "SerialPort" expected property "SerialPort" missing
-[FAIL] SerialPort interface: existence and properties of interface prototype object's "constructor" property
-  assert_own_property: self does not have own property "SerialPort" expected property "SerialPort" missing
-[FAIL] SerialPort interface: existence and properties of interface prototype object's @@unscopables property
-  assert_own_property: self does not have own property "SerialPort" expected property "SerialPort" missing
-[FAIL] SerialPort interface: attribute onconnect
-  assert_own_property: self does not have own property "SerialPort" expected property "SerialPort" missing
-[FAIL] SerialPort interface: attribute ondisconnect
-  assert_own_property: self does not have own property "SerialPort" expected property "SerialPort" missing
+Found 1 FAIL, 0 TIMEOUT, 0 NOTRUN.
 [FAIL] SerialPort interface: attribute connected
-  assert_own_property: self does not have own property "SerialPort" expected property "SerialPort" missing
-[FAIL] SerialPort interface: attribute readable
-  assert_own_property: self does not have own property "SerialPort" expected property "SerialPort" missing
-[FAIL] SerialPort interface: attribute writable
-  assert_own_property: self does not have own property "SerialPort" expected property "SerialPort" missing
-[FAIL] SerialPort interface: operation getInfo()
-  assert_own_property: self does not have own property "SerialPort" expected property "SerialPort" missing
-[FAIL] SerialPort interface: operation open(SerialOptions)
-  assert_own_property: self does not have own property "SerialPort" expected property "SerialPort" missing
-[FAIL] SerialPort interface: operation setSignals(optional SerialOutputSignals)
-  assert_own_property: self does not have own property "SerialPort" expected property "SerialPort" missing
-[FAIL] SerialPort interface: operation getSignals()
-  assert_own_property: self does not have own property "SerialPort" expected property "SerialPort" missing
-[FAIL] SerialPort interface: operation close()
-  assert_own_property: self does not have own property "SerialPort" expected property "SerialPort" missing
-[FAIL] SerialPort interface: operation forget()
-  assert_own_property: self does not have own property "SerialPort" expected property "SerialPort" missing
-[FAIL] WorkerNavigator interface: attribute serial
-  assert_true: The prototype object must have a property "serial" expected true got false
-[FAIL] WorkerNavigator interface: navigator must inherit property "serial" with the proper type
-  assert_inherits: property "serial" not found in prototype chain
+  assert_true: The prototype object must have a property "connected" expected true got false
 Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/mac-mac11/external/wpt/html/browsers/browsing-the-web/history-traversal/event-order/before-load-hash-twice-expected.txt b/third_party/blink/web_tests/platform/mac-mac11/external/wpt/html/browsers/browsing-the-web/history-traversal/event-order/before-load-hash-twice-expected.txt
deleted file mode 100644
index 5b37deb..0000000
--- a/third_party/blink/web_tests/platform/mac-mac11/external/wpt/html/browsers/browsing-the-web/history-traversal/event-order/before-load-hash-twice-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-All subtests passed and are omitted for brevity.
-See https://chromium.googlesource.com/chromium/src/+/HEAD/docs/testing/writing_web_tests.md#Text-Test-Baselines for details.
-Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/virtual/customizable-select-disabled/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-input-keyboard-behavior.tentative-expected.txt b/third_party/blink/web_tests/virtual/customizable-select-disabled/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-input-keyboard-behavior.tentative-expected.txt
new file mode 100644
index 0000000..3fde5cc
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/customizable-select-disabled/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-input-keyboard-behavior.tentative-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+Harness Error. harness_status.status = 1 , harness_status.message = Uncaught TypeError: Cannot read properties of null (reading 'appendChild')
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/prefetch/external/wpt/speculation-rules/prefetch/tentative/service-worker/redirect.sub.https_origin=same-site_sc=in-in_sw=fetch-handler-expected.txt b/third_party/blink/web_tests/virtual/prefetch/external/wpt/speculation-rules/prefetch/tentative/service-worker/redirect.sub.https_origin=same-site_sc=in-in_sw=fetch-handler-expected.txt
new file mode 100644
index 0000000..042227b9
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/prefetch/external/wpt/speculation-rules/prefetch/tentative/service-worker/redirect.sub.https_origin=same-site_sc=in-in_sw=fetch-handler-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+[FAIL] Prefetch with ServiceWorker (fetch-handler)
+  assert_equals: prefetch requests to initial URL should be sent to the server. expected 1 but got 0
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/prefetch/external/wpt/speculation-rules/prefetch/tentative/service-worker/redirect.sub.https_origin=same-site_sc=in-in_sw=fetch-handler-to-fallback-expected.txt b/third_party/blink/web_tests/virtual/prefetch/external/wpt/speculation-rules/prefetch/tentative/service-worker/redirect.sub.https_origin=same-site_sc=in-in_sw=fetch-handler-to-fallback-expected.txt
new file mode 100644
index 0000000..6583f56
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/prefetch/external/wpt/speculation-rules/prefetch/tentative/service-worker/redirect.sub.https_origin=same-site_sc=in-in_sw=fetch-handler-to-fallback-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+[FAIL] Prefetch with ServiceWorker (fetch-handler-to-fallback)
+  assert_equals: prefetch requests to initial URL should be sent to the server. expected 1 but got 0
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/prefetch/external/wpt/speculation-rules/prefetch/tentative/service-worker/redirect.sub.https_origin=same-site_sc=in-in_sw=no-fetch-handler-expected.txt b/third_party/blink/web_tests/virtual/prefetch/external/wpt/speculation-rules/prefetch/tentative/service-worker/redirect.sub.https_origin=same-site_sc=in-in_sw=no-fetch-handler-expected.txt
new file mode 100644
index 0000000..7c599b4
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/prefetch/external/wpt/speculation-rules/prefetch/tentative/service-worker/redirect.sub.https_origin=same-site_sc=in-in_sw=no-fetch-handler-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+[FAIL] Prefetch with ServiceWorker (no-fetch-handler)
+  assert_equals: prefetch requests to initial URL should be sent to the server. expected 1 but got 0
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/prefetch/external/wpt/speculation-rules/prefetch/tentative/service-worker/redirect.sub.https_origin=same-site_sc=in-out_sw=fetch-handler-expected.txt b/third_party/blink/web_tests/virtual/prefetch/external/wpt/speculation-rules/prefetch/tentative/service-worker/redirect.sub.https_origin=same-site_sc=in-out_sw=fetch-handler-expected.txt
new file mode 100644
index 0000000..042227b9
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/prefetch/external/wpt/speculation-rules/prefetch/tentative/service-worker/redirect.sub.https_origin=same-site_sc=in-out_sw=fetch-handler-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+[FAIL] Prefetch with ServiceWorker (fetch-handler)
+  assert_equals: prefetch requests to initial URL should be sent to the server. expected 1 but got 0
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/prefetch/external/wpt/speculation-rules/prefetch/tentative/service-worker/redirect.sub.https_origin=same-site_sc=in-out_sw=fetch-handler-to-fallback-expected.txt b/third_party/blink/web_tests/virtual/prefetch/external/wpt/speculation-rules/prefetch/tentative/service-worker/redirect.sub.https_origin=same-site_sc=in-out_sw=fetch-handler-to-fallback-expected.txt
new file mode 100644
index 0000000..6583f56
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/prefetch/external/wpt/speculation-rules/prefetch/tentative/service-worker/redirect.sub.https_origin=same-site_sc=in-out_sw=fetch-handler-to-fallback-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+[FAIL] Prefetch with ServiceWorker (fetch-handler-to-fallback)
+  assert_equals: prefetch requests to initial URL should be sent to the server. expected 1 but got 0
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/prefetch/external/wpt/speculation-rules/prefetch/tentative/service-worker/redirect.sub.https_origin=same-site_sc=in-out_sw=no-fetch-handler-expected.txt b/third_party/blink/web_tests/virtual/prefetch/external/wpt/speculation-rules/prefetch/tentative/service-worker/redirect.sub.https_origin=same-site_sc=in-out_sw=no-fetch-handler-expected.txt
new file mode 100644
index 0000000..7c599b4
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/prefetch/external/wpt/speculation-rules/prefetch/tentative/service-worker/redirect.sub.https_origin=same-site_sc=in-out_sw=no-fetch-handler-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+[FAIL] Prefetch with ServiceWorker (no-fetch-handler)
+  assert_equals: prefetch requests to initial URL should be sent to the server. expected 1 but got 0
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/select-parser-relaxation-opt-out/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-input-keyboard-behavior.tentative-expected.txt b/third_party/blink/web_tests/virtual/select-parser-relaxation-opt-out/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-input-keyboard-behavior.tentative-expected.txt
new file mode 100644
index 0000000..3fde5cc
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/select-parser-relaxation-opt-out/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-input-keyboard-behavior.tentative-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+Harness Error. harness_status.status = 1 , harness_status.message = Uncaught TypeError: Cannot read properties of null (reading 'appendChild')
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/select-parser-relaxation/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-input-keyboard-behavior.tentative-expected.txt b/third_party/blink/web_tests/virtual/select-parser-relaxation/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-input-keyboard-behavior.tentative-expected.txt
new file mode 100644
index 0000000..e3f13fc
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/select-parser-relaxation/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-input-keyboard-behavior.tentative-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+[FAIL] Keyboard behavior of <input> in <select>.
+  assert_equals: appearance: base-select must be supported for this test to run. expected "base-select" but got "auto"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-properties-after-frame-navigated-expected.txt b/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-properties-after-frame-navigated-expected.txt
index 71375285..78be227 100644
--- a/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-properties-after-frame-navigated-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-properties-after-frame-navigated-expected.txt
@@ -134,6 +134,7 @@
 PASS window.cached_styleMedia.type is ''
 PASS window.cached_toolbar.visible is false
 PASS window.cached_trustedTypes.defaultPolicy is null
+PASS window.cached_viewport.segments is null
 PASS window.cached_visualViewport.height is 0
 PASS window.cached_visualViewport.offsetLeft is 0
 PASS window.cached_visualViewport.offsetTop is 0
diff --git a/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-properties-after-frame-removed-and-gced-expected.txt b/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-properties-after-frame-removed-and-gced-expected.txt
index ad60be25..e616044d 100644
--- a/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-properties-after-frame-removed-and-gced-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-properties-after-frame-removed-and-gced-expected.txt
@@ -134,6 +134,7 @@
 PASS window.cached_styleMedia.type is ''
 PASS window.cached_toolbar.visible is false
 PASS window.cached_trustedTypes.defaultPolicy is null
+PASS window.cached_viewport.segments is null
 PASS window.cached_visualViewport.height is 0
 PASS window.cached_visualViewport.offsetLeft is 0
 PASS window.cached_visualViewport.offsetTop is 0
diff --git a/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-properties-after-frame-removed-expected.txt b/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-properties-after-frame-removed-expected.txt
index 8d77c5c..a2791361 100644
--- a/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-properties-after-frame-removed-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-properties-after-frame-removed-expected.txt
@@ -134,6 +134,7 @@
 PASS window.cached_styleMedia.type is ''
 PASS window.cached_toolbar.visible is false
 PASS window.cached_trustedTypes.defaultPolicy is null
+PASS window.cached_viewport.segments is null
 PASS window.cached_visualViewport.height is 0
 PASS window.cached_visualViewport.offsetLeft is 0
 PASS window.cached_visualViewport.offsetTop is 0
diff --git a/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt b/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt
index ad21b7a2..9dd9a87 100644
--- a/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt
@@ -284,6 +284,7 @@
 PASS oldChildWindow.styleMedia.type is newChildWindow.styleMedia.type
 PASS oldChildWindow.toolbar.visible is newChildWindow.toolbar.visible
 PASS oldChildWindow.trustedTypes.defaultPolicy is newChildWindow.trustedTypes.defaultPolicy
+PASS oldChildWindow.viewport.segments is newChildWindow.viewport.segments
 PASS oldChildWindow.visualViewport.height is newChildWindow.visualViewport.height
 PASS oldChildWindow.visualViewport.offsetLeft is newChildWindow.visualViewport.offsetLeft
 PASS oldChildWindow.visualViewport.offsetTop is newChildWindow.visualViewport.offsetTop
diff --git a/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-window-after-frame-removed-and-gced-expected.txt b/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-window-after-frame-removed-and-gced-expected.txt
index 81eac550..3169310 100644
--- a/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-window-after-frame-removed-and-gced-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-window-after-frame-removed-and-gced-expected.txt
@@ -215,6 +215,7 @@
 PASS childWindow.styleMedia.type is ''
 PASS childWindow.toolbar.visible is false
 PASS childWindow.trustedTypes.defaultPolicy is null
+PASS childWindow.viewport.segments is null
 PASS childWindow.visualViewport.height is 0
 PASS childWindow.visualViewport.offsetLeft is 0
 PASS childWindow.visualViewport.offsetTop is 0
diff --git a/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-window-after-frame-removed-expected.txt b/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-window-after-frame-removed-expected.txt
index 518adb2..484ae5fd 100644
--- a/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-window-after-frame-removed-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-window-after-frame-removed-expected.txt
@@ -215,6 +215,7 @@
 PASS childWindow.styleMedia.type is ''
 PASS childWindow.toolbar.visible is false
 PASS childWindow.trustedTypes.defaultPolicy is null
+PASS childWindow.viewport.segments is null
 PASS childWindow.visualViewport.height is 0
 PASS childWindow.visualViewport.offsetLeft is 0
 PASS childWindow.visualViewport.offsetTop is 0
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
index 62ae0652..e9cccf5 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -5229,6 +5229,14 @@
     getter isComposing
     method constructor
     method getTargetRanges
+interface IntegrityViolationReportBody : ReportBody
+    attribute @@toStringTag
+    getter blockedURL
+    getter destination
+    getter documentURL
+    getter reportOnly
+    method constructor
+    method toJSON
 interface IntersectionObserver
     attribute @@toStringTag
     getter delay
@@ -9702,6 +9710,10 @@
     method has
     method keys
     method values
+interface Viewport
+    attribute @@toStringTag
+    getter segments
+    method constructor
 interface VirtualKeyboard : EventTarget
     attribute @@toStringTag
     getter boundingRect
@@ -12132,6 +12144,7 @@
     getter toolbar
     getter top
     getter trustedTypes
+    getter viewport
     getter visualViewport
     getter window
     method NodeFilter
@@ -12346,6 +12359,7 @@
     setter status
     setter statusbar
     setter toolbar
+    setter viewport
     setter visualViewport
 PASS successfullyParsed is true
 
diff --git a/third_party/blink/web_tests/wpt_internal/webxr/resources/xr-internal-device-mocking.js b/third_party/blink/web_tests/wpt_internal/webxr/resources/xr-internal-device-mocking.js
index 78a155f..3e6a528 100644
--- a/third_party/blink/web_tests/wpt_internal/webxr/resources/xr-internal-device-mocking.js
+++ b/third_party/blink/web_tests/wpt_internal/webxr/resources/xr-internal-device-mocking.js
@@ -49,12 +49,13 @@
     reflectionProbe: {
       cubeMap: {
         widthAndHeight: 16,
-        positiveX: new Array(16 * 16).fill({ red: 0, green: 0, blue: 0, alpha: 0 }),
-        negativeX: new Array(16 * 16).fill({ red: 0, green: 0, blue: 0, alpha: 0 }),
-        positiveY: new Array(16 * 16).fill({ red: 0, green: 0, blue: 0, alpha: 0 }),
-        negativeY: new Array(16 * 16).fill({ red: 0, green: 0, blue: 0, alpha: 0 }),
-        positiveZ: new Array(16 * 16).fill({ red: 0, green: 0, blue: 0, alpha: 0 }),
-        negativeZ: new Array(16 * 16).fill({ red: 0, green: 0, blue: 0, alpha: 0 }),
+        // Arrays should be widthAndHeight^2 * 4 channels per pixel (r, g, b, a).
+        positiveX: new Array(16 * 16 * 4).fill(0),
+        negativeX: new Array(16 * 16 * 4).fill(0),
+        positiveY: new Array(16 * 16 * 4).fill(0),
+        negativeY: new Array(16 * 16 * 4).fill(0),
+        positiveZ: new Array(16 * 16 * 4).fill(0),
+        negativeZ: new Array(16 * 16 * 4).fill(0),
       },
     },
   };
diff --git a/third_party/catapult b/third_party/catapult
index 4a83985..52ad7cf 160000
--- a/third_party/catapult
+++ b/third_party/catapult
@@ -1 +1 @@
-Subproject commit 4a839855c5e79d91689784e2c7284f3f6288123b
+Subproject commit 52ad7cf544050f01f47c070716b9dae8eb9fab2b
diff --git a/third_party/crashpad/crashpad/test/ios/crash_type_xctest.mm b/third_party/crashpad/crashpad/test/ios/crash_type_xctest.mm
index 1c92fb6..b6d1bff 100644
--- a/third_party/crashpad/crashpad/test/ios/crash_type_xctest.mm
+++ b/third_party/crashpad/crashpad/test/ios/crash_type_xctest.mm
@@ -356,7 +356,8 @@
 }
 #endif
 
-- (void)testCrashWithAnnotations {
+// TODO(crbug.com/416140624): The test is flaky. Enabled the test.
+- (void)DISABLED_testCrashWithAnnotations {
 #if TARGET_OS_SIMULATOR
   // This test will fail on <iOS17 simulators when running on macOS >=14.3 or
   // <iOS18 simulators when running on macOS >=15.4 due to a bug in Simulator.
diff --git a/third_party/crossbench b/third_party/crossbench
index 6f0c318..47e5e3d 160000
--- a/third_party/crossbench
+++ b/third_party/crossbench
@@ -1 +1 @@
-Subproject commit 6f0c3181a39f6fdfbaea35689d8126e0361da071
+Subproject commit 47e5e3d994ddcc2319cec110ce670ccc1baa3223
diff --git a/third_party/dawn b/third_party/dawn
index 0f24b33..968646ae 160000
--- a/third_party/dawn
+++ b/third_party/dawn
@@ -1 +1 @@
-Subproject commit 0f24b334d63efffa544d1c2fc6d779576a3aa5e9
+Subproject commit 968646ae6a99b1751d5a7a4298e3bf6976123326
diff --git a/third_party/depot_tools b/third_party/depot_tools
index 30d9438..18580cf 160000
--- a/third_party/depot_tools
+++ b/third_party/depot_tools
@@ -1 +1 @@
-Subproject commit 30d9438e7baf0c045f973de2f517b51554d14d7d
+Subproject commit 18580cf9c407a3c23aa072b9d745832516de699e
diff --git a/third_party/devtools-frontend/src b/third_party/devtools-frontend/src
index 682af1f..28fbee1 160000
--- a/third_party/devtools-frontend/src
+++ b/third_party/devtools-frontend/src
@@ -1 +1 @@
-Subproject commit 682af1f84e33247088b8ca38e189f734ff9923ef
+Subproject commit 28fbee169c8a26c6c2ed2ec2550465fe3d697a34
diff --git a/third_party/libc++/src b/third_party/libc++/src
index a01c02c..a9cc573 160000
--- a/third_party/libc++/src
+++ b/third_party/libc++/src
@@ -1 +1 @@
-Subproject commit a01c02c9d4acbdae3b7e8a2f3ee58579a9c29f96
+Subproject commit a9cc573e7c591795d11b72d8323ba0e573b55bd6
diff --git a/third_party/libxslt/chromium/change-atype-extra.patch b/third_party/libxslt/chromium/change-atype-extra.patch
new file mode 100644
index 0000000..9e2f666
--- /dev/null
+++ b/third_party/libxslt/chromium/change-atype-extra.patch
@@ -0,0 +1,110 @@
+diff --git a/third_party/libxslt/src/libxslt/transform.c b/third_party/libxslt/src/libxslt/transform.c
+index 54ef821b5016f..1ac2471d6441b 100644
+--- a/third_party/libxslt/src/libxslt/transform.c
++++ b/third_party/libxslt/src/libxslt/transform.c
+@@ -5772,7 +5772,8 @@ xsltCleanupSourceDoc(xmlDocPtr doc) {
+             xmlAttrPtr prop = cur->properties;
+ 
+             while (prop) {
+-                prop->atype &= ~(XSLT_SOURCE_NODE_MASK << 27);
++                prop->extra &=
++                    ~(XSLT_SOURCE_NODE_MASK << XSLT_SOURCE_NODE_SHIFT_32);
+                 prop->psvi = NULL;
+                 prop = prop->next;
+             }
+diff --git a/third_party/libxslt/src/libxslt/xsltutils.c b/third_party/libxslt/src/libxslt/xsltutils.c
+index a20da96182289..0155e7fc5a89f 100644
+--- a/third_party/libxslt/src/libxslt/xsltutils.c
++++ b/third_party/libxslt/src/libxslt/xsltutils.c
+@@ -1920,26 +1920,26 @@ xsltSaveResultToString(xmlChar **doc_txt_ptr, int * doc_txt_len,
+ int
+ xsltGetSourceNodeFlags(xmlNodePtr node) {
+     /*
+-     * Squeeze the bit flags into the upper bits of
++     * Squeeze the bit flags into the upper 4 bits of
+      *
+-     * - 'int properties' member in struct _xmlDoc
+-     * - 'xmlAttributeType atype' member in struct _xmlAttr
++     * - 'unsigned int extra' member in struct _xmlDoc
++     * - 'unsigned int extra' member in struct _xmlAttr
+      * - 'unsigned short extra' member in struct _xmlNode
+      */
+     switch (node->type) {
+         case XML_DOCUMENT_NODE:
+         case XML_HTML_DOCUMENT_NODE:
+-            return ((xmlDocPtr) node)->properties >> 27;
++            return ((xmlDocPtr) node)->extra >> XSLT_SOURCE_NODE_SHIFT_32;
+ 
+         case XML_ATTRIBUTE_NODE:
+-            return ((xmlAttrPtr) node)->atype >> 27;
++            return ((xmlAttrPtr) node)->extra >> XSLT_SOURCE_NODE_SHIFT_32;
+ 
+         case XML_ELEMENT_NODE:
+         case XML_TEXT_NODE:
+         case XML_CDATA_SECTION_NODE:
+         case XML_PI_NODE:
+         case XML_COMMENT_NODE:
+-            return node->extra >> 12;
++            return node->extra >> XSLT_SOURCE_NODE_SHIFT_32;
+ 
+         default:
+             return 0;
+@@ -1964,11 +1964,11 @@ xsltSetSourceNodeFlags(xsltTransformContextPtr ctxt, xmlNodePtr node,
+     switch (node->type) {
+         case XML_DOCUMENT_NODE:
+         case XML_HTML_DOCUMENT_NODE:
+-            ((xmlDocPtr) node)->properties |= flags << 27;
++            ((xmlDocPtr) node)->extra |= (flags << XSLT_SOURCE_NODE_SHIFT_32);
+             return 0;
+ 
+         case XML_ATTRIBUTE_NODE:
+-            ((xmlAttrPtr) node)->atype |= flags << 27;
++            ((xmlAttrPtr) node)->extra |= (flags << XSLT_SOURCE_NODE_SHIFT_32);
+             return 0;
+ 
+         case XML_ELEMENT_NODE:
+@@ -1976,7 +1976,7 @@ xsltSetSourceNodeFlags(xsltTransformContextPtr ctxt, xmlNodePtr node,
+         case XML_CDATA_SECTION_NODE:
+         case XML_PI_NODE:
+         case XML_COMMENT_NODE:
+-            node->extra |= flags << 12;
++            node->extra |= (flags << XSLT_SOURCE_NODE_SHIFT_16);
+             return 0;
+ 
+         default:
+@@ -1998,11 +1998,11 @@ xsltClearSourceNodeFlags(xmlNodePtr node, int flags) {
+     switch (node->type) {
+         case XML_DOCUMENT_NODE:
+         case XML_HTML_DOCUMENT_NODE:
+-            ((xmlDocPtr) node)->properties &= ~(flags << 27);
++            ((xmlDocPtr) node)->extra &= ~(flags << XSLT_SOURCE_NODE_SHIFT_32);
+             return 0;
+ 
+         case XML_ATTRIBUTE_NODE:
+-            ((xmlAttrPtr) node)->atype &= ~(flags << 27);
++            ((xmlAttrPtr) node)->extra &= ~(flags << XSLT_SOURCE_NODE_SHIFT_32);
+             return 0;
+ 
+         case XML_ELEMENT_NODE:
+@@ -2010,7 +2010,7 @@ xsltClearSourceNodeFlags(xmlNodePtr node, int flags) {
+         case XML_CDATA_SECTION_NODE:
+         case XML_PI_NODE:
+         case XML_COMMENT_NODE:
+-            node->extra &= ~(flags << 12);
++            node->extra &= ~(flags << XSLT_SOURCE_NODE_SHIFT_16);
+             return 0;
+ 
+         default:
+diff --git a/third_party/libxslt/src/libxslt/xsltutils.h b/third_party/libxslt/src/libxslt/xsltutils.h
+index 2514774b3f11a..d15d22726afaa 100644
+--- a/third_party/libxslt/src/libxslt/xsltutils.h
++++ b/third_party/libxslt/src/libxslt/xsltutils.h
+@@ -261,6 +261,8 @@ XSLTPUBFUN xmlXPathCompExprPtr XSLTCALL
+ #define XSLT_SOURCE_NODE_MASK       15u
+ #define XSLT_SOURCE_NODE_HAS_KEY    1u
+ #define XSLT_SOURCE_NODE_HAS_ID     2u
++#define XSLT_SOURCE_NODE_SHIFT_16   12u
++#define XSLT_SOURCE_NODE_SHIFT_32   28u
+ int
+ xsltGetSourceNodeFlags(xmlNodePtr node);
+ int
diff --git a/third_party/libxslt/chromium/change-id.patch b/third_party/libxslt/chromium/change-id.patch
new file mode 100644
index 0000000..b151abb
--- /dev/null
+++ b/third_party/libxslt/chromium/change-id.patch
@@ -0,0 +1,114 @@
+diff --git a/third_party/libxslt/src/libxslt/functions.c b/third_party/libxslt/src/libxslt/functions.c
+index 72a58dc4d6592..309af458c22f7 100644
+--- a/third_party/libxslt/src/libxslt/functions.c
++++ b/third_party/libxslt/src/libxslt/functions.c
+@@ -760,7 +760,7 @@ xsltGenerateIdFunction(xmlXPathParserContextPtr ctxt, int nargs){
+     }
+ 
+     if (xsltGetSourceNodeFlags(cur) & XSLT_SOURCE_NODE_HAS_ID) {
+-        id = (unsigned long) (size_t) *psviPtr;
++        id = (unsigned long) xsltGetSourceNodeValue(cur);
+     } else {
+         if (cur->type == XML_TEXT_NODE && cur->line == USHRT_MAX) {
+             /* Text nodes store big line numbers in psvi. */
+@@ -772,7 +772,7 @@ xsltGenerateIdFunction(xmlXPathParserContextPtr ctxt, int nargs){
+             goto out;
+         }
+ 
+-        if (tctxt->currentId == ULONG_MAX) {
++        if (tctxt->currentId == XSLT_SOURCE_NODE_VALUE_MAX) {
+             xsltTransformError(tctxt, NULL, NULL,
+                     "generate-id(): id overflow\n");
+             ctxt->error = XPATH_MEMORY_ERROR;
+@@ -780,7 +780,7 @@ xsltGenerateIdFunction(xmlXPathParserContextPtr ctxt, int nargs){
+         }
+ 
+         id = ++tctxt->currentId;
+-        *psviPtr = (void *) (size_t) id;
++        xsltSetSourceNodeValue(cur, id);
+         xsltSetSourceNodeFlags(tctxt, cur, XSLT_SOURCE_NODE_HAS_ID);
+     }
+ 
+diff --git a/third_party/libxslt/src/libxslt/xsltutils.c b/third_party/libxslt/src/libxslt/xsltutils.c
+index 0155e7fc5a89f..aec8be4ad077c 100644
+--- a/third_party/libxslt/src/libxslt/xsltutils.c
++++ b/third_party/libxslt/src/libxslt/xsltutils.c
+@@ -2018,6 +2018,54 @@ xsltClearSourceNodeFlags(xmlNodePtr node, int flags) {
+     }
+ }
+ 
++/**
++ * xsltGetSourceNodeValue:
++ * @node:  Node from source document
++ *
++ * Returns the associated 28 bit unsigned value for a source node,
++ * or 0 if node does not have an associated value.
++ */
++int
++xsltGetSourceNodeValue(xmlNodePtr node) {
++    switch (node->type) {
++        case XML_DOCUMENT_NODE:
++        case XML_HTML_DOCUMENT_NODE:
++            return (((xmlDocPtr) node)->extra & XSLT_SOURCE_NODE_VALUE_MASK);
++
++        case XML_ATTRIBUTE_NODE:
++            return (((xmlAttrPtr) node)->extra & XSLT_SOURCE_NODE_VALUE_MASK);
++
++        default:
++            return 0;
++    }
++}
++
++/**
++ * xsltSetSourceNodeValue:
++ * @node:  Node from source document
++ * @value:  28 bit unsigned value to associate with the node.
++ *
++ * Returns 0 on success, -1 on error.
++ */
++int
++xsltSetSourceNodeValue(xmlNodePtr node, int value) {
++    switch (node->type) {
++        case XML_DOCUMENT_NODE:
++        case XML_HTML_DOCUMENT_NODE:
++            ((xmlDocPtr) node)->extra &= ~XSLT_SOURCE_NODE_VALUE_MASK;
++            ((xmlDocPtr) node)->extra |= (value & XSLT_SOURCE_NODE_VALUE_MASK);
++            return 0;
++
++        case XML_ATTRIBUTE_NODE:
++            ((xmlAttrPtr) node)->extra &= ~XSLT_SOURCE_NODE_VALUE_MASK;
++            ((xmlAttrPtr) node)->extra |= (value & XSLT_SOURCE_NODE_VALUE_MASK);
++            return 0;
++
++        default:
++            return -1;
++    }
++}
++
+ /**
+  * xsltGetPSVIPtr:
+  * @cur:  Node
+diff --git a/third_party/libxslt/src/libxslt/xsltutils.h b/third_party/libxslt/src/libxslt/xsltutils.h
+index d15d22726afaa..1e753eebadd98 100644
+--- a/third_party/libxslt/src/libxslt/xsltutils.h
++++ b/third_party/libxslt/src/libxslt/xsltutils.h
+@@ -263,6 +263,8 @@ XSLTPUBFUN xmlXPathCompExprPtr XSLTCALL
+ #define XSLT_SOURCE_NODE_HAS_ID     2u
+ #define XSLT_SOURCE_NODE_SHIFT_16   12u
+ #define XSLT_SOURCE_NODE_SHIFT_32   28u
++#define XSLT_SOURCE_NODE_VALUE_MASK ((1 << XSLT_SOURCE_NODE_SHIFT_32) - 1)
++#define XSLT_SOURCE_NODE_VALUE_MAX  XSLT_SOURCE_NODE_VALUE_MASK
+ int
+ xsltGetSourceNodeFlags(xmlNodePtr node);
+ int
+@@ -270,6 +272,10 @@ xsltSetSourceNodeFlags(xsltTransformContextPtr ctxt, xmlNodePtr node,
+                        int flags);
+ int
+ xsltClearSourceNodeFlags(xmlNodePtr node, int flags);
++int
++xsltSetSourceNodeValue(xmlNodePtr node, int value);
++int
++xsltGetSourceNodeValue(xmlNodePtr node);
+ void **
+ xsltGetPSVIPtr(xmlNodePtr cur);
+ /** DOC_ENABLE */
diff --git a/third_party/libxslt/src/libxslt/functions.c b/third_party/libxslt/src/libxslt/functions.c
index 72a58dc..309af45 100644
--- a/third_party/libxslt/src/libxslt/functions.c
+++ b/third_party/libxslt/src/libxslt/functions.c
@@ -760,7 +760,7 @@
     }
 
     if (xsltGetSourceNodeFlags(cur) & XSLT_SOURCE_NODE_HAS_ID) {
-        id = (unsigned long) (size_t) *psviPtr;
+        id = (unsigned long) xsltGetSourceNodeValue(cur);
     } else {
         if (cur->type == XML_TEXT_NODE && cur->line == USHRT_MAX) {
             /* Text nodes store big line numbers in psvi. */
@@ -772,7 +772,7 @@
             goto out;
         }
 
-        if (tctxt->currentId == ULONG_MAX) {
+        if (tctxt->currentId == XSLT_SOURCE_NODE_VALUE_MAX) {
             xsltTransformError(tctxt, NULL, NULL,
                     "generate-id(): id overflow\n");
             ctxt->error = XPATH_MEMORY_ERROR;
@@ -780,7 +780,7 @@
         }
 
         id = ++tctxt->currentId;
-        *psviPtr = (void *) (size_t) id;
+        xsltSetSourceNodeValue(cur, id);
         xsltSetSourceNodeFlags(tctxt, cur, XSLT_SOURCE_NODE_HAS_ID);
     }
 
diff --git a/third_party/libxslt/src/libxslt/transform.c b/third_party/libxslt/src/libxslt/transform.c
index 54ef821..1ac2471 100644
--- a/third_party/libxslt/src/libxslt/transform.c
+++ b/third_party/libxslt/src/libxslt/transform.c
@@ -5772,7 +5772,8 @@
             xmlAttrPtr prop = cur->properties;
 
             while (prop) {
-                prop->atype &= ~(XSLT_SOURCE_NODE_MASK << 27);
+                prop->extra &=
+                    ~(XSLT_SOURCE_NODE_MASK << XSLT_SOURCE_NODE_SHIFT_32);
                 prop->psvi = NULL;
                 prop = prop->next;
             }
diff --git a/third_party/libxslt/src/libxslt/xsltutils.c b/third_party/libxslt/src/libxslt/xsltutils.c
index a20da96..aec8be4a 100644
--- a/third_party/libxslt/src/libxslt/xsltutils.c
+++ b/third_party/libxslt/src/libxslt/xsltutils.c
@@ -1920,26 +1920,26 @@
 int
 xsltGetSourceNodeFlags(xmlNodePtr node) {
     /*
-     * Squeeze the bit flags into the upper bits of
+     * Squeeze the bit flags into the upper 4 bits of
      *
-     * - 'int properties' member in struct _xmlDoc
-     * - 'xmlAttributeType atype' member in struct _xmlAttr
+     * - 'unsigned int extra' member in struct _xmlDoc
+     * - 'unsigned int extra' member in struct _xmlAttr
      * - 'unsigned short extra' member in struct _xmlNode
      */
     switch (node->type) {
         case XML_DOCUMENT_NODE:
         case XML_HTML_DOCUMENT_NODE:
-            return ((xmlDocPtr) node)->properties >> 27;
+            return ((xmlDocPtr) node)->extra >> XSLT_SOURCE_NODE_SHIFT_32;
 
         case XML_ATTRIBUTE_NODE:
-            return ((xmlAttrPtr) node)->atype >> 27;
+            return ((xmlAttrPtr) node)->extra >> XSLT_SOURCE_NODE_SHIFT_32;
 
         case XML_ELEMENT_NODE:
         case XML_TEXT_NODE:
         case XML_CDATA_SECTION_NODE:
         case XML_PI_NODE:
         case XML_COMMENT_NODE:
-            return node->extra >> 12;
+            return node->extra >> XSLT_SOURCE_NODE_SHIFT_32;
 
         default:
             return 0;
@@ -1964,11 +1964,11 @@
     switch (node->type) {
         case XML_DOCUMENT_NODE:
         case XML_HTML_DOCUMENT_NODE:
-            ((xmlDocPtr) node)->properties |= flags << 27;
+            ((xmlDocPtr) node)->extra |= (flags << XSLT_SOURCE_NODE_SHIFT_32);
             return 0;
 
         case XML_ATTRIBUTE_NODE:
-            ((xmlAttrPtr) node)->atype |= flags << 27;
+            ((xmlAttrPtr) node)->extra |= (flags << XSLT_SOURCE_NODE_SHIFT_32);
             return 0;
 
         case XML_ELEMENT_NODE:
@@ -1976,7 +1976,7 @@
         case XML_CDATA_SECTION_NODE:
         case XML_PI_NODE:
         case XML_COMMENT_NODE:
-            node->extra |= flags << 12;
+            node->extra |= (flags << XSLT_SOURCE_NODE_SHIFT_16);
             return 0;
 
         default:
@@ -1998,11 +1998,11 @@
     switch (node->type) {
         case XML_DOCUMENT_NODE:
         case XML_HTML_DOCUMENT_NODE:
-            ((xmlDocPtr) node)->properties &= ~(flags << 27);
+            ((xmlDocPtr) node)->extra &= ~(flags << XSLT_SOURCE_NODE_SHIFT_32);
             return 0;
 
         case XML_ATTRIBUTE_NODE:
-            ((xmlAttrPtr) node)->atype &= ~(flags << 27);
+            ((xmlAttrPtr) node)->extra &= ~(flags << XSLT_SOURCE_NODE_SHIFT_32);
             return 0;
 
         case XML_ELEMENT_NODE:
@@ -2010,7 +2010,55 @@
         case XML_CDATA_SECTION_NODE:
         case XML_PI_NODE:
         case XML_COMMENT_NODE:
-            node->extra &= ~(flags << 12);
+            node->extra &= ~(flags << XSLT_SOURCE_NODE_SHIFT_16);
+            return 0;
+
+        default:
+            return -1;
+    }
+}
+
+/**
+ * xsltGetSourceNodeValue:
+ * @node:  Node from source document
+ *
+ * Returns the associated 28 bit unsigned value for a source node,
+ * or 0 if node does not have an associated value.
+ */
+int
+xsltGetSourceNodeValue(xmlNodePtr node) {
+    switch (node->type) {
+        case XML_DOCUMENT_NODE:
+        case XML_HTML_DOCUMENT_NODE:
+            return (((xmlDocPtr) node)->extra & XSLT_SOURCE_NODE_VALUE_MASK);
+
+        case XML_ATTRIBUTE_NODE:
+            return (((xmlAttrPtr) node)->extra & XSLT_SOURCE_NODE_VALUE_MASK);
+
+        default:
+            return 0;
+    }
+}
+
+/**
+ * xsltSetSourceNodeValue:
+ * @node:  Node from source document
+ * @value:  28 bit unsigned value to associate with the node.
+ *
+ * Returns 0 on success, -1 on error.
+ */
+int
+xsltSetSourceNodeValue(xmlNodePtr node, int value) {
+    switch (node->type) {
+        case XML_DOCUMENT_NODE:
+        case XML_HTML_DOCUMENT_NODE:
+            ((xmlDocPtr) node)->extra &= ~XSLT_SOURCE_NODE_VALUE_MASK;
+            ((xmlDocPtr) node)->extra |= (value & XSLT_SOURCE_NODE_VALUE_MASK);
+            return 0;
+
+        case XML_ATTRIBUTE_NODE:
+            ((xmlAttrPtr) node)->extra &= ~XSLT_SOURCE_NODE_VALUE_MASK;
+            ((xmlAttrPtr) node)->extra |= (value & XSLT_SOURCE_NODE_VALUE_MASK);
             return 0;
 
         default:
diff --git a/third_party/libxslt/src/libxslt/xsltutils.h b/third_party/libxslt/src/libxslt/xsltutils.h
index 2514774..1e753ee 100644
--- a/third_party/libxslt/src/libxslt/xsltutils.h
+++ b/third_party/libxslt/src/libxslt/xsltutils.h
@@ -261,6 +261,10 @@
 #define XSLT_SOURCE_NODE_MASK       15u
 #define XSLT_SOURCE_NODE_HAS_KEY    1u
 #define XSLT_SOURCE_NODE_HAS_ID     2u
+#define XSLT_SOURCE_NODE_SHIFT_16   12u
+#define XSLT_SOURCE_NODE_SHIFT_32   28u
+#define XSLT_SOURCE_NODE_VALUE_MASK ((1 << XSLT_SOURCE_NODE_SHIFT_32) - 1)
+#define XSLT_SOURCE_NODE_VALUE_MAX  XSLT_SOURCE_NODE_VALUE_MASK
 int
 xsltGetSourceNodeFlags(xmlNodePtr node);
 int
@@ -268,6 +272,10 @@
                        int flags);
 int
 xsltClearSourceNodeFlags(xmlNodePtr node, int flags);
+int
+xsltSetSourceNodeValue(xmlNodePtr node, int value);
+int
+xsltGetSourceNodeValue(xmlNodePtr node);
 void **
 xsltGetPSVIPtr(xmlNodePtr cur);
 /** DOC_ENABLE */
diff --git a/third_party/llvm-libc/src b/third_party/llvm-libc/src
index c8248a0..d7bdad4 160000
--- a/third_party/llvm-libc/src
+++ b/third_party/llvm-libc/src
@@ -1 +1 @@
-Subproject commit c8248a038fe70b7495d1d52fde500cf2dd6fae82
+Subproject commit d7bdad4ef86b827a96469b1dfdfcfa1218930e59
diff --git a/third_party/omnibox_proto/README.chromium b/third_party/omnibox_proto/README.chromium
index 581050f..ad576226 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: 738839352
-Date: 2025-03-20
+Version: 755469696
+Date: 2025-05-06
 License: BSD-3-Clause
 License File: LICENSE
 Shipped: yes
diff --git a/third_party/omnibox_proto/answer_type.proto b/third_party/omnibox_proto/answer_type.proto
index b9ce998..9322ae0 100644
--- a/third_party/omnibox_proto/answer_type.proto
+++ b/third_party/omnibox_proto/answer_type.proto
@@ -20,10 +20,10 @@
   ANSWER_TYPE_SUNRISE_SUNSET = 6;
   ANSWER_TYPE_TRANSLATION = 7;
   ANSWER_TYPE_WEATHER = 8;
-  ANSWER_TYPE_WHEN_IS = 9;
+  ANSWER_TYPE_WHEN_IS = 9 [deprecated = true];
   ANSWER_TYPE_CURRENCY = 10;
   ANSWER_TYPE_LOCAL_TIME = 11;
-  ANSWER_TYPE_PLAY_INSTALL = 12;
-  ANSWER_TYPE_WEB_ANSWER = 14;
+  ANSWER_TYPE_PLAY_INSTALL = 12 [deprecated = true];
+  ANSWER_TYPE_WEB_ANSWER = 14 [deprecated = true];
   reserved 4, 13;
 }
diff --git a/third_party/omnibox_proto/groups.proto b/third_party/omnibox_proto/groups.proto
index fd18521..f90ff3f 100644
--- a/third_party/omnibox_proto/groups.proto
+++ b/third_party/omnibox_proto/groups.proto
@@ -73,6 +73,7 @@
   GROUP_VISITED_DOC_RELATED = 10005;
   GROUP_MULTIMODAL = 10006;
   GROUP_CONTEXTUAL_SEARCH = 10007;
+  GROUP_CONTEXTUAL_SEARCH_ACTION = 10008;
   GROUP_POLARIS_RESERVED_MAX = 19999;
 
   // Mobile-specific auxiliary suggestions.
diff --git a/third_party/skia b/third_party/skia
index 32591be..c8f54c1 160000
--- a/third_party/skia
+++ b/third_party/skia
@@ -1 +1 @@
-Subproject commit 32591be9cd3bea8c9e1ad39c64bc8230a4c4d499
+Subproject commit c8f54c1bc565b8a7eced0a77088b228efdc70c2c
diff --git a/third_party/wayland-protocols/BUILD.gn b/third_party/wayland-protocols/BUILD.gn
index 79df6e8..4bcbc2c 100644
--- a/third_party/wayland-protocols/BUILD.gn
+++ b/third_party/wayland-protocols/BUILD.gn
@@ -171,10 +171,6 @@
   sources = [ "unstable/ui-controls/ui-controls-unstable-v1.xml" ]
 }
 
-wayland_protocol("test_controller_protocol") {
-  sources = [ "unstable/test-controller/test-controller-unstable-v1.xml" ]
-}
-
 wayland_protocol("viewporter_protocol") {
   sources = [ "src/stable/viewporter/viewporter.xml" ]
 }
diff --git a/third_party/wayland-protocols/unstable/test-controller/OWNERS b/third_party/wayland-protocols/unstable/test-controller/OWNERS
deleted file mode 100644
index 6cb9cdc..0000000
--- a/third_party/wayland-protocols/unstable/test-controller/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-aycyang@chromium.org
diff --git a/third_party/wayland-protocols/unstable/test-controller/README b/third_party/wayland-protocols/unstable/test-controller/README
deleted file mode 100644
index 690d497..0000000
--- a/third_party/wayland-protocols/unstable/test-controller/README
+++ /dev/null
@@ -1,4 +0,0 @@
-APIs used in tests.
-
-Maintainers:
-Alex Yang <aycyang@chromium.org>
diff --git a/third_party/wayland-protocols/unstable/test-controller/test-controller-unstable-v1.xml b/third_party/wayland-protocols/unstable/test-controller/test-controller-unstable-v1.xml
deleted file mode 100644
index 84d31f2..0000000
--- a/third_party/wayland-protocols/unstable/test-controller/test-controller-unstable-v1.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<protocol name="test_controller_unstable_v1">
-  <copyright>
-    Copyright 2024 The Chromium Authors.
-
-    Permission is hereby granted, free of charge, to any person obtaining a
-    copy of this software and associated documentation files (the "Software"),
-    to deal in the Software without restriction, including without limitation
-    the rights to use, copy, modify, merge, publish, distribute, sublicense,
-    and/or sell copies of the Software, and to permit persons to whom the
-    Software is furnished to do so, subject to the following conditions:
-
-    The above copyright notice and this permission notice (including the next
-    paragraph) shall be included in all copies or substantial portions of the
-    Software.
-
-    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-    DEALINGS IN THE SOFTWARE.
-  </copyright>
-
-  <interface name="zcr_test_controller_v1" version="1">
-    <description summary="APIs for tests">
-      Contains APIs intended for use in integration tests.
-    </description>
-    <request name="mock_event_tick_clock_start" since="1">
-      <description summary="initialize a test-controlled event tick clock">
-        Replace the compositor's real-time event tick clock with a
-        client-controlled event tick clock. Useful for tighter control of input
-        event timing.
-      </description>
-    </request>
-    <request name="mock_event_tick_clock_advance" since="1">
-      <arg name="milliseconds" type="uint"/>
-      <description summary="advance the mock event tick clock">
-        Advance the client-controlled event tick clock by the given number of
-        milliseconds.
-      </description>
-    </request>
-  </interface>
-</protocol>
diff --git a/third_party/webrtc b/third_party/webrtc
index 626c9f1..d69d080 160000
--- a/third_party/webrtc
+++ b/third_party/webrtc
@@ -1 +1 @@
-Subproject commit 626c9f19118aac2e0faf45db73e9101d4502190c
+Subproject commit d69d0808c37b4ea338e59a58837ef180023316a6
diff --git a/tools/cast3p/runtime.version b/tools/cast3p/runtime.version
index eb8be83..3f73c7226 100644
--- a/tools/cast3p/runtime.version
+++ b/tools/cast3p/runtime.version
@@ -1 +1 @@
-476819
+477659
diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py
index f274064..e30c768f 100755
--- a/tools/clang/scripts/update.py
+++ b/tools/clang/scripts/update.py
@@ -39,7 +39,7 @@
 # These fields are written by //tools/clang/scripts/upload_revision.py, and
 # should not be changed manually.
 # They are also read by build/config/compiler/BUILD.gn.
-CLANG_REVISION = 'llvmorg-21-init-10502-gb2e2ae87'
+CLANG_REVISION = 'llvmorg-21-init-10851-gcd6c4b61'
 CLANG_SUB_REVISION = 1
 
 PACKAGE_VERSION = '%s-%s' % (CLANG_REVISION, CLANG_SUB_REVISION)
diff --git a/tools/cygprofile/generate_orderfile.pydeps b/tools/cygprofile/generate_orderfile.pydeps
index 08b16e8..7555de0 100644
--- a/tools/cygprofile/generate_orderfile.pydeps
+++ b/tools/cygprofile/generate_orderfile.pydeps
@@ -54,6 +54,7 @@
 //third_party/catapult/devil/devil/devil_env.py
 //third_party/catapult/devil/devil/utils/__init__.py
 //third_party/catapult/devil/devil/utils/cmd_helper.py
+//third_party/catapult/devil/devil/utils/host_utils.py
 //third_party/catapult/devil/devil/utils/lazy/__init__.py
 //third_party/catapult/devil/devil/utils/lazy/weak_constant.py
 //third_party/catapult/devil/devil/utils/logging_common.py
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 9560ec11..76e40bed 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -12434,6 +12434,22 @@
   </description>
 </action>
 
+<action name="Glic.Settings.ClosedCaptions.Disabled">
+  <owner>sanaakbani@google.com</owner>
+  <owner>iwells@chromium.org</owner>
+  <description>
+    Logged if the user disables closed captions in glic settings.
+  </description>
+</action>
+
+<action name="Glic.Settings.ClosedCaptions.Enabled">
+  <owner>sanaakbani@google.com</owner>
+  <owner>iwells@chromium.org</owner>
+  <description>
+    Logged if the user enables closed captions in glic settings.
+  </description>
+</action>
+
 <action name="Glic.Settings.Geolocation.Disabled">
   <owner>tommasin@chromium.org</owner>
   <owner>erikchen@chromium.org</owner>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index f805f67..5335630 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -11329,6 +11329,7 @@
   <int value="-1169571217" label="SharingDesktopScreenshotsEdit:disabled"/>
   <int value="-1168910727"
       label="enable-experimental-accessibility-language-detection-dynamic"/>
+  <int value="-1168836114" label="AndroidTabDeclutterAutoDelete:enabled"/>
   <int value="-1168123287" label="WebRTC-FrameBuffer3:disabled"/>
   <int value="-1167992523" label="DesktopPWAsCustomTabUI:disabled"/>
   <int value="-1167716837"
@@ -14661,6 +14662,7 @@
   <int value="110888614" label="ReduceDisplayNotifications:enabled"/>
   <int value="112126033"
       label="ClearInstanceInfoWhenClosedIntentionally:disabled"/>
+  <int value="112131756" label="AndroidSmsOtpFilling:disabled"/>
   <int value="112140177" label="DesktopPWAsUnifiedInstall:disabled"/>
   <int value="113142558" label="TrackingProtectionOnboardingRollback:enabled"/>
   <int value="114318184" label="AccessibilityFlashScreenFeature:disabled"/>
@@ -14669,6 +14671,7 @@
   <int value="114657517" label="SecurePaymentConfirmationDebug:disabled"/>
   <int value="114748684" label="AlmanacGameMigration:disabled"/>
   <int value="114948617" label="SurfacePolish:enabled"/>
+  <int value="115449720" label="AndroidTabDeclutterAutoDelete:disabled"/>
   <int value="115834091" label="TrackByDefaultOnMobile:enabled"/>
   <int value="115852043"
       label="PageInfoAboutThisSiteKeepSidePanelOnSameTabNavs:enabled"/>
@@ -14920,6 +14923,7 @@
   <int value="212194994" label="PerAppLanguage:enabled"/>
   <int value="212245695" label="PowerBookmarksSidePanel:disabled"/>
   <int value="212361556" label="ComposeProactiveNudge:disabled"/>
+  <int value="212459881" label="AndroidSmsOtpFilling:enabled"/>
   <int value="212489101" label="AutofillAssistantChromeEntry:enabled"/>
   <int value="212959824" label="SafetyHubWeakAndReusedPasswords:disabled"/>
   <int value="212977039" label="MediaFoundationD3D11VideoCapture:disabled"/>
diff --git a/tools/metrics/histograms/metadata/accessibility/histograms.xml b/tools/metrics/histograms/metadata/accessibility/histograms.xml
index a3f4e38..abf5b3b9 100644
--- a/tools/metrics/histograms/metadata/accessibility/histograms.xml
+++ b/tools/metrics/histograms/metadata/accessibility/histograms.xml
@@ -3270,15 +3270,6 @@
   </token>
 </histogram>
 
-<histogram name="Accessibility.ScreenAI.OCR.ImageSize10M" units="count"
-    expires_after="2025-08-24">
-  <owner>rhalavati@chromium.org</owner>
-  <owner>chrome-a11y-core@google.com</owner>
-  <summary>
-    Records the number of pixels in the passed image to OCR request.
-  </summary>
-</histogram>
-
 <histogram name="Accessibility.ScreenAI.OCR.Latency.{DownsampleStatus}"
     units="ms" expires_after="2026-04-01">
   <owner>rhalavati@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/android/enums.xml b/tools/metrics/histograms/metadata/android/enums.xml
index a113b188..5a46dc1 100644
--- a/tools/metrics/histograms/metadata/android/enums.xml
+++ b/tools/metrics/histograms/metadata/android/enums.xml
@@ -958,11 +958,12 @@
   <int value="4" label="START_STICKY"/>
 </enum>
 
-<enum name="DragDropTabResult">
+<enum name="DragDropResult">
   <summary>
     SUCCESS is recorded during ACTION_DRAG_ENDED. The rest enums are all for
     failed drops, which are recorded in ACTION_DROP. Also see
-    Android.DragDrop.Tab.FromStrip.Result
+    Android.DragDrop.Tab.FromStrip.Result and
+    Android.DragDrop.TabGroup.FromStrip.Result.
   </summary>
   <int value="0" label="SUCCESS"/>
   <int value="1" label="IGNORED_TOOLBAR"/>
@@ -971,10 +972,13 @@
   <int value="4" label="IGNORED_SAME_INSTANCE"/>
   <int value="5" label="ERROR_TAB_NOT_FOUND"/>
   <int value="6" label="IGNORED_MAX_INSTANCES"/>
+  <int value="7" label="IGNORED_MHTML_TAB"/>
 </enum>
 
 <enum name="DragDropType">
-  <summary>See Android.DragDrop.Tab.Type</summary>
+  <summary>
+    See Android.DragDrop.Tab.Type and Android.DragDrop.TabGroup.Type
+  </summary>
   <int value="0" label="TAB_STRIP_TO_TAB_STRIP"/>
   <int value="1" label="TAB_STRIP_TO_CONTENT"/>
   <int value="2" label="TAB_STRIP_TO_NEW_INSTANCE"/>
diff --git a/tools/metrics/histograms/metadata/android/histograms.xml b/tools/metrics/histograms/metadata/android/histograms.xml
index 5bfc066..20c4665e 100644
--- a/tools/metrics/histograms/metadata/android/histograms.xml
+++ b/tools/metrics/histograms/metadata/android/histograms.xml
@@ -304,6 +304,11 @@
   <variant name="Unset" summary="pre-warmed tabs without creation reason"/>
 </variants>
 
+<variants name="TabObjectType">
+  <variant name=".Tab" summary="tab"/>
+  <variant name=".TabGroup" summary="tab group"/>
+</variants>
+
 <variants name="TaskType">
   <variant name="AuxiliarySearchDonate" summary="AuxiliarySearchDonate"/>
   <variant name="BackgroundSyncOneShot" summary="BackgroundSyncOneShot"/>
@@ -1750,99 +1755,16 @@
   </summary>
 </histogram>
 
-<histogram name="Android.DragDrop.Tab.Duration.WithinDestStrip" units="ms"
-    expires_after="2025-09-07">
-  <owner>shuyng@google.com</owner>
-  <owner>clank-large-form-factors@google.com</owner>
-  <summary>
-    During tab drag and drop, record duration when the drag is within the
-    destination strip, which is the time difference from the first time entering
-    the destination strip to the last time leaving or dropping into the
-    destination strip. Recorded when a tab drag and drop is finished.
-  </summary>
-</histogram>
-
-<histogram name="Android.DragDrop.Tab.FromStrip.Result"
-    enum="DragDropTabResult" expires_after="2025-09-07">
-  <owner>shuyng@google.com</owner>
-  <owner>clank-large-form-factors@google.com</owner>
-  <summary>
-    Records the tab drag and drop results, including successful drops and failed
-    drops with varied reasons. For failed drops, it is recorded during
-    ACTION_DROP. For successful drops, it is recored during ACTION_DRAG_ENDED.
-    Note that the successful drops count can be recorded more often than
-    Android.DragDrop.Tab.Type, such as reorder within strip or tabs being
-    consumed as text by EditText in native pages.
-  </summary>
-</histogram>
-
-<histogram name="Android.DragDrop.Tab.FromStrip.Result.DesktopWindow"
-    enum="DragDropTabResult" expires_after="2025-11-04">
-  <owner>aishwaryarj@google.com</owner>
-  <owner>clank-large-form-factors@google.com</owner>
-  <summary>
-    Records the tab drag and drop results in desktop windowing mode, including
-    successful drops and failed drops with varied reasons. For failed drops, it
-    is recorded during ACTION_DROP. For successful drops, it is recored during
-    ACTION_DRAG_ENDED. Note that the successful drops count can be recorded more
-    often than Android.DragDrop.Tab.Type.DesktopWindow, such as reorder within
-    strip or tabs being consumed as text by EditText in native pages.
-  </summary>
-</histogram>
-
-<histogram name="Android.DragDrop.Tab.MaxInstanceFailureCount" units="failures"
-    expires_after="2025-09-28">
+<histogram name="Android.DragDrop.TabOrGroup.MaxInstanceFailureCount"
+    units="failures" expires_after="2026-05-05">
   <owner>aishwaryarj@google.com</owner>
   <owner>wenyufu@chromium.org</owner>
   <owner>clank-large-form-factors@google.com</owner>
   <summary>
-    The number of times tab tearing failed for a user, because the maximum
-    number of Chrome instances were open, in a 24-hour period. Recorded when the
-    max-instance toast is shown when a dragged tab drop is unhandled at the max
-    instance limit.
-  </summary>
-</histogram>
-
-<histogram name="Android.DragDrop.Tab.ReorderStripWithDragDrop" enum="Boolean"
-    expires_after="2025-09-28">
-  <owner>shuyng@google.com</owner>
-  <owner>clank-large-form-factors@google.com</owner>
-  <summary>
-    Records tab drops into source strip with or without leaving the strip. True
-    means the tab drag has left the source strip; while false means it does not.
-    Recorded when tab is dropped into source strip. Used to determine the amount
-    of accidental tab drag out of the strip.
-  </summary>
-</histogram>
-
-<histogram name="Android.DragDrop.Tab.SourceWindowClosed" enum="Boolean"
-    expires_after="2025-09-14">
-  <owner>aishwaryarj@google.com</owner>
-  <owner>wenyufu@chromium.org</owner>
-  <owner>clank-large-form-factors@google.com</owner>
-  <summary>
-    Records true when a tab drag/drop results in closing the source Chrome
-    window because it was the last tab in the window, false when the drop does
-    not close the source window. Only recorded when tab drag/drop is handled.
-  </summary>
-</histogram>
-
-<histogram name="Android.DragDrop.Tab.Type" enum="DragDropType"
-    expires_after="2025-09-07">
-  <owner>shuyng@google.com</owner>
-  <owner>clank-large-form-factors@google.com</owner>
-  <summary>
-    Records the drag source and drop target when a tab is dropped successfully.
-  </summary>
-</histogram>
-
-<histogram name="Android.DragDrop.Tab.Type.DesktopWindow" enum="DragDropType"
-    expires_after="2025-11-04">
-  <owner>aishwaryarj@google.com</owner>
-  <owner>clank-large-form-factors@google.com</owner>
-  <summary>
-    Records the drag source and drop target when a tab is dropped successfully
-    in desktop windowing mode.
+    The number of times tab or group tearing failed for a user, because the
+    maximum number of Chrome instances were open, in a 24-hour period. Recorded
+    when the max-instance toast is shown when a dragged tab or group drop is
+    unhandled at the max instance limit.
   </summary>
 </histogram>
 
@@ -1858,6 +1780,109 @@
   </summary>
 </histogram>
 
+<histogram name="Android.DragDrop{TabObjectType}.Duration.WithinDestStrip"
+    units="ms" expires_after="2026-05-05">
+  <owner>shuyng@google.com</owner>
+  <owner>zheliooo@google.com</owner>
+  <owner>clank-large-form-factors@google.com</owner>
+  <summary>
+    During {TabObjectType} drag and drop, record duration when the drag is
+    within the destination strip, which is the time difference from the first
+    time entering the destination strip to the last time leaving or dropping
+    into the destination strip. Recorded when a {TabObjectType} drag and drop is
+    finished.
+  </summary>
+  <token key="TabObjectType" variants="TabObjectType"/>
+</histogram>
+
+<histogram name="Android.DragDrop{TabObjectType}.FromStrip.Result"
+    enum="DragDropResult" expires_after="2026-05-05">
+  <owner>shuyng@google.com</owner>
+  <owner>zheliooo@google.com</owner>
+  <owner>clank-large-form-factors@google.com</owner>
+  <summary>
+    Records the {TabObjectType} drag and drop results, including successful
+    drops and failed drops with varied reasons. For failed drops, it is recorded
+    during ACTION_DROP. For successful drops, it is recorded during
+    ACTION_DRAG_ENDED. Note that the successful drops count can be recorded more
+    often than Android.DragDrop.Tab.Type or Android.DragDrop.TabGroup.Type such
+    as reorder within strip or tabs being consumed as text by EditText in native
+    pages.
+  </summary>
+  <token key="TabObjectType" variants="TabObjectType"/>
+</histogram>
+
+<histogram
+    name="Android.DragDrop{TabObjectType}.FromStrip.Result.DesktopWindow"
+    enum="DragDropResult" expires_after="2026-05-05">
+  <owner>aishwaryarj@google.com</owner>
+  <owner>zheliooo@google.com</owner>
+  <owner>clank-large-form-factors@google.com</owner>
+  <summary>
+    Records the {TabObjectType} drag and drop results in desktop windowing mode,
+    including successful drops and failed drops with varied reasons. For failed
+    drops, it is recorded during ACTION_DROP. For successful drops, it is
+    recored during ACTION_DRAG_ENDED. Note that the successful drops count can
+    be recorded more often than Android.DragDrop.Tab.Type.DesktopWindow or
+    Android.DragDrop.TabGroup.Type.DesktopWindow, such as reorder within strip
+    or tabs being consumed as text by EditText in native pages.
+  </summary>
+  <token key="TabObjectType" variants="TabObjectType"/>
+</histogram>
+
+<histogram name="Android.DragDrop{TabObjectType}.ReorderStripWithDragDrop"
+    enum="Boolean" expires_after="2026-05-05">
+  <owner>shuyng@google.com</owner>
+  <owner>zheliooo@google.com</owner>
+  <owner>clank-large-form-factors@google.com</owner>
+  <summary>
+    Records {TabObjectType} drops into source strip with or without leaving the
+    strip. True means the drag has left the source strip; while false means it
+    does not. Recorded when drop into source strip. Used to determine the amount
+    of accidental drag out of the strip.
+  </summary>
+  <token key="TabObjectType" variants="TabObjectType"/>
+</histogram>
+
+<histogram name="Android.DragDrop{TabObjectType}.SourceWindowClosed"
+    enum="Boolean" expires_after="2026-05-05">
+  <owner>aishwaryarj@google.com</owner>
+  <owner>wenyufu@chromium.org</owner>
+  <owner>zheliooo@google.com</owner>
+  <owner>clank-large-form-factors@google.com</owner>
+  <summary>
+    Records true when a {TabObjectType} drag/drop results in closing the source
+    Chrome window because it was the last {TabObjectType} in the window, false
+    when the drop does not close the source window. Only recorded when drag/drop
+    is handled.
+  </summary>
+  <token key="TabObjectType" variants="TabObjectType"/>
+</histogram>
+
+<histogram name="Android.DragDrop{TabObjectType}.Type" enum="DragDropType"
+    expires_after="2026-05-05">
+  <owner>shuyng@google.com</owner>
+  <owner>zheliooo@google.com</owner>
+  <owner>clank-large-form-factors@google.com</owner>
+  <summary>
+    Records the drag source and drop target when a {TabObjectType} is dropped
+    successfully.
+  </summary>
+  <token key="TabObjectType" variants="TabObjectType"/>
+</histogram>
+
+<histogram name="Android.DragDrop{TabObjectType}.Type.DesktopWindow"
+    enum="DragDropType" expires_after="2026-05-05">
+  <owner>aishwaryarj@google.com</owner>
+  <owner>zheliooo@google.com</owner>
+  <owner>clank-large-form-factors@google.com</owner>
+  <summary>
+    Records the drag source and drop target when a {TabObjectType} is dropped
+    successfully in desktop windowing mode.
+  </summary>
+  <token key="TabObjectType" variants="TabObjectType"/>
+</histogram>
+
 <histogram name="Android.DynamicColors.IsAvailable" enum="Boolean"
     expires_after="2024-06-30">
   <owner>skym@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/apps/enums.xml b/tools/metrics/histograms/metadata/apps/enums.xml
index 7cd65d8..1b3b604e 100644
--- a/tools/metrics/histograms/metadata/apps/enums.xml
+++ b/tools/metrics/histograms/metadata/apps/enums.xml
@@ -390,6 +390,23 @@
       label="(Obsolete) Web apps opened in Standalone browser tabs"/>
 </enum>
 
+<enum name="ChromeAppDeprecationLaunchOutcome">
+  <int value="0" label="UserInstalled AllowedByFlag"/>
+  <int value="1" label="UserInstalled AllowedByAllowlist"/>
+  <int value="2" label="UserInstalled Blocked"/>
+  <int value="3" label="KioskMode AllowedByFlag"/>
+  <int value="4" label="KioskMode AllowedByAllowlist"/>
+  <int value="5" label="KioskMode AllowedByAdminPolicy"/>
+  <int value="6" label="KioskMode Blocked"/>
+  <int value="7" label="Managed AllowedByFlag"/>
+  <int value="8" label="Managed AllowedByAllowlist"/>
+  <int value="9" label="Managed AllowedByAdminPolicy"/>
+  <int value="10" label="Managed Blocked"/>
+  <int value="11" label="Not Chrome App Allowed"/>
+  <int value="12" label="Default Allowed"/>
+  <int value="13" label="Default Blocked"/>
+</enum>
+
 <enum name="ChromeOSUICommands">
 <!-- Please put in checks to ensure Command IDs are stable before adding them to this enum. -->
 
diff --git a/tools/metrics/histograms/metadata/apps/histograms.xml b/tools/metrics/histograms/metadata/apps/histograms.xml
index ccfa1d7..b605c96e8 100644
--- a/tools/metrics/histograms/metadata/apps/histograms.xml
+++ b/tools/metrics/histograms/metadata/apps/histograms.xml
@@ -346,6 +346,16 @@
   </summary>
 </histogram>
 
+<histogram name="Apps.AppLaunch.ChromeAppsDeprecationCheck"
+    enum="ChromeAppDeprecationLaunchOutcome" expires_after="2026-05-01">
+  <owner>giovax@google.com</owner>
+  <owner>src/apps/DEPRECATION_OWNERS</owner>
+  <summary>
+    The outcome of the deprecation checks performed during the launch of a
+    Chrome App.
+  </summary>
+</histogram>
+
 <histogram name="Apps.AppLaunchPerAppType" enum="AppType"
     expires_after="2025-09-14">
   <owner>ovn@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/autofill/histograms.xml b/tools/metrics/histograms/metadata/autofill/histograms.xml
index a5cf1a2..9214d9af 100644
--- a/tools/metrics/histograms/metadata/autofill/histograms.xml
+++ b/tools/metrics/histograms/metadata/autofill/histograms.xml
@@ -6648,6 +6648,18 @@
   </summary>
 </histogram>
 
+<histogram name="Autofill.TouchToFill.LoyaltyCard.TriggerOutcome"
+    enum="TouchToFill.PaymentMethod.TriggerOutcome" expires_after="2025-12-31">
+  <owner>tchudakov@google.com</owner>
+  <owner>jsaul@google.com</owner>
+  <owner>payments-autofill-team@google.com</owner>
+  <summary>
+    Records the outcome of the attempt to trigger Touch To Fill for loyalty
+    cards. It is recorded only when the form is parsed as a loyalty card by
+    Autofill on Chrome Android and the user clicks on the form field.
+  </summary>
+</histogram>
+
 <histogram name="Autofill.TransliterationDuration" units="ms"
     expires_after="2025-11-01">
   <owner>sygiet@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/blink/enums.xml b/tools/metrics/histograms/metadata/blink/enums.xml
index 02d3134e..59a462d 100644
--- a/tools/metrics/histograms/metadata/blink/enums.xml
+++ b/tools/metrics/histograms/metadata/blink/enums.xml
@@ -8197,6 +8197,7 @@
   <int value="332" label="WindowManagement"/>
   <int value="333" label="Requestclose"/>
   <int value="334" label="DRAFT_Uint8ArrayToFromBase64AndHex"/>
+  <int value="335" label="LineBreak"/>
 </enum>
 
 <!-- LINT.ThenChange(//third_party/blink/public/mojom/use_counter/metrics/webdx_feature.mojom:WebDXFeature) -->
diff --git a/tools/metrics/histograms/metadata/omnibox/histograms.xml b/tools/metrics/histograms/metadata/omnibox/histograms.xml
index 930e5cb6..687d647 100644
--- a/tools/metrics/histograms/metadata/omnibox/histograms.xml
+++ b/tools/metrics/histograms/metadata/omnibox/histograms.xml
@@ -2019,6 +2019,20 @@
   </token>
 </histogram>
 
+<histogram name="Omnibox.SearchPrefetch.DuplicateSearchTermsAge" units="ms"
+    expires_after="2025-09-14">
+  <owner>lingqi@chromium.org</owner>
+  <owner>nhiroki@chromium.org</owner>
+  <owner>chrome-prerendering@google.com</owner>
+  <summary>
+    Measures the duration between when the search terms are visited and when the
+    same search terms are visited again.
+
+    Recorded at SearchPrefetchService::RecordInterceptionMetrics when the same
+    search terms are visited again.
+  </summary>
+</histogram>
+
 <histogram
     name="Omnibox.SearchPrefetch.SearchWhatYouTypedWasAlsoSuggested.{HistoryOrSuggest}"
     enum="Boolean" expires_after="2023-05-02">
diff --git a/tools/metrics/histograms/metadata/sync/histograms.xml b/tools/metrics/histograms/metadata/sync/histograms.xml
index a9d3b5eb..0723f80a 100644
--- a/tools/metrics/histograms/metadata/sync/histograms.xml
+++ b/tools/metrics/histograms/metadata/sync/histograms.xml
@@ -1617,10 +1617,8 @@
 </histogram>
 
 <histogram name="Sync.PassphraseType5" enum="PassphraseTypeForMetrics"
-    expires_after="2025-06-17">
-<!-- Note: This histogram should (once it's verified to work correctly) replace
-     Sync.PassphraseType4 for the purpose of UMA filtering, and then become
-     expires-never. -->
+    expires_after="never">
+<!-- expires-never: important for UMA filtering. -->
 
   <owner>treib@chromium.org</owner>
   <owner>src/components/sync/OWNERS</owner>
diff --git a/tools/perf/core/bot_platforms.py b/tools/perf/core/bot_platforms.py
index 1be69fc..b058d2b 100644
--- a/tools/perf/core/bot_platforms.py
+++ b/tools/perf/core/bot_platforms.py
@@ -380,14 +380,14 @@
 
 def _crossbench_speedometer2(estimated_runtime=60, arguments=None):
   """Alias for the latest Speedometer 2.X version."""
-  return CrossbenchConfig('speedometer2.1.crossbench',
-                          'speedometer_2.1',
+  return CrossbenchConfig('speedometer2.crossbench',
+                          'speedometer_2',
                           estimated_runtime=estimated_runtime,
                           arguments=arguments)
 
 
 def _crossbench_speedometer3_0(estimated_runtime=60, arguments=None):
-  return CrossbenchConfig('speedometer3.crossbench',
+  return CrossbenchConfig('speedometer3.0.crossbench',
                           'speedometer_3.0',
                           estimated_runtime=estimated_runtime,
                           arguments=arguments)
@@ -403,7 +403,7 @@
 def _crossbench_speedometer3(estimated_runtime=60, arguments=None):
   """Alias for the latest Speedometer 3.X version."""
   return CrossbenchConfig('speedometer3.crossbench',
-                          'speedometer_3.0',
+                          'speedometer_3',
                           estimated_runtime=estimated_runtime,
                           arguments=arguments)
 
@@ -498,20 +498,17 @@
 
 _CROSSBENCH_JETSTREAM_SPEEDOMETER = frozenset([
     _crossbench_jetstream2(),
-    _crossbench_speedometer3_0(),
-    _crossbench_speedometer3_1(),
+    _crossbench_speedometer3(),
 ])
 
 _CROSSBENCH_MOTIONMARK_SPEEDOMETER = frozenset([
     _crossbench_motionmark1_3(),
-    _crossbench_speedometer3_0(),
-    _crossbench_speedometer3_1(),
+    _crossbench_speedometer3(),
 ])
 
 _CROSSBENCH_BENCHMARKS_ALL = frozenset([
     _crossbench_speedometer2(),
-    _crossbench_speedometer3_0(),
-    _crossbench_speedometer3_1(),
+    _crossbench_speedometer3(),
     _crossbench_motionmark1_3(),
     _crossbench_jetstream2(),
 ])
@@ -520,7 +517,7 @@
 # Android.
 _CROSSBENCH_ANDROID = frozenset([
     _crossbench_speedometer3_0(arguments=['--fileserver']),
-    _crossbench_speedometer3_1(arguments=['--fileserver']),
+    _crossbench_speedometer3(arguments=['--fileserver']),
     _crossbench_loadline_phone(arguments=[
         '--cool-down-threshold=moderate',
         '--no-splash',
@@ -532,7 +529,7 @@
 _CROSSBENCH_PIXEL9 = frozenset([
     # _crossbench_jetstream2(arguments=['--fileserver', '--debug']),
     _crossbench_motionmark1_3(arguments=['--fileserver', '--debug']),
-    _crossbench_speedometer3_1(arguments=['--fileserver', '--debug']),
+    _crossbench_speedometer3(arguments=['--fileserver', '--debug']),
     _crossbench_loadline_phone(arguments=[
         '--cool-down-threshold=moderate',
         '--no-splash',
@@ -541,7 +538,7 @@
 ])
 
 _CROSSBENCH_ANDROID_BYRA = frozenset([
-    _crossbench_speedometer3_1(arguments=['--fileserver', '--debug']),
+    _crossbench_speedometer3(arguments=['--fileserver', '--debug']),
 ])
 
 _CROSSBENCH_TANGOR = frozenset([
diff --git a/tools/perf/core/shard_maps/android-byra-perf_map.json b/tools/perf/core/shard_maps/android-byra-perf_map.json
index 94eba60..4f4d6a6e 100644
--- a/tools/perf/core/shard_maps/android-byra-perf_map.json
+++ b/tools/perf/core/shard_maps/android-byra-perf_map.json
@@ -63,8 +63,8 @@
             }
         },
         "crossbench": {
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
+            "speedometer_3": {
+                "display_name": "speedometer3.crossbench",
                 "arguments": [
                     "--fileserver",
                     "--debug"
diff --git a/tools/perf/core/shard_maps/android-pixel-fold-perf_map.json b/tools/perf/core/shard_maps/android-pixel-fold-perf_map.json
index 918edaf..194f3e9f 100644
--- a/tools/perf/core/shard_maps/android-pixel-fold-perf_map.json
+++ b/tools/perf/core/shard_maps/android-pixel-fold-perf_map.json
@@ -50,7 +50,7 @@
                 "abridged": false
             },
             "blink_perf.shadow_dom": {
-                "end": 8,
+                "end": 6,
                 "abridged": false
             }
         }
@@ -58,7 +58,7 @@
     "3": {
         "benchmarks": {
             "blink_perf.shadow_dom": {
-                "begin": 8,
+                "begin": 6,
                 "abridged": false
             },
             "blink_perf.webaudio": {
@@ -92,7 +92,7 @@
                 "abridged": false
             },
             "rasterize_and_record_micro.top_25": {
-                "end": 4,
+                "end": 3,
                 "abridged": false
             }
         },
@@ -108,7 +108,7 @@
     "4": {
         "benchmarks": {
             "rasterize_and_record_micro.top_25": {
-                "begin": 4,
+                "begin": 3,
                 "abridged": false
             },
             "rendering.mobile": {
@@ -248,16 +248,16 @@
         }
     },
     "extra_infos": {
-        "num_stories": 1109,
+        "num_stories": 1110,
         "predicted_min_shard_time": 1781.0,
         "predicted_min_shard_index": 10,
         "predicted_max_shard_time": 1832.0,
         "predicted_max_shard_index": 12,
         "shard #0": 1809.0,
-        "shard #1": 1812.0,
-        "shard #2": 1814.0,
-        "shard #3": 1807.0,
-        "shard #4": 1799.0,
+        "shard #1": 1811.0,
+        "shard #2": 1809.0,
+        "shard #3": 1810.0,
+        "shard #4": 1812.0,
         "shard #5": 1816.0,
         "shard #6": 1818.0,
         "shard #7": 1821.0,
diff --git a/tools/perf/core/shard_maps/android-pixel-tangor-perf_map.json b/tools/perf/core/shard_maps/android-pixel-tangor-perf_map.json
index d1a1582..82d3ee3 100644
--- a/tools/perf/core/shard_maps/android-pixel-tangor-perf_map.json
+++ b/tools/perf/core/shard_maps/android-pixel-tangor-perf_map.json
@@ -194,13 +194,13 @@
         }
     },
     "extra_infos": {
-        "num_stories": 1109,
+        "num_stories": 1111,
         "predicted_min_shard_time": 3797.0,
         "predicted_min_shard_index": 7,
-        "predicted_max_shard_time": 5608.0,
+        "predicted_max_shard_time": 5620.0,
         "predicted_max_shard_index": 1,
-        "shard #0": 4097.0,
-        "shard #1": 5608.0,
+        "shard #0": 4105.0,
+        "shard #1": 5620.0,
         "shard #2": 3860.0,
         "shard #3": 3837.0,
         "shard #4": 3826.0,
diff --git a/tools/perf/core/shard_maps/android-pixel4-perf_map.json b/tools/perf/core/shard_maps/android-pixel4-perf_map.json
index 041a2b5..b7fb420a 100644
--- a/tools/perf/core/shard_maps/android-pixel4-perf_map.json
+++ b/tools/perf/core/shard_maps/android-pixel4-perf_map.json
@@ -44,7 +44,7 @@
                 "abridged": false
             },
             "blink_perf.bindings": {
-                "end": 28,
+                "end": 29,
                 "abridged": false
             },
             "jetstream2": {
@@ -61,11 +61,11 @@
     "3": {
         "benchmarks": {
             "blink_perf.bindings": {
-                "begin": 28,
+                "begin": 29,
                 "abridged": false
             },
             "blink_perf.css": {
-                "end": 25,
+                "end": 26,
                 "abridged": false
             },
             "jetstream2": {
@@ -82,7 +82,7 @@
     "4": {
         "benchmarks": {
             "blink_perf.css": {
-                "begin": 25,
+                "begin": 26,
                 "abridged": false
             },
             "jetstream2": {
@@ -123,7 +123,7 @@
                 "abridged": false
             },
             "blink_perf.layout": {
-                "end": 33,
+                "end": 34,
                 "abridged": false
             },
             "speedometer2": {
@@ -137,8 +137,8 @@
     "7": {
         "benchmarks": {
             "blink_perf.layout": {
-                "begin": 33,
-                "end": 90,
+                "begin": 34,
+                "end": 91,
                 "abridged": false
             },
             "speedometer2": {
@@ -152,7 +152,7 @@
     "8": {
         "benchmarks": {
             "blink_perf.layout": {
-                "begin": 90,
+                "begin": 91,
                 "abridged": false
             },
             "blink_perf.owp_storage": {
@@ -635,18 +635,18 @@
         }
     },
     "extra_infos": {
-        "num_stories": 1147,
+        "num_stories": 1149,
         "predicted_min_shard_time": 687.0,
         "predicted_min_shard_index": 27,
         "predicted_max_shard_time": 1158.0,
         "predicted_max_shard_index": 43,
         "shard #0": 753.0,
         "shard #1": 780.0,
-        "shard #2": 776.0,
-        "shard #3": 782.0,
-        "shard #4": 777.0,
+        "shard #2": 784.0,
+        "shard #3": 780.0,
+        "shard #4": 781.0,
         "shard #5": 785.0,
-        "shard #6": 775.0,
+        "shard #6": 785.0,
         "shard #7": 782.0,
         "shard #8": 779.0,
         "shard #9": 787.0,
diff --git a/tools/perf/core/shard_maps/android-pixel4_webview-perf-pgo_map.json b/tools/perf/core/shard_maps/android-pixel4_webview-perf-pgo_map.json
index f1550e2..98c4fb4a 100644
--- a/tools/perf/core/shard_maps/android-pixel4_webview-perf-pgo_map.json
+++ b/tools/perf/core/shard_maps/android-pixel4_webview-perf-pgo_map.json
@@ -26,7 +26,7 @@
                 "abridged": false
             },
             "blink_perf.css": {
-                "end": 70,
+                "end": 71,
                 "abridged": false
             },
             "speedometer2": {
@@ -40,7 +40,7 @@
     "2": {
         "benchmarks": {
             "blink_perf.css": {
-                "begin": 70,
+                "begin": 71,
                 "abridged": false
             },
             "blink_perf.dom": {
@@ -53,7 +53,7 @@
                 "abridged": false
             },
             "blink_perf.layout": {
-                "end": 23,
+                "end": 24,
                 "abridged": false
             },
             "speedometer2": {
@@ -67,8 +67,8 @@
     "3": {
         "benchmarks": {
             "blink_perf.layout": {
-                "begin": 23,
-                "end": 101,
+                "begin": 24,
+                "end": 102,
                 "abridged": false
             },
             "speedometer2": {
@@ -82,7 +82,7 @@
     "4": {
         "benchmarks": {
             "blink_perf.layout": {
-                "begin": 101,
+                "begin": 102,
                 "abridged": false
             },
             "blink_perf.owp_storage": {
@@ -384,14 +384,14 @@
         }
     },
     "extra_infos": {
-        "num_stories": 1110,
+        "num_stories": 1112,
         "predicted_min_shard_time": 2668.0,
         "predicted_min_shard_index": 19,
         "predicted_max_shard_time": 3149.0,
         "predicted_max_shard_index": 18,
         "shard #0": 2912.0,
-        "shard #1": 2894.0,
-        "shard #2": 2906.0,
+        "shard #1": 2904.0,
+        "shard #2": 2916.0,
         "shard #3": 2897.0,
         "shard #4": 2894.0,
         "shard #5": 2906.0,
diff --git a/tools/perf/core/shard_maps/android-pixel4_webview-perf_map.json b/tools/perf/core/shard_maps/android-pixel4_webview-perf_map.json
index b7e15ef..c185bf9 100644
--- a/tools/perf/core/shard_maps/android-pixel4_webview-perf_map.json
+++ b/tools/perf/core/shard_maps/android-pixel4_webview-perf_map.json
@@ -107,7 +107,7 @@
                 "abridged": false
             },
             "blink_perf.shadow_dom": {
-                "end": 30,
+                "end": 29,
                 "abridged": false
             },
             "speedometer2": {
@@ -121,7 +121,7 @@
     "6": {
         "benchmarks": {
             "blink_perf.shadow_dom": {
-                "begin": 30,
+                "begin": 29,
                 "abridged": false
             },
             "blink_perf.webaudio": {
@@ -152,7 +152,7 @@
                 "abridged": false
             },
             "rendering.mobile": {
-                "end": 2,
+                "end": 1,
                 "abridged": false
             },
             "speedometer2": {
@@ -166,8 +166,8 @@
     "7": {
         "benchmarks": {
             "rendering.mobile": {
-                "begin": 2,
-                "end": 58,
+                "begin": 1,
+                "end": 57,
                 "abridged": false
             },
             "speedometer2": {
@@ -181,8 +181,8 @@
     "8": {
         "benchmarks": {
             "rendering.mobile": {
-                "begin": 58,
-                "end": 114,
+                "begin": 57,
+                "end": 113,
                 "abridged": false
             },
             "speedometer2": {
@@ -196,8 +196,8 @@
     "9": {
         "benchmarks": {
             "rendering.mobile": {
-                "begin": 114,
-                "end": 160,
+                "begin": 113,
+                "end": 159,
                 "abridged": false
             },
             "speedometer2": {
@@ -211,8 +211,8 @@
     "10": {
         "benchmarks": {
             "rendering.mobile": {
-                "begin": 160,
-                "end": 200,
+                "begin": 159,
+                "end": 199,
                 "abridged": false
             },
             "speedometer2": {
@@ -226,8 +226,8 @@
     "11": {
         "benchmarks": {
             "rendering.mobile": {
-                "begin": 200,
-                "end": 250,
+                "begin": 199,
+                "end": 249,
                 "abridged": false
             },
             "speedometer2": {
@@ -241,8 +241,8 @@
     "12": {
         "benchmarks": {
             "rendering.mobile": {
-                "begin": 250,
-                "end": 320,
+                "begin": 249,
+                "end": 319,
                 "abridged": false
             },
             "speedometer2": {
@@ -256,8 +256,8 @@
     "13": {
         "benchmarks": {
             "rendering.mobile": {
-                "begin": 320,
-                "end": 357,
+                "begin": 319,
+                "end": 356,
                 "abridged": false
             },
             "speedometer2": {
@@ -271,7 +271,7 @@
     "14": {
         "benchmarks": {
             "rendering.mobile": {
-                "begin": 357,
+                "begin": 356,
                 "abridged": false
             },
             "rendering.mobile.notracing": {
@@ -280,9 +280,6 @@
             "speedometer": {
                 "abridged": false
             },
-            "speedometer-future": {
-                "abridged": false
-            },
             "speedometer2": {
                 "abridged": false
             },
@@ -293,6 +290,9 @@
     },
     "15": {
         "benchmarks": {
+            "speedometer-future": {
+                "abridged": false
+            },
             "speedometer2": {
                 "abridged": false
             },
@@ -309,7 +309,7 @@
                 "abridged": false
             },
             "system_health.common_mobile": {
-                "end": 39,
+                "end": 38,
                 "abridged": false
             }
         }
@@ -317,7 +317,7 @@
     "16": {
         "benchmarks": {
             "system_health.common_mobile": {
-                "begin": 39,
+                "begin": 38,
                 "abridged": false
             },
             "system_health.memory_mobile": {
@@ -416,28 +416,28 @@
         }
     },
     "extra_infos": {
-        "num_stories": 1110,
+        "num_stories": 1112,
         "predicted_min_shard_time": 2024.0,
         "predicted_min_shard_index": 21,
         "predicted_max_shard_time": 2204.0,
         "predicted_max_shard_index": 17,
         "shard #0": 2108.0,
         "shard #1": 2113.0,
-        "shard #2": 2112.0,
-        "shard #3": 2117.0,
-        "shard #4": 2115.0,
-        "shard #5": 2099.0,
-        "shard #6": 2124.0,
-        "shard #7": 2107.0,
-        "shard #8": 2109.0,
-        "shard #9": 2105.0,
-        "shard #10": 2106.0,
-        "shard #11": 2092.0,
-        "shard #12": 2122.0,
-        "shard #13": 2093.0,
-        "shard #14": 2099.0,
-        "shard #15": 2112.0,
-        "shard #16": 2082.0,
+        "shard #2": 2122.0,
+        "shard #3": 2103.0,
+        "shard #4": 2114.0,
+        "shard #5": 2105.0,
+        "shard #6": 2105.0,
+        "shard #7": 2095.0,
+        "shard #8": 2125.0,
+        "shard #9": 2087.0,
+        "shard #10": 2114.0,
+        "shard #11": 2103.0,
+        "shard #12": 2109.0,
+        "shard #13": 2091.0,
+        "shard #14": 2114.0,
+        "shard #15": 2098.0,
+        "shard #16": 2129.0,
         "shard #17": 2204.0,
         "shard #18": 2099.0,
         "shard #19": 2126.0,
diff --git a/tools/perf/core/shard_maps/android-pixel6-perf-pgo_map.json b/tools/perf/core/shard_maps/android-pixel6-perf-pgo_map.json
index 059820b..555a0e333 100644
--- a/tools/perf/core/shard_maps/android-pixel6-perf-pgo_map.json
+++ b/tools/perf/core/shard_maps/android-pixel6-perf-pgo_map.json
@@ -145,13 +145,13 @@
             }
         },
         "crossbench": {
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
+            "speedometer_3.0": {
+                "display_name": "speedometer3.0.crossbench",
                 "arguments": [
                     "--fileserver"
                 ]
             },
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": [
                     "--fileserver"
diff --git a/tools/perf/core/shard_maps/android-pixel6-perf_map.json b/tools/perf/core/shard_maps/android-pixel6-perf_map.json
index b263de35..2de1f36 100644
--- a/tools/perf/core/shard_maps/android-pixel6-perf_map.json
+++ b/tools/perf/core/shard_maps/android-pixel6-perf_map.json
@@ -266,13 +266,13 @@
             }
         },
         "crossbench": {
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
+            "speedometer_3.0": {
+                "display_name": "speedometer3.0.crossbench",
                 "arguments": [
                     "--fileserver"
                 ]
             },
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": [
                     "--fileserver"
diff --git a/tools/perf/core/shard_maps/android-pixel6-pro-perf_map.json b/tools/perf/core/shard_maps/android-pixel6-pro-perf_map.json
index 141a01b1..23138db 100644
--- a/tools/perf/core/shard_maps/android-pixel6-pro-perf_map.json
+++ b/tools/perf/core/shard_maps/android-pixel6-pro-perf_map.json
@@ -213,13 +213,13 @@
         }
     },
     "extra_infos": {
-        "num_stories": 1108,
+        "num_stories": 1110,
         "predicted_min_shard_time": 3155.0,
         "predicted_min_shard_index": 6,
         "predicted_max_shard_time": 3258.0,
         "predicted_max_shard_index": 7,
-        "shard #0": 3209.0,
-        "shard #1": 3217.0,
+        "shard #0": 3219.0,
+        "shard #1": 3227.0,
         "shard #2": 3210.0,
         "shard #3": 3216.0,
         "shard #4": 3195.0,
diff --git a/tools/perf/core/shard_maps/android-pixel9-perf_map.json b/tools/perf/core/shard_maps/android-pixel9-perf_map.json
index 839d753..baa5795b 100644
--- a/tools/perf/core/shard_maps/android-pixel9-perf_map.json
+++ b/tools/perf/core/shard_maps/android-pixel9-perf_map.json
@@ -41,8 +41,8 @@
     },
     "3": {
         "crossbench": {
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
+            "speedometer_3": {
+                "display_name": "speedometer3.crossbench",
                 "arguments": [
                     "--fileserver",
                     "--debug"
diff --git a/tools/perf/core/shard_maps/android-pixel9-pro-perf_map.json b/tools/perf/core/shard_maps/android-pixel9-pro-perf_map.json
index 839d753..baa5795b 100644
--- a/tools/perf/core/shard_maps/android-pixel9-pro-perf_map.json
+++ b/tools/perf/core/shard_maps/android-pixel9-pro-perf_map.json
@@ -41,8 +41,8 @@
     },
     "3": {
         "crossbench": {
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
+            "speedometer_3": {
+                "display_name": "speedometer3.crossbench",
                 "arguments": [
                     "--fileserver",
                     "--debug"
diff --git a/tools/perf/core/shard_maps/android-pixel9-pro-xl-perf_map.json b/tools/perf/core/shard_maps/android-pixel9-pro-xl-perf_map.json
index 839d753..baa5795b 100644
--- a/tools/perf/core/shard_maps/android-pixel9-pro-xl-perf_map.json
+++ b/tools/perf/core/shard_maps/android-pixel9-pro-xl-perf_map.json
@@ -41,8 +41,8 @@
     },
     "3": {
         "crossbench": {
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
+            "speedometer_3": {
+                "display_name": "speedometer3.crossbench",
                 "arguments": [
                     "--fileserver",
                     "--debug"
diff --git a/tools/perf/core/shard_maps/linux-perf-calibration_map.json b/tools/perf/core/shard_maps/linux-perf-calibration_map.json
index 6ee588eb..2793ee6 100644
--- a/tools/perf/core/shard_maps/linux-perf-calibration_map.json
+++ b/tools/perf/core/shard_maps/linux-perf-calibration_map.json
@@ -8,7 +8,7 @@
                 "abridged": false
             },
             "blink_perf.accessibility": {
-                "end": 14,
+                "end": 15,
                 "abridged": false
             },
             "jetstream2": {
@@ -37,11 +37,11 @@
                 "abridged": false
             },
             "blink_perf.accessibility": {
-                "begin": 14,
+                "begin": 15,
                 "abridged": false
             },
             "blink_perf.bindings": {
-                "end": 30,
+                "end": 31,
                 "abridged": false
             },
             "jetstream2": {
@@ -61,11 +61,11 @@
                 "abridged": false
             },
             "blink_perf.bindings": {
-                "begin": 30,
+                "begin": 31,
                 "abridged": false
             },
             "blink_perf.css": {
-                "end": 17,
+                "end": 19,
                 "abridged": false
             },
             "jetstream2": {
@@ -85,8 +85,8 @@
                 "abridged": false
             },
             "blink_perf.css": {
-                "begin": 17,
-                "end": 58,
+                "begin": 19,
+                "end": 60,
                 "abridged": false
             },
             "jetstream2": {
@@ -106,14 +106,14 @@
                 "abridged": false
             },
             "blink_perf.css": {
-                "begin": 58,
+                "begin": 60,
                 "abridged": false
             },
             "blink_perf.dom": {
                 "abridged": false
             },
             "blink_perf.events": {
-                "end": 1,
+                "end": 2,
                 "abridged": false
             },
             "jetstream2": {
@@ -133,14 +133,14 @@
                 "abridged": false
             },
             "blink_perf.events": {
-                "begin": 1,
+                "begin": 2,
                 "abridged": false
             },
             "blink_perf.image_decoder": {
                 "abridged": false
             },
             "blink_perf.layout": {
-                "end": 26,
+                "end": 27,
                 "abridged": false
             },
             "jetstream2": {
@@ -160,8 +160,8 @@
                 "abridged": false
             },
             "blink_perf.layout": {
-                "begin": 26,
-                "end": 66,
+                "begin": 27,
+                "end": 67,
                 "abridged": false
             },
             "jetstream2": {
@@ -181,8 +181,8 @@
                 "abridged": false
             },
             "blink_perf.layout": {
-                "begin": 66,
-                "end": 107,
+                "begin": 67,
+                "end": 108,
                 "abridged": false
             },
             "jetstream2": {
@@ -202,7 +202,7 @@
                 "abridged": false
             },
             "blink_perf.layout": {
-                "begin": 107,
+                "begin": 108,
                 "abridged": false
             },
             "blink_perf.owp_storage": {
@@ -662,14 +662,14 @@
         }
     },
     "extra_infos": {
-        "num_stories": 2275,
+        "num_stories": 2277,
         "predicted_min_shard_time": 785.0,
         "predicted_min_shard_index": 26,
         "predicted_max_shard_time": 900.0,
         "predicted_max_shard_index": 25,
-        "shard #0": 820.0,
+        "shard #0": 830.0,
         "shard #1": 830,
-        "shard #2": 820,
+        "shard #2": 830,
         "shard #3": 830,
         "shard #4": 820,
         "shard #5": 830,
diff --git a/tools/perf/core/shard_maps/linux-perf-fyi_map.json b/tools/perf/core/shard_maps/linux-perf-fyi_map.json
index 7011acc..eafaa3e6 100644
--- a/tools/perf/core/shard_maps/linux-perf-fyi_map.json
+++ b/tools/perf/core/shard_maps/linux-perf-fyi_map.json
@@ -12,14 +12,10 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
             "jetstream_2.2": {
                 "display_name": "jetstream2.crossbench",
                 "arguments": []
@@ -43,14 +39,10 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
             "jetstream_2.2": {
                 "display_name": "jetstream2.crossbench",
                 "arguments": []
@@ -74,14 +66,10 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
             "jetstream_2.2": {
                 "display_name": "jetstream2.crossbench",
                 "arguments": []
@@ -90,11 +78,9 @@
                 "display_name": "motionmark1.3.crossbench",
                 "arguments": []
             },
-            "speedometer_2.1": {
-                "display_name": "speedometer2.1.crossbench",
-                "arguments": [
-                    "--fileserver"
-                ]
+            "speedometer_2": {
+                "display_name": "speedometer2.crossbench",
+                "arguments": []
             }
         }
     },
@@ -111,14 +97,10 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
             "jetstream_2.2": {
                 "display_name": "jetstream2.crossbench",
                 "arguments": []
@@ -130,14 +112,14 @@
         }
     },
     "extra_infos": {
-        "num_stories": 29,
-        "predicted_min_shard_time": 787.0,
+        "num_stories": 25,
+        "predicted_min_shard_time": 727.0,
         "predicted_min_shard_index": 0,
-        "predicted_max_shard_time": 847.0,
+        "predicted_max_shard_time": 787.0,
         "predicted_max_shard_index": 2,
-        "shard #0": 787.0,
-        "shard #1": 787.0,
-        "shard #2": 847.0,
-        "shard #3": 787.0
+        "shard #0": 727.0,
+        "shard #1": 727.0,
+        "shard #2": 787.0,
+        "shard #3": 727.0
     }
 }
diff --git a/tools/perf/core/shard_maps/linux-r350-perf_map.json b/tools/perf/core/shard_maps/linux-r350-perf_map.json
index cf41f48..1c7a651 100644
--- a/tools/perf/core/shard_maps/linux-r350-perf_map.json
+++ b/tools/perf/core/shard_maps/linux-r350-perf_map.json
@@ -33,14 +33,10 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
             "jetstream_2.2": {
                 "display_name": "jetstream2.crossbench",
                 "arguments": []
@@ -58,14 +54,10 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
             "jetstream_2.2": {
                 "display_name": "jetstream2.crossbench",
                 "arguments": []
@@ -136,14 +128,10 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
             "jetstream_2.2": {
                 "display_name": "jetstream2.crossbench",
                 "arguments": []
@@ -186,14 +174,10 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
             "jetstream_2.2": {
                 "display_name": "jetstream2.crossbench",
                 "arguments": []
@@ -204,7 +188,7 @@
         "benchmarks": {
             "blink_perf.accessibility": {
                 "begin": 7,
-                "end": 9,
+                "end": 10,
                 "abridged": false
             },
             "jetstream2": {
@@ -236,14 +220,10 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
             "jetstream_2.2": {
                 "display_name": "jetstream2.crossbench",
                 "arguments": []
@@ -253,8 +233,8 @@
     "5": {
         "benchmarks": {
             "blink_perf.accessibility": {
-                "begin": 9,
-                "end": 22,
+                "begin": 10,
+                "end": 24,
                 "abridged": false
             },
             "speedometer": {
@@ -280,14 +260,10 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
             "jetstream_2.2": {
                 "display_name": "jetstream2.crossbench",
                 "arguments": []
@@ -297,11 +273,14 @@
     "6": {
         "benchmarks": {
             "blink_perf.accessibility": {
-                "begin": 22,
+                "begin": 24,
                 "abridged": false
             },
             "blink_perf.bindings": {
-                "end": 41,
+                "abridged": false
+            },
+            "blink_perf.css": {
+                "end": 4,
                 "abridged": false
             },
             "speedometer": {
@@ -327,14 +306,10 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
             "jetstream_2.2": {
                 "display_name": "jetstream2.crossbench",
                 "arguments": []
@@ -343,12 +318,9 @@
     },
     "7": {
         "benchmarks": {
-            "blink_perf.bindings": {
-                "begin": 41,
-                "abridged": false
-            },
             "blink_perf.css": {
-                "end": 50,
+                "begin": 4,
+                "end": 67,
                 "abridged": false
             },
             "speedometer": {
@@ -374,14 +346,10 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
             "jetstream_2.2": {
                 "display_name": "jetstream2.crossbench",
                 "arguments": []
@@ -391,11 +359,11 @@
     "8": {
         "benchmarks": {
             "blink_perf.css": {
-                "begin": 50,
+                "begin": 67,
                 "abridged": false
             },
             "blink_perf.dom": {
-                "end": 3,
+                "end": 4,
                 "abridged": false
             },
             "speedometer": {
@@ -421,14 +389,10 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
             "jetstream_2.2": {
                 "display_name": "jetstream2.crossbench",
                 "arguments": []
@@ -438,8 +402,14 @@
     "9": {
         "benchmarks": {
             "blink_perf.dom": {
-                "begin": 3,
-                "end": 13,
+                "begin": 4,
+                "abridged": false
+            },
+            "blink_perf.events": {
+                "abridged": false
+            },
+            "blink_perf.image_decoder": {
+                "end": 1,
                 "abridged": false
             },
             "speedometer": {
@@ -465,14 +435,10 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
             "jetstream_2.2": {
                 "display_name": "jetstream2.crossbench",
                 "arguments": []
@@ -481,17 +447,141 @@
     },
     "10": {
         "benchmarks": {
-            "blink_perf.dom": {
-                "begin": 13,
-                "abridged": false
-            },
-            "blink_perf.events": {
-                "abridged": false
-            },
             "blink_perf.image_decoder": {
+                "begin": 1,
                 "abridged": false
             },
             "blink_perf.layout": {
+                "end": 42,
+                "abridged": false
+            },
+            "speedometer": {
+                "abridged": false
+            },
+            "speedometer2-minorms": {
+                "abridged": false
+            },
+            "speedometer2": {
+                "abridged": false
+            },
+            "speedometer2-predictable": {
+                "abridged": false
+            },
+            "speedometer3": {
+                "abridged": false
+            },
+            "speedometer3-minorms": {
+                "abridged": false
+            },
+            "speedometer3-predictable": {
+                "abridged": false
+            }
+        },
+        "crossbench": {
+            "speedometer_3": {
+                "display_name": "speedometer3.crossbench",
+                "arguments": []
+            },
+            "jetstream_2.2": {
+                "display_name": "jetstream2.crossbench",
+                "arguments": []
+            }
+        }
+    },
+    "11": {
+        "benchmarks": {
+            "blink_perf.layout": {
+                "begin": 42,
+                "end": 95,
+                "abridged": false
+            },
+            "speedometer": {
+                "abridged": false
+            },
+            "speedometer2-minorms": {
+                "abridged": false
+            },
+            "speedometer2": {
+                "abridged": false
+            },
+            "speedometer2-predictable": {
+                "abridged": false
+            },
+            "speedometer3": {
+                "abridged": false
+            },
+            "speedometer3-minorms": {
+                "abridged": false
+            },
+            "speedometer3-predictable": {
+                "abridged": false
+            }
+        },
+        "crossbench": {
+            "speedometer_3": {
+                "display_name": "speedometer3.crossbench",
+                "arguments": []
+            },
+            "jetstream_2.2": {
+                "display_name": "jetstream2.crossbench",
+                "arguments": []
+            }
+        }
+    },
+    "12": {
+        "benchmarks": {
+            "blink_perf.layout": {
+                "begin": 95,
+                "abridged": false
+            },
+            "blink_perf.owp_storage": {
+                "abridged": false
+            },
+            "blink_perf.paint": {
+                "abridged": false
+            },
+            "speedometer": {
+                "abridged": false
+            },
+            "speedometer2-minorms": {
+                "abridged": false
+            },
+            "speedometer2": {
+                "abridged": false
+            },
+            "speedometer2-predictable": {
+                "abridged": false
+            },
+            "speedometer3": {
+                "abridged": false
+            },
+            "speedometer3-minorms": {
+                "abridged": false
+            },
+            "speedometer3-predictable": {
+                "abridged": false
+            }
+        },
+        "crossbench": {
+            "speedometer_3": {
+                "display_name": "speedometer3.crossbench",
+                "arguments": []
+            },
+            "jetstream_2.2": {
+                "display_name": "jetstream2.crossbench",
+                "arguments": []
+            }
+        }
+    },
+    "13": {
+        "benchmarks": {
+            "blink_perf.parser": {
+                "abridged": false
+            },
+            "blink_perf.shadow_dom": {
+                "abridged": false
+            },
+            "blink_perf.svg": {
                 "end": 14,
                 "abridged": false
             },
@@ -518,155 +608,10 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
-            "jetstream_2.2": {
-                "display_name": "jetstream2.crossbench",
-                "arguments": []
-            }
-        }
-    },
-    "11": {
-        "benchmarks": {
-            "blink_perf.layout": {
-                "begin": 14,
-                "end": 63,
-                "abridged": false
-            },
-            "speedometer": {
-                "abridged": false
-            },
-            "speedometer2-minorms": {
-                "abridged": false
-            },
-            "speedometer2": {
-                "abridged": false
-            },
-            "speedometer2-predictable": {
-                "abridged": false
-            },
-            "speedometer3": {
-                "abridged": false
-            },
-            "speedometer3-minorms": {
-                "abridged": false
-            },
-            "speedometer3-predictable": {
-                "abridged": false
-            }
-        },
-        "crossbench": {
-            "speedometer_3.0": {
-                "display_name": "speedometer3.crossbench",
-                "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
-            "jetstream_2.2": {
-                "display_name": "jetstream2.crossbench",
-                "arguments": []
-            }
-        }
-    },
-    "12": {
-        "benchmarks": {
-            "blink_perf.layout": {
-                "begin": 63,
-                "end": 106,
-                "abridged": false
-            },
-            "speedometer": {
-                "abridged": false
-            },
-            "speedometer2-minorms": {
-                "abridged": false
-            },
-            "speedometer2": {
-                "abridged": false
-            },
-            "speedometer2-predictable": {
-                "abridged": false
-            },
-            "speedometer3": {
-                "abridged": false
-            },
-            "speedometer3-minorms": {
-                "abridged": false
-            },
-            "speedometer3-predictable": {
-                "abridged": false
-            }
-        },
-        "crossbench": {
-            "speedometer_3.0": {
-                "display_name": "speedometer3.crossbench",
-                "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
-            "jetstream_2.2": {
-                "display_name": "jetstream2.crossbench",
-                "arguments": []
-            }
-        }
-    },
-    "13": {
-        "benchmarks": {
-            "blink_perf.layout": {
-                "begin": 106,
-                "abridged": false
-            },
-            "blink_perf.owp_storage": {
-                "abridged": false
-            },
-            "blink_perf.paint": {
-                "abridged": false
-            },
-            "blink_perf.parser": {
-                "end": 9,
-                "abridged": false
-            },
-            "speedometer": {
-                "abridged": false
-            },
-            "speedometer2-minorms": {
-                "abridged": false
-            },
-            "speedometer2": {
-                "abridged": false
-            },
-            "speedometer2-predictable": {
-                "abridged": false
-            },
-            "speedometer3": {
-                "abridged": false
-            },
-            "speedometer3-minorms": {
-                "abridged": false
-            },
-            "speedometer3-predictable": {
-                "abridged": false
-            }
-        },
-        "crossbench": {
-            "speedometer_3.0": {
-                "display_name": "speedometer3.crossbench",
-                "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
             "jetstream_2.2": {
                 "display_name": "jetstream2.crossbench",
                 "arguments": []
@@ -675,55 +620,10 @@
     },
     "14": {
         "benchmarks": {
-            "blink_perf.parser": {
-                "begin": 9,
-                "abridged": false
-            },
-            "blink_perf.shadow_dom": {
-                "abridged": false
-            },
             "blink_perf.svg": {
+                "begin": 14,
                 "abridged": false
             },
-            "speedometer": {
-                "abridged": false
-            },
-            "speedometer2-minorms": {
-                "abridged": false
-            },
-            "speedometer2": {
-                "abridged": false
-            },
-            "speedometer2-predictable": {
-                "abridged": false
-            },
-            "speedometer3": {
-                "abridged": false
-            },
-            "speedometer3-minorms": {
-                "abridged": false
-            },
-            "speedometer3-predictable": {
-                "abridged": false
-            }
-        },
-        "crossbench": {
-            "speedometer_3.0": {
-                "display_name": "speedometer3.crossbench",
-                "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
-            "jetstream_2.2": {
-                "display_name": "jetstream2.crossbench",
-                "arguments": []
-            }
-        }
-    },
-    "15": {
-        "benchmarks": {
             "blink_perf.webaudio": {
                 "abridged": false
             },
@@ -763,21 +663,17 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
             "jetstream_2.2": {
                 "display_name": "jetstream2.crossbench",
                 "arguments": []
             }
         }
     },
-    "16": {
+    "15": {
         "benchmarks": {
             "desktop_ui": {
                 "begin": 1,
@@ -807,21 +703,17 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
             "jetstream_2.2": {
                 "display_name": "jetstream2.crossbench",
                 "arguments": []
             }
         }
     },
-    "17": {
+    "16": {
         "benchmarks": {
             "desktop_ui": {
                 "begin": 5,
@@ -851,21 +743,17 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
             "jetstream_2.2": {
                 "display_name": "jetstream2.crossbench",
                 "arguments": []
             }
         }
     },
-    "18": {
+    "17": {
         "benchmarks": {
             "desktop_ui": {
                 "begin": 7,
@@ -877,6 +765,9 @@
             "dummy_benchmark.stable_benchmark_1": {
                 "abridged": false
             },
+            "jetstream2": {
+                "abridged": false
+            },
             "speedometer": {
                 "abridged": false
             },
@@ -900,30 +791,23 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
             "jetstream_2.2": {
                 "display_name": "jetstream2.crossbench",
                 "arguments": []
             }
         }
     },
-    "19": {
+    "18": {
         "benchmarks": {
-            "jetstream2": {
-                "abridged": false
-            },
             "jetstream2-minorms": {
                 "abridged": false
             },
             "media.desktop": {
-                "end": 6,
+                "end": 8,
                 "abridged": false
             },
             "speedometer": {
@@ -954,12 +838,48 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
+            "jetstream_2.2": {
+                "display_name": "jetstream2.crossbench",
+                "arguments": []
+            }
+        }
+    },
+    "19": {
+        "benchmarks": {
+            "media.desktop": {
+                "begin": 8,
+                "end": 12,
+                "abridged": false
+            },
+            "speedometer": {
+                "abridged": false
+            },
+            "speedometer2-minorms": {
+                "abridged": false
+            },
+            "speedometer2": {
+                "abridged": false
+            },
+            "speedometer2-predictable": {
+                "abridged": false
+            },
+            "speedometer3": {
+                "abridged": false
+            },
+            "speedometer3-minorms": {
+                "abridged": false
+            },
+            "speedometer3-predictable": {
+                "abridged": false
+            }
+        },
+        "crossbench": {
+            "speedometer_3": {
+                "display_name": "speedometer3.crossbench",
                 "arguments": []
             },
             "jetstream_2.2": {
@@ -971,22 +891,24 @@
     "20": {
         "benchmarks": {
             "media.desktop": {
-                "begin": 6,
+                "begin": 12,
                 "abridged": false
             },
             "memory.desktop": {
-                "end": 4,
                 "abridged": false
             }
         }
     },
     "21": {
         "benchmarks": {
-            "memory.desktop": {
-                "begin": 4,
+            "octane": {
                 "abridged": false
             },
-            "octane": {
+            "octane-minorms": {
+                "abridged": false
+            },
+            "power.desktop": {
+                "end": 9,
                 "abridged": false
             }
         },
@@ -999,10 +921,8 @@
     },
     "22": {
         "benchmarks": {
-            "octane-minorms": {
-                "abridged": false
-            },
             "power.desktop": {
+                "begin": 9,
                 "abridged": false
             },
             "rasterize_and_record_micro.top_25": {
@@ -1019,11 +939,7 @@
             },
             "speedometer2-future": {
                 "abridged": false
-            }
-        }
-    },
-    "23": {
-        "benchmarks": {
+            },
             "speedometer2-minorms": {
                 "abridged": false
             },
@@ -1043,22 +959,31 @@
                 "abridged": false
             },
             "system_health.memory_desktop": {
-                "end": 20,
+                "end": 1,
                 "abridged": false
             }
         },
         "crossbench": {
-            "speedometer_2.1": {
-                "display_name": "speedometer2.1.crossbench",
+            "speedometer_2": {
+                "display_name": "speedometer2.crossbench",
                 "arguments": []
             }
         }
     },
+    "23": {
+        "benchmarks": {
+            "system_health.memory_desktop": {
+                "begin": 1,
+                "end": 23,
+                "abridged": false
+            }
+        }
+    },
     "24": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 20,
-                "end": 38,
+                "begin": 23,
+                "end": 43,
                 "abridged": false
             }
         }
@@ -1066,8 +991,8 @@
     "25": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 38,
-                "end": 62,
+                "begin": 43,
+                "end": 65,
                 "abridged": false
             }
         }
@@ -1075,8 +1000,8 @@
     "26": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 62,
-                "end": 74,
+                "begin": 65,
+                "end": 76,
                 "abridged": false
             }
         }
@@ -1084,7 +1009,11 @@
     "27": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 74,
+                "begin": 76,
+                "abridged": false
+            },
+            "v8.browsing_desktop": {
+                "end": 2,
                 "abridged": false
             }
         },
@@ -1094,20 +1023,19 @@
                     "--use-chrome-perf-format"
                 ],
                 "path": "tint_benchmark"
+            },
+            "tracing_perftests": {
+                "path": "tracing_perftests"
             }
         }
     },
     "28": {
         "benchmarks": {
             "v8.browsing_desktop": {
+                "begin": 2,
                 "end": 28,
                 "abridged": false
             }
-        },
-        "executables": {
-            "tracing_perftests": {
-                "path": "tracing_perftests"
-            }
         }
     },
     "29": {
@@ -1128,40 +1056,40 @@
         }
     },
     "extra_infos": {
-        "num_stories": 938,
-        "predicted_min_shard_time": 701.0,
+        "num_stories": 920,
+        "predicted_min_shard_time": 641.0,
         "predicted_min_shard_index": 0,
         "predicted_max_shard_time": 1086.0,
         "predicted_max_shard_index": 29,
-        "shard #0": 701.0,
-        "shard #1": 862.0,
-        "shard #2": 788.0,
-        "shard #3": 770.0,
-        "shard #4": 787.0,
-        "shard #5": 782.0,
-        "shard #6": 779.0,
-        "shard #7": 777.0,
-        "shard #8": 818.0,
-        "shard #9": 772.0,
-        "shard #10": 774.0,
-        "shard #11": 770.0,
-        "shard #12": 776.0,
-        "shard #13": 776.0,
-        "shard #14": 770.0,
-        "shard #15": 767.0,
-        "shard #16": 756.0,
-        "shard #17": 794.0,
-        "shard #18": 749.0,
-        "shard #19": 780.0,
-        "shard #20": 755.0,
-        "shard #21": 776.0,
-        "shard #22": 797.0,
-        "shard #23": 786.0,
-        "shard #24": 783.0,
-        "shard #25": 789.0,
-        "shard #26": 777.0,
-        "shard #27": 813.0,
-        "shard #28": 783.0,
+        "shard #0": 641.0,
+        "shard #1": 802.0,
+        "shard #2": 728.0,
+        "shard #3": 710.0,
+        "shard #4": 747.0,
+        "shard #5": 740.0,
+        "shard #6": 742.0,
+        "shard #7": 746.0,
+        "shard #8": 776.0,
+        "shard #9": 741.0,
+        "shard #10": 738.0,
+        "shard #11": 737.0,
+        "shard #12": 736.0,
+        "shard #13": 739.0,
+        "shard #14": 739.0,
+        "shard #15": 696.0,
+        "shard #16": 734.0,
+        "shard #17": 756.0,
+        "shard #18": 739.0,
+        "shard #19": 770.0,
+        "shard #20": 746.0,
+        "shard #21": 750.0,
+        "shard #22": 755.0,
+        "shard #23": 654.0,
+        "shard #24": 747.0,
+        "shard #25": 753.0,
+        "shard #26": 783.0,
+        "shard #27": 747.0,
+        "shard #28": 735.0,
         "shard #29": 1086.0
     }
 }
diff --git a/tools/perf/core/shard_maps/mac-intel-perf_map.json b/tools/perf/core/shard_maps/mac-intel-perf_map.json
index 8d85013..6cf3f2a 100644
--- a/tools/perf/core/shard_maps/mac-intel-perf_map.json
+++ b/tools/perf/core/shard_maps/mac-intel-perf_map.json
@@ -5,7 +5,7 @@
                 "abridged": false
             },
             "blink_perf.accessibility": {
-                "end": 2,
+                "end": 3,
                 "abridged": false
             },
             "jetstream2": {
@@ -32,21 +32,17 @@
                 "display_name": "jetstream2.crossbench",
                 "arguments": []
             },
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
     "1": {
         "benchmarks": {
             "blink_perf.accessibility": {
-                "begin": 2,
-                "end": 15,
+                "begin": 3,
+                "end": 16,
                 "abridged": false
             },
             "jetstream2": {
@@ -64,24 +60,20 @@
                 "display_name": "jetstream2.crossbench",
                 "arguments": []
             },
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
     "2": {
         "benchmarks": {
             "blink_perf.accessibility": {
-                "begin": 15,
+                "begin": 16,
                 "abridged": false
             },
             "blink_perf.bindings": {
-                "end": 36,
+                "end": 42,
                 "abridged": false
             },
             "jetstream2": {
@@ -99,24 +91,20 @@
                 "display_name": "jetstream2.crossbench",
                 "arguments": []
             },
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
     "3": {
         "benchmarks": {
             "blink_perf.bindings": {
-                "begin": 36,
+                "begin": 42,
                 "abridged": false
             },
             "blink_perf.css": {
-                "end": 66,
+                "end": 74,
                 "abridged": false
             },
             "jetstream2": {
@@ -134,24 +122,20 @@
                 "display_name": "jetstream2.crossbench",
                 "arguments": []
             },
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
     "4": {
         "benchmarks": {
             "blink_perf.css": {
-                "begin": 66,
+                "begin": 74,
                 "abridged": false
             },
             "blink_perf.dom": {
-                "end": 4,
+                "end": 6,
                 "abridged": false
             },
             "jetstream2": {
@@ -169,20 +153,16 @@
                 "display_name": "jetstream2.crossbench",
                 "arguments": []
             },
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
     "5": {
         "benchmarks": {
             "blink_perf.dom": {
-                "begin": 4,
+                "begin": 6,
                 "abridged": false
             },
             "blink_perf.events": {
@@ -192,7 +172,7 @@
                 "abridged": false
             },
             "blink_perf.layout": {
-                "end": 46,
+                "end": 54,
                 "abridged": false
             },
             "speedometer2": {
@@ -203,27 +183,26 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
     "6": {
         "benchmarks": {
             "blink_perf.layout": {
-                "begin": 46,
+                "begin": 54,
                 "abridged": false
             },
             "blink_perf.owp_storage": {
                 "abridged": false
             },
             "blink_perf.parser": {
-                "end": 29,
+                "abridged": false
+            },
+            "blink_perf.shadow_dom": {
+                "end": 15,
                 "abridged": false
             },
             "speedometer2": {
@@ -234,23 +213,16 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
     "7": {
         "benchmarks": {
-            "blink_perf.parser": {
-                "begin": 29,
-                "abridged": false
-            },
             "blink_perf.shadow_dom": {
+                "begin": 15,
                 "abridged": false
             },
             "blink_perf.webaudio": {
@@ -288,13 +260,9 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
@@ -313,7 +281,7 @@
                 "abridged": false
             },
             "media.desktop": {
-                "end": 10,
+                "end": 11,
                 "abridged": false
             },
             "speedometer2": {
@@ -329,24 +297,20 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
     "9": {
         "benchmarks": {
             "media.desktop": {
-                "begin": 10,
+                "begin": 11,
                 "abridged": false
             },
             "memory.desktop": {
-                "end": 3,
+                "end": 5,
                 "abridged": false
             },
             "speedometer2": {
@@ -357,20 +321,16 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
     "10": {
         "benchmarks": {
             "memory.desktop": {
-                "begin": 3,
+                "begin": 5,
                 "abridged": false
             },
             "speedometer2": {
@@ -381,12 +341,12 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
+            "motionmark_1.3": {
+                "display_name": "motionmark1.3.crossbench",
                 "arguments": []
             }
         }
@@ -397,7 +357,10 @@
                 "abridged": false
             },
             "power.desktop": {
-                "end": 8,
+                "abridged": false
+            },
+            "rasterize_and_record_micro.top_25": {
+                "end": 9,
                 "abridged": false
             },
             "speedometer2": {
@@ -408,52 +371,21 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
-            "motionmark_1.3": {
-                "display_name": "motionmark1.3.crossbench",
-                "arguments": []
             }
         }
     },
     "12": {
         "benchmarks": {
-            "power.desktop": {
-                "begin": 8,
-                "abridged": false
-            },
             "rasterize_and_record_micro.top_25": {
+                "begin": 9,
                 "abridged": false
             },
             "rendering.desktop.notracing": {
                 "abridged": false
             },
-            "speedometer2": {
-                "abridged": false
-            },
-            "speedometer3": {
-                "abridged": false
-            }
-        },
-        "crossbench": {
-            "speedometer_3.0": {
-                "display_name": "speedometer3.crossbench",
-                "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            }
-        }
-    },
-    "13": {
-        "benchmarks": {
             "speedometer": {
                 "abridged": false
             },
@@ -473,21 +405,38 @@
                 "abridged": false
             },
             "system_health.common_desktop": {
-                "end": 27,
+                "end": 8,
                 "abridged": false
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
+            "speedometer_2": {
+                "display_name": "speedometer2.crossbench",
                 "arguments": []
+            }
+        }
+    },
+    "13": {
+        "benchmarks": {
+            "system_health.common_desktop": {
+                "begin": 8,
+                "end": 46,
+                "abridged": false
             },
-            "speedometer_2.1": {
-                "display_name": "speedometer2.1.crossbench",
+            "speedometer2": {
+                "abridged": false
+            },
+            "speedometer3": {
+                "abridged": false
+            }
+        },
+        "crossbench": {
+            "speedometer_3": {
+                "display_name": "speedometer3.crossbench",
                 "arguments": []
             }
         }
@@ -495,8 +444,8 @@
     "14": {
         "benchmarks": {
             "system_health.common_desktop": {
-                "begin": 27,
-                "end": 69,
+                "begin": 46,
+                "end": 75,
                 "abridged": false
             },
             "speedometer2": {
@@ -507,24 +456,20 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
     "15": {
         "benchmarks": {
             "system_health.common_desktop": {
-                "begin": 69,
+                "begin": 75,
                 "abridged": false
             },
             "system_health.memory_desktop": {
-                "end": 2,
+                "end": 10,
                 "abridged": false
             },
             "speedometer2": {
@@ -535,21 +480,17 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
     "16": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 2,
-                "end": 17,
+                "begin": 10,
+                "end": 23,
                 "abridged": false
             },
             "speedometer2": {
@@ -560,21 +501,17 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
     "17": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 17,
-                "end": 27,
+                "begin": 23,
+                "end": 35,
                 "abridged": false
             },
             "speedometer2": {
@@ -585,21 +522,17 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
     "18": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 27,
-                "end": 44,
+                "begin": 35,
+                "end": 49,
                 "abridged": false
             },
             "speedometer2": {
@@ -610,21 +543,17 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
     "19": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 44,
-                "end": 57,
+                "begin": 49,
+                "end": 62,
                 "abridged": false
             },
             "speedometer2": {
@@ -635,21 +564,17 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
     "20": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 57,
-                "end": 73,
+                "begin": 62,
+                "end": 74,
                 "abridged": false
             }
         }
@@ -657,18 +582,7 @@
     "21": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 73,
-                "abridged": false
-            }
-        }
-    },
-    "22": {
-        "benchmarks": {
-            "v8.browsing_desktop": {
-                "abridged": false
-            },
-            "v8.browsing_desktop-future": {
-                "end": 1,
+                "begin": 74,
                 "abridged": false
             }
         },
@@ -681,10 +595,21 @@
             }
         }
     },
+    "22": {
+        "benchmarks": {
+            "v8.browsing_desktop": {
+                "abridged": false
+            },
+            "v8.browsing_desktop-future": {
+                "end": 5,
+                "abridged": false
+            }
+        }
+    },
     "23": {
         "benchmarks": {
             "v8.browsing_desktop-future": {
-                "begin": 1,
+                "begin": 5,
                 "abridged": false
             },
             "wasmpspdfkit": {
@@ -704,34 +629,34 @@
         }
     },
     "extra_infos": {
-        "num_stories": 860,
-        "predicted_min_shard_time": 792.0,
-        "predicted_min_shard_index": 10,
-        "predicted_max_shard_time": 1077.0,
+        "num_stories": 842,
+        "predicted_min_shard_time": 836.0,
+        "predicted_min_shard_index": 17,
+        "predicted_max_shard_time": 965.0,
         "predicted_max_shard_index": 7,
-        "shard #0": 929.0,
-        "shard #1": 938.0,
-        "shard #2": 926.0,
-        "shard #3": 942.0,
-        "shard #4": 924.0,
-        "shard #5": 919.0,
-        "shard #6": 933.0,
-        "shard #7": 1077.0,
-        "shard #8": 868.0,
-        "shard #9": 911.0,
-        "shard #10": 792.0,
-        "shard #11": 929.0,
-        "shard #12": 965.0,
-        "shard #13": 940.0,
-        "shard #14": 904.0,
-        "shard #15": 906.0,
-        "shard #16": 953.0,
-        "shard #17": 914.0,
-        "shard #18": 965.0,
-        "shard #19": 938.0,
-        "shard #20": 981.0,
-        "shard #21": 933.0,
-        "shard #22": 899.0,
-        "shard #23": 1013.0
+        "shard #0": 891.0,
+        "shard #1": 878.0,
+        "shard #2": 882.0,
+        "shard #3": 888.0,
+        "shard #4": 864.0,
+        "shard #5": 880.0,
+        "shard #6": 880.0,
+        "shard #7": 965.0,
+        "shard #8": 941.0,
+        "shard #9": 888.0,
+        "shard #10": 922.0,
+        "shard #11": 866.0,
+        "shard #12": 870.0,
+        "shard #13": 867.0,
+        "shard #14": 863.0,
+        "shard #15": 893.0,
+        "shard #16": 860.0,
+        "shard #17": 836.0,
+        "shard #18": 857.0,
+        "shard #19": 875.0,
+        "shard #20": 891.0,
+        "shard #21": 930.0,
+        "shard #22": 836.0,
+        "shard #23": 896.0
     }
 }
diff --git a/tools/perf/core/shard_maps/mac-laptop_low_end-perf_map.json b/tools/perf/core/shard_maps/mac-laptop_low_end-perf_map.json
index b70bd2b..34d1193 100644
--- a/tools/perf/core/shard_maps/mac-laptop_low_end-perf_map.json
+++ b/tools/perf/core/shard_maps/mac-laptop_low_end-perf_map.json
@@ -8,7 +8,7 @@
                 "abridged": false
             },
             "blink_perf.bindings": {
-                "end": 39,
+                "end": 51,
                 "abridged": false
             }
         }
@@ -16,7 +16,7 @@
     "1": {
         "benchmarks": {
             "blink_perf.bindings": {
-                "begin": 39,
+                "begin": 51,
                 "abridged": false
             },
             "blink_perf.css": {
@@ -26,39 +26,34 @@
                 "abridged": false
             },
             "blink_perf.dom": {
-                "end": 11,
+                "abridged": false
+            },
+            "blink_perf.events": {
                 "abridged": false
             }
         }
     },
     "2": {
         "benchmarks": {
-            "blink_perf.dom": {
-                "begin": 11,
-                "abridged": false
-            },
-            "blink_perf.events": {
-                "abridged": false
-            },
             "blink_perf.image_decoder": {
                 "abridged": false
             },
             "blink_perf.layout": {
-                "end": 105,
-                "abridged": false
-            }
-        }
-    },
-    "3": {
-        "benchmarks": {
-            "blink_perf.layout": {
-                "begin": 105,
                 "abridged": false
             },
             "blink_perf.owp_storage": {
                 "abridged": false
             },
             "blink_perf.parser": {
+                "end": 21,
+                "abridged": false
+            }
+        }
+    },
+    "3": {
+        "benchmarks": {
+            "blink_perf.parser": {
+                "begin": 21,
                 "abridged": false
             },
             "blink_perf.shadow_dom": {
@@ -83,15 +78,6 @@
                 "abridged": false
             },
             "desktop_ui": {
-                "end": 5,
-                "abridged": false
-            }
-        }
-    },
-    "4": {
-        "benchmarks": {
-            "desktop_ui": {
-                "begin": 5,
                 "abridged": false
             },
             "dummy_benchmark.noisy_benchmark_1": {
@@ -101,10 +87,7 @@
                 "abridged": false
             },
             "media.desktop": {
-                "abridged": false
-            },
-            "memory.desktop": {
-                "end": 1,
+                "end": 4,
                 "abridged": false
             }
         },
@@ -114,14 +97,30 @@
             }
         }
     },
+    "4": {
+        "benchmarks": {
+            "media.desktop": {
+                "begin": 4,
+                "abridged": false
+            },
+            "memory.desktop": {
+                "end": 5,
+                "abridged": false
+            }
+        }
+    },
     "5": {
         "benchmarks": {
             "memory.desktop": {
-                "begin": 1,
+                "begin": 5,
                 "abridged": false
             },
             "octane": {
                 "abridged": false
+            },
+            "power.desktop": {
+                "end": 9,
+                "abridged": false
             }
         },
         "crossbench": {
@@ -134,13 +133,14 @@
     "6": {
         "benchmarks": {
             "power.desktop": {
+                "begin": 9,
                 "abridged": false
             },
             "rasterize_and_record_micro.top_25": {
                 "abridged": false
             },
             "rendering.desktop": {
-                "end": 14,
+                "end": 27,
                 "abridged": false
             }
         }
@@ -148,8 +148,8 @@
     "7": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 14,
-                "end": 64,
+                "begin": 27,
+                "end": 74,
                 "abridged": false
             }
         }
@@ -157,8 +157,8 @@
     "8": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 64,
-                "end": 112,
+                "begin": 74,
+                "end": 120,
                 "abridged": false
             }
         }
@@ -166,8 +166,8 @@
     "9": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 112,
-                "end": 158,
+                "begin": 120,
+                "end": 162,
                 "abridged": false
             }
         }
@@ -175,8 +175,8 @@
     "10": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 158,
-                "end": 207,
+                "begin": 162,
+                "end": 208,
                 "abridged": false
             }
         }
@@ -184,8 +184,8 @@
     "11": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 207,
-                "end": 251,
+                "begin": 208,
+                "end": 253,
                 "abridged": false
             }
         }
@@ -193,8 +193,8 @@
     "12": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 251,
-                "end": 283,
+                "begin": 253,
+                "end": 284,
                 "abridged": false
             }
         }
@@ -202,7 +202,7 @@
     "13": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 283,
+                "begin": 284,
                 "end": 330,
                 "abridged": false
             }
@@ -236,7 +236,7 @@
                 "abridged": false
             },
             "system_health.common_desktop": {
-                "end": 2,
+                "end": 5,
                 "abridged": false
             }
         },
@@ -250,7 +250,7 @@
     "15": {
         "benchmarks": {
             "system_health.common_desktop": {
-                "begin": 2,
+                "begin": 5,
                 "end": 56,
                 "abridged": false
             }
@@ -281,7 +281,7 @@
         "benchmarks": {
             "system_health.memory_desktop": {
                 "begin": 24,
-                "end": 47,
+                "end": 46,
                 "abridged": false
             }
         }
@@ -289,8 +289,8 @@
     "19": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 47,
-                "end": 70,
+                "begin": 46,
+                "end": 69,
                 "abridged": false
             }
         }
@@ -298,18 +298,23 @@
     "20": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 70,
+                "begin": 69,
+                "end": 78,
                 "abridged": false
             }
         }
     },
     "21": {
         "benchmarks": {
+            "system_health.memory_desktop": {
+                "begin": 78,
+                "abridged": false
+            },
             "v8.browsing_desktop": {
                 "abridged": false
             },
             "v8.browsing_desktop-future": {
-                "end": 9,
+                "end": 4,
                 "abridged": false
             }
         }
@@ -317,7 +322,7 @@
     "22": {
         "benchmarks": {
             "v8.browsing_desktop-future": {
-                "begin": 9,
+                "begin": 4,
                 "abridged": false
             },
             "wasmpspdfkit": {
@@ -329,33 +334,33 @@
         }
     },
     "extra_infos": {
-        "num_stories": 1125,
-        "predicted_min_shard_time": 1221.0,
-        "predicted_min_shard_index": 21,
-        "predicted_max_shard_time": 1401.0,
-        "predicted_max_shard_index": 19,
-        "shard #0": 1283.0,
-        "shard #1": 1264.0,
-        "shard #2": 1286.0,
-        "shard #3": 1280.0,
-        "shard #4": 1283.0,
-        "shard #5": 1291.0,
-        "shard #6": 1281.0,
-        "shard #7": 1278.0,
-        "shard #8": 1286.0,
-        "shard #9": 1291.0,
-        "shard #10": 1275.0,
-        "shard #11": 1299.0,
-        "shard #12": 1291.0,
-        "shard #13": 1273.0,
-        "shard #14": 1283.0,
-        "shard #15": 1275.0,
-        "shard #16": 1249.0,
-        "shard #17": 1287.0,
-        "shard #18": 1257.0,
-        "shard #19": 1401.0,
-        "shard #20": 1299.0,
-        "shard #21": 1221.0,
-        "shard #22": 1231.0
+        "num_stories": 1123,
+        "predicted_min_shard_time": 1388.0,
+        "predicted_min_shard_index": 8,
+        "predicted_max_shard_time": 1470.0,
+        "predicted_max_shard_index": 18,
+        "shard #0": 1420.0,
+        "shard #1": 1418.0,
+        "shard #2": 1428.0,
+        "shard #3": 1420.0,
+        "shard #4": 1446.0,
+        "shard #5": 1419.0,
+        "shard #6": 1437.0,
+        "shard #7": 1413.0,
+        "shard #8": 1388.0,
+        "shard #9": 1407.0,
+        "shard #10": 1412.0,
+        "shard #11": 1452.0,
+        "shard #12": 1430.0,
+        "shard #13": 1427.0,
+        "shard #14": 1426.0,
+        "shard #15": 1424.0,
+        "shard #16": 1399.0,
+        "shard #17": 1416.0,
+        "shard #18": 1470.0,
+        "shard #19": 1440.0,
+        "shard #20": 1452.0,
+        "shard #21": 1404.0,
+        "shard #22": 1434.0
     }
-}
\ No newline at end of file
+}
diff --git a/tools/perf/core/shard_maps/mac-m1-pro-perf_map.json b/tools/perf/core/shard_maps/mac-m1-pro-perf_map.json
index 5453dc7..7cb1845 100644
--- a/tools/perf/core/shard_maps/mac-m1-pro-perf_map.json
+++ b/tools/perf/core/shard_maps/mac-m1-pro-perf_map.json
@@ -9,14 +9,10 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
             "jetstream_2.2": {
                 "display_name": "jetstream2.crossbench",
                 "arguments": []
@@ -30,14 +26,10 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
             "motionmark_1.3": {
                 "display_name": "motionmark1.3.crossbench",
                 "arguments": []
@@ -54,13 +46,9 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
@@ -74,29 +62,25 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
-            "speedometer_2.1": {
-                "display_name": "speedometer2.1.crossbench",
+            "speedometer_2": {
+                "display_name": "speedometer2.crossbench",
                 "arguments": []
             }
         }
     },
     "extra_infos": {
-        "num_stories": 18,
-        "predicted_min_shard_time": 233.0,
+        "num_stories": 14,
+        "predicted_min_shard_time": 173.0,
         "predicted_min_shard_index": 3,
-        "predicted_max_shard_time": 509.0,
+        "predicted_max_shard_time": 449.0,
         "predicted_max_shard_index": 1,
-        "shard #0": 390.0,
-        "shard #1": 509.0,
-        "shard #2": 476.0,
-        "shard #3": 233.0
+        "shard #0": 330.0,
+        "shard #1": 449.0,
+        "shard #2": 416.0,
+        "shard #3": 173.0
     }
 }
diff --git a/tools/perf/core/shard_maps/mac-m1_mini_2020-perf-pgo_map.json b/tools/perf/core/shard_maps/mac-m1_mini_2020-perf-pgo_map.json
index 5adf18a6..3ea90e3 100644
--- a/tools/perf/core/shard_maps/mac-m1_mini_2020-perf-pgo_map.json
+++ b/tools/perf/core/shard_maps/mac-m1_mini_2020-perf-pgo_map.json
@@ -22,13 +22,9 @@
     },
     "1": {
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         },
         "benchmarks": {
@@ -68,13 +64,9 @@
     },
     "3": {
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         },
         "benchmarks": {
@@ -96,10 +88,6 @@
             "jetstream_2.2": {
                 "display_name": "jetstream2.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.0": {
-                "display_name": "speedometer3.crossbench",
-                "arguments": []
             }
         },
         "benchmarks": {
@@ -118,8 +106,8 @@
     },
     "5": {
         "crossbench": {
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
+            "speedometer_3": {
+                "display_name": "speedometer3.crossbench",
                 "arguments": []
             }
         },
@@ -156,36 +144,32 @@
                 "display_name": "jetstream2.crossbench",
                 "arguments": []
             },
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
             "motionmark_1.3": {
                 "display_name": "motionmark1.3.crossbench",
                 "arguments": []
             },
-            "speedometer_2.1": {
-                "display_name": "speedometer2.1.crossbench",
+            "speedometer_2": {
+                "display_name": "speedometer2.crossbench",
                 "arguments": []
             }
         }
     },
     "extra_infos": {
-        "num_stories": 33,
+        "num_stories": 29,
         "predicted_min_shard_time": 1182.0,
         "predicted_min_shard_index": 5,
-        "predicted_max_shard_time": 2191.0,
+        "predicted_max_shard_time": 2131.0,
         "predicted_max_shard_index": 6,
         "shard #0": 1327.0,
-        "shard #1": 1267.0,
+        "shard #1": 1207.0,
         "shard #2": 1327.0,
-        "shard #3": 1267.0,
-        "shard #4": 1387.0,
+        "shard #3": 1207.0,
+        "shard #4": 1327.0,
         "shard #5": 1182.0,
-        "shard #6": 2191.0
+        "shard #6": 2131.0
     }
 }
diff --git a/tools/perf/core/shard_maps/mac-m1_mini_2020-perf_map.json b/tools/perf/core/shard_maps/mac-m1_mini_2020-perf_map.json
index dbb7e9b..b900be8 100644
--- a/tools/perf/core/shard_maps/mac-m1_mini_2020-perf_map.json
+++ b/tools/perf/core/shard_maps/mac-m1_mini_2020-perf_map.json
@@ -32,13 +32,9 @@
                 "display_name": "jetstream2.crossbench",
                 "arguments": []
             },
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
@@ -48,7 +44,7 @@
                 "abridged": false
             },
             "blink_perf.accessibility": {
-                "end": 8,
+                "end": 7,
                 "abridged": false
             },
             "jetstream2": {
@@ -122,27 +118,20 @@
                 "display_name": "jetstream2.crossbench",
                 "arguments": []
             },
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
     "3": {
         "benchmarks": {
             "blink_perf.accessibility": {
-                "begin": 8,
+                "begin": 7,
                 "abridged": false
             },
             "blink_perf.bindings": {
-                "abridged": false
-            },
-            "blink_perf.css": {
-                "end": 32,
+                "end": 48,
                 "abridged": false
             },
             "jetstream2": {
@@ -207,30 +196,23 @@
                 "display_name": "jetstream2.crossbench",
                 "arguments": []
             },
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
     "5": {
         "benchmarks": {
+            "blink_perf.bindings": {
+                "begin": 48,
+                "abridged": false
+            },
             "blink_perf.css": {
-                "begin": 32,
                 "abridged": false
             },
             "blink_perf.dom": {
-                "abridged": false
-            },
-            "blink_perf.events": {
-                "abridged": false
-            },
-            "blink_perf.image_decoder": {
-                "end": 9,
+                "end": 13,
                 "abridged": false
             },
             "jetstream2": {
@@ -265,12 +247,18 @@
                 "abridged": false,
                 "pageset_repeat": 2
             },
+            "blink_perf.dom": {
+                "begin": 13,
+                "abridged": false
+            },
+            "blink_perf.events": {
+                "abridged": false
+            },
             "blink_perf.image_decoder": {
-                "begin": 9,
                 "abridged": false
             },
             "blink_perf.layout": {
-                "end": 85,
+                "end": 51,
                 "abridged": false
             },
             "speedometer2": {
@@ -293,14 +281,10 @@
                 "display_name": "jetstream2.crossbench",
                 "arguments": []
             },
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
             "motionmark_1.3": {
                 "display_name": "motionmark1.3.crossbench",
                 "arguments": []
@@ -314,17 +298,14 @@
                 "pageset_repeat": 2
             },
             "blink_perf.layout": {
-                "begin": 85,
+                "begin": 51,
                 "abridged": false
             },
             "blink_perf.owp_storage": {
                 "abridged": false
             },
             "blink_perf.parser": {
-                "abridged": false
-            },
-            "blink_perf.shadow_dom": {
-                "end": 19,
+                "end": 12,
                 "abridged": false
             },
             "speedometer2": {
@@ -347,14 +328,10 @@
                 "display_name": "jetstream2.crossbench",
                 "arguments": []
             },
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
             "motionmark_1.3": {
                 "display_name": "motionmark1.3.crossbench",
                 "arguments": []
@@ -367,8 +344,11 @@
                 "abridged": false,
                 "pageset_repeat": 2
             },
+            "blink_perf.parser": {
+                "begin": 12,
+                "abridged": false
+            },
             "blink_perf.shadow_dom": {
-                "begin": 19,
                 "abridged": false
             },
             "blink_perf.webaudio": {
@@ -389,10 +369,6 @@
             "blink_perf.webgpu_fast_call": {
                 "abridged": false
             },
-            "desktop_ui": {
-                "end": 8,
-                "abridged": false
-            },
             "speedometer2": {
                 "abridged": false,
                 "pageset_repeat": 2
@@ -418,14 +394,10 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
             "motionmark_1.3": {
                 "display_name": "motionmark1.3.crossbench",
                 "arguments": []
@@ -439,7 +411,6 @@
                 "pageset_repeat": 2
             },
             "desktop_ui": {
-                "begin": 8,
                 "abridged": false
             },
             "dummy_benchmark.noisy_benchmark_1": {
@@ -468,14 +439,10 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
             "motionmark_1.3": {
                 "display_name": "motionmark1.3.crossbench",
                 "arguments": []
@@ -492,7 +459,7 @@
                 "abridged": false
             },
             "media.desktop": {
-                "end": 13,
+                "end": 14,
                 "abridged": false
             },
             "speedometer2": {
@@ -511,14 +478,10 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
             "motionmark_1.3": {
                 "display_name": "motionmark1.3.crossbench",
                 "arguments": []
@@ -532,7 +495,7 @@
                 "pageset_repeat": 2
             },
             "media.desktop": {
-                "begin": 13,
+                "begin": 14,
                 "abridged": false
             },
             "memory.desktop": {
@@ -555,14 +518,10 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
             "motionmark_1.3": {
                 "display_name": "motionmark1.3.crossbench",
                 "arguments": []
@@ -602,14 +561,10 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
             "motionmark_1.3": {
                 "display_name": "motionmark1.3.crossbench",
                 "arguments": []
@@ -630,7 +585,7 @@
                 "abridged": false
             },
             "rendering.desktop": {
-                "end": 11,
+                "end": 12,
                 "abridged": false
             },
             "speedometer2": {
@@ -649,14 +604,10 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
             "motionmark_1.3": {
                 "display_name": "motionmark1.3.crossbench",
                 "arguments": []
@@ -670,8 +621,8 @@
                 "pageset_repeat": 2
             },
             "rendering.desktop": {
-                "begin": 11,
-                "end": 43,
+                "begin": 12,
+                "end": 45,
                 "abridged": false
             },
             "speedometer2": {
@@ -690,14 +641,10 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
             "motionmark_1.3": {
                 "display_name": "motionmark1.3.crossbench",
                 "arguments": []
@@ -711,8 +658,8 @@
                 "pageset_repeat": 2
             },
             "rendering.desktop": {
-                "begin": 43,
-                "end": 76,
+                "begin": 45,
+                "end": 79,
                 "abridged": false
             },
             "speedometer2": {
@@ -731,14 +678,10 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
             "motionmark_1.3": {
                 "display_name": "motionmark1.3.crossbench",
                 "arguments": []
@@ -752,8 +695,8 @@
                 "pageset_repeat": 2
             },
             "rendering.desktop": {
-                "begin": 76,
-                "end": 110,
+                "begin": 79,
+                "end": 115,
                 "abridged": false
             },
             "speedometer2": {
@@ -772,14 +715,10 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
             "motionmark_1.3": {
                 "display_name": "motionmark1.3.crossbench",
                 "arguments": []
@@ -793,8 +732,8 @@
                 "pageset_repeat": 2
             },
             "rendering.desktop": {
-                "begin": 110,
-                "end": 143,
+                "begin": 115,
+                "end": 148,
                 "abridged": false
             },
             "speedometer2": {
@@ -813,14 +752,10 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
             "motionmark_1.3": {
                 "display_name": "motionmark1.3.crossbench",
                 "arguments": []
@@ -834,8 +769,8 @@
                 "pageset_repeat": 2
             },
             "rendering.desktop": {
-                "begin": 143,
-                "end": 171,
+                "begin": 148,
+                "end": 176,
                 "abridged": false
             },
             "speedometer2": {
@@ -854,14 +789,10 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
             "motionmark_1.3": {
                 "display_name": "motionmark1.3.crossbench",
                 "arguments": []
@@ -875,8 +806,8 @@
                 "pageset_repeat": 2
             },
             "rendering.desktop": {
-                "begin": 171,
-                "end": 202,
+                "begin": 176,
+                "end": 204,
                 "abridged": false
             },
             "speedometer2": {
@@ -895,14 +826,10 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
             "motionmark_1.3": {
                 "display_name": "motionmark1.3.crossbench",
                 "arguments": []
@@ -916,8 +843,8 @@
                 "pageset_repeat": 2
             },
             "rendering.desktop": {
-                "begin": 202,
-                "end": 220,
+                "begin": 204,
+                "end": 225,
                 "abridged": false
             },
             "speedometer3": {
@@ -929,14 +856,10 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
             "motionmark_1.3": {
                 "display_name": "motionmark1.3.crossbench",
                 "arguments": []
@@ -950,8 +873,8 @@
                 "pageset_repeat": 2
             },
             "rendering.desktop": {
-                "begin": 220,
-                "end": 257,
+                "begin": 225,
+                "end": 263,
                 "abridged": false
             },
             "speedometer3": {
@@ -963,14 +886,10 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
             "motionmark_1.3": {
                 "display_name": "motionmark1.3.crossbench",
                 "arguments": []
@@ -984,8 +903,8 @@
                 "pageset_repeat": 2
             },
             "rendering.desktop": {
-                "begin": 257,
-                "end": 281,
+                "begin": 263,
+                "end": 292,
                 "abridged": false
             },
             "speedometer3": {
@@ -997,14 +916,10 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
             "motionmark_1.3": {
                 "display_name": "motionmark1.3.crossbench",
                 "arguments": []
@@ -1014,7 +929,7 @@
     "23": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 281,
+                "begin": 292,
                 "abridged": false
             },
             "speedometer": {
@@ -1044,13 +959,13 @@
                 "abridged": false
             },
             "system_health.common_desktop": {
-                "end": 19,
+                "end": 24,
                 "abridged": false
             }
         },
         "crossbench": {
-            "speedometer_2.1": {
-                "display_name": "speedometer2.1.crossbench",
+            "speedometer_2": {
+                "display_name": "speedometer2.crossbench",
                 "arguments": []
             }
         }
@@ -1058,11 +973,11 @@
     "24": {
         "benchmarks": {
             "system_health.common_desktop": {
-                "begin": 19,
+                "begin": 24,
                 "abridged": false
             },
             "system_health.memory_desktop": {
-                "end": 9,
+                "end": 10,
                 "abridged": false
             },
             "speedometer3": {
@@ -1077,8 +992,8 @@
     "25": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 9,
-                "end": 41,
+                "begin": 10,
+                "end": 44,
                 "abridged": false
             },
             "speedometer3": {
@@ -1093,8 +1008,8 @@
     "26": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 41,
-                "end": 73,
+                "begin": 44,
+                "end": 74,
                 "abridged": false
             },
             "speedometer3": {
@@ -1109,7 +1024,7 @@
     "27": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 73,
+                "begin": 74,
                 "abridged": false
             },
             "v8.browsing_desktop": {
@@ -1148,38 +1063,38 @@
         }
     },
     "extra_infos": {
-        "num_stories": 1308,
-        "predicted_min_shard_time": 1888.0,
+        "num_stories": 1290,
+        "predicted_min_shard_time": 1828.0,
         "predicted_min_shard_index": 0,
-        "predicted_max_shard_time": 2871.0,
+        "predicted_max_shard_time": 2700.0,
         "predicted_max_shard_index": 27,
-        "shard #0": 1888.0,
-        "shard #1": 2031.0,
-        "shard #2": 1888.0,
-        "shard #3": 2018.0,
-        "shard #4": 1888.0,
-        "shard #5": 2022.0,
-        "shard #6": 2025.0,
-        "shard #7": 2020.0,
-        "shard #8": 2029.0,
-        "shard #9": 2247.0,
-        "shard #10": 2014.0,
-        "shard #11": 2042.0,
-        "shard #12": 2027.0,
-        "shard #13": 2008.0,
-        "shard #14": 2003.0,
-        "shard #15": 2012.0,
-        "shard #16": 2005.0,
-        "shard #17": 2016.0,
-        "shard #18": 2017.0,
-        "shard #19": 2003.0,
-        "shard #20": 2003.0,
-        "shard #21": 2004.0,
-        "shard #22": 2006.0,
-        "shard #23": 1996.0,
-        "shard #24": 1947.0,
-        "shard #25": 2055.0,
-        "shard #26": 1941.0,
-        "shard #27": 2871.0
+        "shard #0": 1828.0,
+        "shard #1": 1933.0,
+        "shard #2": 1828.0,
+        "shard #3": 1980.0,
+        "shard #4": 1828.0,
+        "shard #5": 1985.0,
+        "shard #6": 1987.0,
+        "shard #7": 1984.0,
+        "shard #8": 2076.0,
+        "shard #9": 2227.0,
+        "shard #10": 1965.0,
+        "shard #11": 1971.0,
+        "shard #12": 1967.0,
+        "shard #13": 1970.0,
+        "shard #14": 1961.0,
+        "shard #15": 1969.0,
+        "shard #16": 1965.0,
+        "shard #17": 1973.0,
+        "shard #18": 1963.0,
+        "shard #19": 1948.0,
+        "shard #20": 1973.0,
+        "shard #21": 1972.0,
+        "shard #22": 1965.0,
+        "shard #23": 1975.0,
+        "shard #24": 1966.0,
+        "shard #25": 1980.0,
+        "shard #26": 2007.0,
+        "shard #27": 2700.0
     }
 }
diff --git a/tools/perf/core/shard_maps/mac-m2-pro-perf_map.json b/tools/perf/core/shard_maps/mac-m2-pro-perf_map.json
index a8236b7..8bf1ea6 100644
--- a/tools/perf/core/shard_maps/mac-m2-pro-perf_map.json
+++ b/tools/perf/core/shard_maps/mac-m2-pro-perf_map.json
@@ -32,7 +32,7 @@
                 "abridged": false
             },
             "blink_perf.layout": {
-                "end": 78,
+                "end": 73,
                 "abridged": false
             }
         }
@@ -40,7 +40,7 @@
     "2": {
         "benchmarks": {
             "blink_perf.layout": {
-                "begin": 78,
+                "begin": 73,
                 "abridged": false
             },
             "blink_perf.owp_storage": {
@@ -93,7 +93,7 @@
                 "abridged": false
             },
             "memory.desktop": {
-                "end": 3,
+                "end": 2,
                 "abridged": false
             }
         },
@@ -107,14 +107,14 @@
     "4": {
         "benchmarks": {
             "memory.desktop": {
-                "begin": 3,
+                "begin": 2,
                 "abridged": false
             },
             "octane": {
                 "abridged": false
             },
             "power.desktop": {
-                "end": 4,
+                "end": 2,
                 "abridged": false
             }
         },
@@ -128,14 +128,14 @@
     "5": {
         "benchmarks": {
             "power.desktop": {
-                "begin": 4,
+                "begin": 2,
                 "abridged": false
             },
             "rasterize_and_record_micro.top_25": {
                 "abridged": false
             },
             "rendering.desktop": {
-                "end": 27,
+                "end": 23,
                 "abridged": false
             }
         }
@@ -143,8 +143,8 @@
     "6": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 27,
-                "end": 88,
+                "begin": 23,
+                "end": 83,
                 "abridged": false
             }
         }
@@ -152,8 +152,8 @@
     "7": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 88,
-                "end": 149,
+                "begin": 83,
+                "end": 144,
                 "abridged": false
             }
         }
@@ -161,8 +161,8 @@
     "8": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 149,
-                "end": 203,
+                "begin": 144,
+                "end": 199,
                 "abridged": false
             }
         }
@@ -170,8 +170,8 @@
     "9": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 203,
-                "end": 249,
+                "begin": 199,
+                "end": 244,
                 "abridged": false
             }
         }
@@ -179,8 +179,8 @@
     "10": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 249,
-                "end": 295,
+                "begin": 244,
+                "end": 291,
                 "abridged": false
             }
         }
@@ -188,8 +188,8 @@
     "11": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 295,
-                "end": 348,
+                "begin": 291,
+                "end": 345,
                 "abridged": false
             }
         }
@@ -197,7 +197,7 @@
     "12": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 348,
+                "begin": 345,
                 "abridged": false
             },
             "rendering.desktop.notracing": {
@@ -228,20 +228,16 @@
                 "abridged": false
             },
             "system_health.common_desktop": {
-                "end": 21,
+                "end": 20,
                 "abridged": false
             }
         },
         "crossbench": {
-            "speedometer_2.1": {
-                "display_name": "speedometer2.1.crossbench",
+            "speedometer_2": {
+                "display_name": "speedometer2.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             }
@@ -250,8 +246,8 @@
     "13": {
         "benchmarks": {
             "system_health.common_desktop": {
-                "begin": 21,
-                "end": 73,
+                "begin": 20,
+                "end": 72,
                 "abridged": false
             }
         }
@@ -259,11 +255,11 @@
     "14": {
         "benchmarks": {
             "system_health.common_desktop": {
-                "begin": 73,
+                "begin": 72,
                 "abridged": false
             },
             "system_health.memory_desktop": {
-                "end": 17,
+                "end": 16,
                 "abridged": false
             }
         }
@@ -271,8 +267,8 @@
     "15": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 17,
-                "end": 37,
+                "begin": 16,
+                "end": 36,
                 "abridged": false
             }
         }
@@ -280,8 +276,8 @@
     "16": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 37,
-                "end": 59,
+                "begin": 36,
+                "end": 58,
                 "abridged": false
             }
         }
@@ -289,7 +285,7 @@
     "17": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 59,
+                "begin": 58,
                 "end": 76,
                 "abridged": false
             }
@@ -325,29 +321,29 @@
         }
     },
     "extra_infos": {
-        "num_stories": 1123,
-        "predicted_min_shard_time": 1070.0,
-        "predicted_min_shard_index": 18,
-        "predicted_max_shard_time": 1144.0,
-        "predicted_max_shard_index": 3,
+        "num_stories": 1124,
+        "predicted_min_shard_time": 1059.0,
+        "predicted_min_shard_index": 3,
+        "predicted_max_shard_time": 1164.0,
+        "predicted_max_shard_index": 17,
         "shard #0": 1103.0,
-        "shard #1": 1103.0,
-        "shard #2": 1101.0,
-        "shard #3": 1144.0,
-        "shard #4": 1089.0,
-        "shard #5": 1107.0,
-        "shard #6": 1108.0,
-        "shard #7": 1104.0,
-        "shard #8": 1092.0,
-        "shard #9": 1101.0,
-        "shard #10": 1109.0,
-        "shard #11": 1109.0,
-        "shard #12": 1118.0,
-        "shard #13": 1108.0,
-        "shard #14": 1136.0,
-        "shard #15": 1089.0,
+        "shard #1": 1106.0,
+        "shard #2": 1118.0,
+        "shard #3": 1059.0,
+        "shard #4": 1094.0,
+        "shard #5": 1105.0,
+        "shard #6": 1099.0,
+        "shard #7": 1112.0,
+        "shard #8": 1102.0,
+        "shard #9": 1104.0,
+        "shard #10": 1103.0,
+        "shard #11": 1087.0,
+        "shard #12": 1122.0,
+        "shard #13": 1098.0,
+        "shard #14": 1090.0,
+        "shard #15": 1128.0,
         "shard #16": 1107.0,
-        "shard #17": 1113.0,
+        "shard #17": 1164.0,
         "shard #18": 1070.0,
         "shard #19": 1072.0
     }
diff --git a/tools/perf/core/shard_maps/mac-m3-pro-perf_map.json b/tools/perf/core/shard_maps/mac-m3-pro-perf_map.json
index ebb5b31f..d5f6cfa2 100644
--- a/tools/perf/core/shard_maps/mac-m3-pro-perf_map.json
+++ b/tools/perf/core/shard_maps/mac-m3-pro-perf_map.json
@@ -1,10 +1,6 @@
 {
     "0": {
         "crossbench": {
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
             "jetstream_2.2": {
                 "display_name": "jetstream2.crossbench",
                 "arguments": []
@@ -14,19 +10,6 @@
     },
     "1": {
         "crossbench": {
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            }
-        },
-        "benchmarks": {}
-    },
-    "2": {
-        "crossbench": {
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
             "motionmark_1.3": {
                 "display_name": "motionmark1.3.crossbench",
                 "arguments": []
@@ -34,17 +17,18 @@
         },
         "benchmarks": {}
     },
+    "2": {
+        "crossbench": {
+            "speedometer_2": {
+                "display_name": "speedometer2.crossbench",
+                "arguments": []
+            }
+        },
+        "benchmarks": {}
+    },
     "3": {
         "crossbench": {
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
-            "speedometer_2.1": {
-                "display_name": "speedometer2.1.crossbench",
-                "arguments": []
-            },
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             }
@@ -52,14 +36,14 @@
         "benchmarks": {}
     },
     "extra_infos": {
-        "num_stories": 8,
+        "num_stories": 4,
         "predicted_min_shard_time": 60,
-        "predicted_min_shard_index": 1,
-        "predicted_max_shard_time": 420,
-        "predicted_max_shard_index": 2,
-        "shard #0": 240,
-        "shard #1": 60,
-        "shard #2": 420,
-        "shard #3": 180
+        "predicted_min_shard_index": 2,
+        "predicted_max_shard_time": 360,
+        "predicted_max_shard_index": 1,
+        "shard #0": 180,
+        "shard #1": 360,
+        "shard #2": 60,
+        "shard #3": 60
     }
 }
diff --git a/tools/perf/core/shard_maps/win-10-perf_map.json b/tools/perf/core/shard_maps/win-10-perf_map.json
index f01930a..3fa2e07 100644
--- a/tools/perf/core/shard_maps/win-10-perf_map.json
+++ b/tools/perf/core/shard_maps/win-10-perf_map.json
@@ -8,7 +8,7 @@
                 "abridged": false
             },
             "blink_perf.bindings": {
-                "end": 10,
+                "end": 11,
                 "abridged": false
             },
             "jetstream2": {
@@ -48,20 +48,16 @@
                 "display_name": "jetstream2.crossbench",
                 "arguments": []
             },
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
     "1": {
         "benchmarks": {
             "blink_perf.bindings": {
-                "begin": 10,
+                "begin": 11,
                 "abridged": false
             },
             "blink_perf.css": {
@@ -99,13 +95,9 @@
                 "display_name": "jetstream2.crossbench",
                 "arguments": []
             },
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
@@ -122,7 +114,7 @@
                 "abridged": false
             },
             "blink_perf.layout": {
-                "end": 85,
+                "end": 86,
                 "abridged": false
             },
             "jetstream2": {
@@ -153,20 +145,16 @@
                 "display_name": "jetstream2.crossbench",
                 "arguments": []
             },
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
     "3": {
         "benchmarks": {
             "blink_perf.layout": {
-                "begin": 85,
+                "begin": 86,
                 "abridged": false
             },
             "blink_perf.owp_storage": {
@@ -207,13 +195,9 @@
                 "display_name": "jetstream2.crossbench",
                 "arguments": []
             },
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
@@ -284,13 +268,9 @@
                 "display_name": "jetstream2.crossbench",
                 "arguments": []
             },
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
@@ -336,13 +316,9 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
@@ -380,14 +356,10 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
             "motionmark_1.3": {
                 "display_name": "motionmark1.3.crossbench",
                 "arguments": []
@@ -428,13 +400,9 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
@@ -466,13 +434,9 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
@@ -504,13 +468,9 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
@@ -529,13 +489,9 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
@@ -572,16 +528,12 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
-            "speedometer_2.1": {
-                "display_name": "speedometer2.1.crossbench",
+            "speedometer_2": {
+                "display_name": "speedometer2.crossbench",
                 "arguments": []
             }
         }
@@ -601,13 +553,9 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
@@ -629,13 +577,9 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
@@ -654,13 +598,9 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
@@ -679,13 +619,9 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
@@ -707,13 +643,9 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
@@ -748,39 +680,35 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
     "extra_infos": {
-        "num_stories": 1227,
-        "predicted_min_shard_time": 2166.0,
+        "num_stories": 1211,
+        "predicted_min_shard_time": 2106.0,
         "predicted_min_shard_index": 13,
-        "predicted_max_shard_time": 2448.0,
+        "predicted_max_shard_time": 2388.0,
         "predicted_max_shard_index": 17,
-        "shard #0": 2243.0,
-        "shard #1": 2266.0,
-        "shard #2": 2243.0,
-        "shard #3": 2254.0,
-        "shard #4": 2308.0,
-        "shard #5": 2286.0,
-        "shard #6": 2222.0,
-        "shard #7": 2230.0,
-        "shard #8": 2247.0,
-        "shard #9": 2253.0,
-        "shard #10": 2264.0,
-        "shard #11": 2223.0,
-        "shard #12": 2207.0,
-        "shard #13": 2166.0,
-        "shard #14": 2250.0,
-        "shard #15": 2283.0,
-        "shard #16": 2267.0,
-        "shard #17": 2448.0
+        "shard #0": 2193.0,
+        "shard #1": 2206.0,
+        "shard #2": 2193.0,
+        "shard #3": 2194.0,
+        "shard #4": 2248.0,
+        "shard #5": 2226.0,
+        "shard #6": 2162.0,
+        "shard #7": 2170.0,
+        "shard #8": 2187.0,
+        "shard #9": 2193.0,
+        "shard #10": 2204.0,
+        "shard #11": 2163.0,
+        "shard #12": 2147.0,
+        "shard #13": 2106.0,
+        "shard #14": 2190.0,
+        "shard #15": 2223.0,
+        "shard #16": 2207.0,
+        "shard #17": 2388.0
     }
 }
diff --git a/tools/perf/core/shard_maps/win-10_amd_laptop-perf_map.json b/tools/perf/core/shard_maps/win-10_amd_laptop-perf_map.json
index 92d3ba2..3018299 100644
--- a/tools/perf/core/shard_maps/win-10_amd_laptop-perf_map.json
+++ b/tools/perf/core/shard_maps/win-10_amd_laptop-perf_map.json
@@ -7,20 +7,19 @@
         }
     },
     "1": {
-        "benchmarks": {
-            "octane": {
-                "abridged": false
-            }
-        },
         "crossbench": {
             "jetstream_2.2": {
                 "display_name": "jetstream2.crossbench",
                 "arguments": []
             }
-        }
+        },
+        "benchmarks": {}
     },
     "2": {
         "benchmarks": {
+            "octane": {
+                "abridged": false
+            },
             "speedometer2": {
                 "abridged": false
             },
@@ -29,24 +28,20 @@
             }
         },
         "crossbench": {
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             }
         }
     },
     "extra_infos": {
-        "num_stories": 7,
+        "num_stories": 6,
         "predicted_min_shard_time": 75.0,
         "predicted_min_shard_index": 0,
-        "predicted_max_shard_time": 217.0,
+        "predicted_max_shard_time": 180,
         "predicted_max_shard_index": 1,
         "shard #0": 75.0,
-        "shard #1": 217.0,
-        "shard #2": 185.0
+        "shard #1": 180,
+        "shard #2": 162.0
     }
 }
diff --git a/tools/perf/core/shard_maps/win-10_laptop_low_end-perf_map.json b/tools/perf/core/shard_maps/win-10_laptop_low_end-perf_map.json
index 5ee939c..c7d2f8b 100644
--- a/tools/perf/core/shard_maps/win-10_laptop_low_end-perf_map.json
+++ b/tools/perf/core/shard_maps/win-10_laptop_low_end-perf_map.json
@@ -23,13 +23,9 @@
                 "display_name": "jetstream2.crossbench",
                 "arguments": []
             },
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
@@ -55,13 +51,9 @@
                 "display_name": "jetstream2.crossbench",
                 "arguments": []
             },
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
@@ -87,13 +79,9 @@
                 "display_name": "jetstream2.crossbench",
                 "arguments": []
             },
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
@@ -101,7 +89,7 @@
         "benchmarks": {
             "blink_perf.accessibility": {
                 "begin": 8,
-                "end": 21,
+                "end": 22,
                 "abridged": false
             },
             "jetstream2": {
@@ -119,24 +107,20 @@
                 "display_name": "jetstream2.crossbench",
                 "arguments": []
             },
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
     "4": {
         "benchmarks": {
             "blink_perf.accessibility": {
-                "begin": 21,
+                "begin": 22,
                 "abridged": false
             },
             "blink_perf.bindings": {
-                "end": 45,
+                "end": 50,
                 "abridged": false
             },
             "jetstream2": {
@@ -154,25 +138,25 @@
                 "display_name": "jetstream2.crossbench",
                 "arguments": []
             },
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
     "5": {
         "benchmarks": {
             "blink_perf.bindings": {
-                "begin": 45,
+                "begin": 50,
                 "abridged": false
             },
             "blink_perf.css": {
                 "abridged": false
             },
+            "blink_perf.dom": {
+                "end": 2,
+                "abridged": false
+            },
             "speedometer2": {
                 "abridged": false
             },
@@ -181,20 +165,17 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
     "6": {
         "benchmarks": {
             "blink_perf.dom": {
-                "end": 11,
+                "begin": 2,
+                "end": 12,
                 "abridged": false
             },
             "speedometer2": {
@@ -205,20 +186,16 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
     "7": {
         "benchmarks": {
             "blink_perf.dom": {
-                "begin": 11,
+                "begin": 12,
                 "abridged": false
             },
             "blink_perf.events": {
@@ -228,7 +205,7 @@
                 "abridged": false
             },
             "blink_perf.layout": {
-                "end": 42,
+                "end": 50,
                 "abridged": false
             },
             "speedometer2": {
@@ -239,25 +216,25 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
     "8": {
         "benchmarks": {
             "blink_perf.layout": {
-                "begin": 42,
+                "begin": 50,
                 "abridged": false
             },
             "blink_perf.owp_storage": {
                 "abridged": false
             },
+            "blink_perf.parser": {
+                "end": 7,
+                "abridged": false
+            },
             "speedometer2": {
                 "abridged": false
             },
@@ -266,26 +243,23 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
     "9": {
         "benchmarks": {
             "blink_perf.parser": {
+                "begin": 7,
                 "abridged": false
             },
             "blink_perf.shadow_dom": {
                 "abridged": false
             },
             "blink_perf.webaudio": {
-                "end": 6,
+                "end": 7,
                 "abridged": false
             },
             "speedometer2": {
@@ -296,20 +270,16 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
     "10": {
         "benchmarks": {
             "blink_perf.webaudio": {
-                "begin": 6,
+                "begin": 7,
                 "abridged": false
             },
             "blink_perf.webcodecs": {
@@ -336,6 +306,9 @@
             "dummy_benchmark.stable_benchmark_1": {
                 "abridged": false
             },
+            "jetstream2": {
+                "abridged": false
+            },
             "speedometer2": {
                 "abridged": false
             },
@@ -344,108 +317,18 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
     "11": {
         "benchmarks": {
-            "jetstream2": {
-                "abridged": false
-            },
             "media.desktop": {
-                "end": 20,
-                "abridged": false
-            },
-            "speedometer2": {
-                "abridged": false
-            },
-            "speedometer3": {
-                "abridged": false
-            }
-        },
-        "crossbench": {
-            "speedometer_3.0": {
-                "display_name": "speedometer3.crossbench",
-                "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            }
-        }
-    },
-    "12": {
-        "benchmarks": {
-            "media.desktop": {
-                "begin": 20,
                 "abridged": false
             },
             "memory.desktop": {
-                "end": 6,
-                "abridged": false
-            },
-            "speedometer2": {
-                "abridged": false
-            },
-            "speedometer3": {
-                "abridged": false
-            }
-        },
-        "crossbench": {
-            "speedometer_3.0": {
-                "display_name": "speedometer3.crossbench",
-                "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            }
-        }
-    },
-    "13": {
-        "benchmarks": {
-            "memory.desktop": {
-                "begin": 6,
-                "abridged": false
-            },
-            "speedometer2": {
-                "abridged": false
-            },
-            "speedometer3": {
-                "abridged": false
-            }
-        },
-        "crossbench": {
-            "speedometer_3.0": {
-                "display_name": "speedometer3.crossbench",
-                "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
-            "motionmark_1.3": {
-                "display_name": "motionmark1.3.crossbench",
-                "arguments": []
-            }
-        }
-    },
-    "14": {
-        "benchmarks": {
-            "octane": {
-                "abridged": false
-            },
-            "power.desktop": {
-                "abridged": false
-            },
-            "rasterize_and_record_micro.top_25": {
                 "end": 1,
                 "abridged": false
             },
@@ -457,12 +340,84 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
+                "display_name": "speedometer3.crossbench",
+                "arguments": []
+            }
+        }
+    },
+    "12": {
+        "benchmarks": {
+            "memory.desktop": {
+                "begin": 1,
+                "end": 8,
+                "abridged": false
+            },
+            "speedometer2": {
+                "abridged": false
+            },
+            "speedometer3": {
+                "abridged": false
+            }
+        },
+        "crossbench": {
+            "speedometer_3": {
+                "display_name": "speedometer3.crossbench",
+                "arguments": []
+            }
+        }
+    },
+    "13": {
+        "benchmarks": {
+            "memory.desktop": {
+                "begin": 8,
+                "abridged": false
+            },
+            "octane": {
+                "abridged": false
+            },
+            "power.desktop": {
+                "end": 4,
+                "abridged": false
+            },
+            "speedometer2": {
+                "abridged": false
+            },
+            "speedometer3": {
+                "abridged": false
+            }
+        },
+        "crossbench": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
+            "motionmark_1.3": {
+                "display_name": "motionmark1.3.crossbench",
+                "arguments": []
+            }
+        }
+    },
+    "14": {
+        "benchmarks": {
+            "power.desktop": {
+                "begin": 4,
+                "abridged": false
+            },
+            "rasterize_and_record_micro.top_25": {
+                "end": 21,
+                "abridged": false
+            },
+            "speedometer2": {
+                "abridged": false
+            },
+            "speedometer3": {
+                "abridged": false
+            }
+        },
+        "crossbench": {
+            "speedometer_3": {
+                "display_name": "speedometer3.crossbench",
                 "arguments": []
             }
         }
@@ -470,11 +425,11 @@
     "15": {
         "benchmarks": {
             "rasterize_and_record_micro.top_25": {
-                "begin": 1,
+                "begin": 21,
                 "abridged": false
             },
             "rendering.desktop": {
-                "end": 14,
+                "end": 22,
                 "abridged": false
             },
             "speedometer2": {
@@ -485,21 +440,17 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
     "16": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 14,
-                "end": 38,
+                "begin": 22,
+                "end": 49,
                 "abridged": false
             },
             "speedometer2": {
@@ -510,21 +461,17 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
     "17": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 38,
-                "end": 64,
+                "begin": 49,
+                "end": 76,
                 "abridged": false
             },
             "speedometer2": {
@@ -535,21 +482,17 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
     "18": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 64,
-                "end": 90,
+                "begin": 76,
+                "end": 103,
                 "abridged": false
             },
             "speedometer2": {
@@ -560,21 +503,17 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
     "19": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 90,
-                "end": 116,
+                "begin": 103,
+                "end": 130,
                 "abridged": false
             },
             "speedometer2": {
@@ -585,21 +524,17 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
     "20": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 116,
-                "end": 151,
+                "begin": 130,
+                "end": 160,
                 "abridged": false
             }
         }
@@ -607,8 +542,8 @@
     "21": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 151,
-                "end": 178,
+                "begin": 160,
+                "end": 190,
                 "abridged": false
             }
         }
@@ -616,8 +551,8 @@
     "22": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 178,
-                "end": 212,
+                "begin": 190,
+                "end": 222,
                 "abridged": false
             }
         }
@@ -625,8 +560,8 @@
     "23": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 212,
-                "end": 245,
+                "begin": 222,
+                "end": 252,
                 "abridged": false
             }
         }
@@ -634,8 +569,8 @@
     "24": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 245,
-                "end": 267,
+                "begin": 252,
+                "end": 274,
                 "abridged": false
             }
         }
@@ -643,8 +578,8 @@
     "25": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 267,
-                "end": 291,
+                "begin": 274,
+                "end": 299,
                 "abridged": false
             }
         }
@@ -652,8 +587,8 @@
     "26": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 291,
-                "end": 319,
+                "begin": 299,
+                "end": 328,
                 "abridged": false
             }
         }
@@ -661,16 +596,12 @@
     "27": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 319,
+                "begin": 328,
                 "abridged": false
             },
             "rendering.desktop.notracing": {
                 "abridged": false
-            }
-        }
-    },
-    "28": {
-        "benchmarks": {
+            },
             "speedometer": {
                 "abridged": false
             },
@@ -682,7 +613,11 @@
             },
             "speedometer2-future": {
                 "abridged": false
-            },
+            }
+        }
+    },
+    "28": {
+        "benchmarks": {
             "speedometer3": {
                 "abridged": false
             },
@@ -690,13 +625,13 @@
                 "abridged": false
             },
             "system_health.common_desktop": {
-                "end": 18,
+                "end": 24,
                 "abridged": false
             }
         },
         "crossbench": {
-            "speedometer_2.1": {
-                "display_name": "speedometer2.1.crossbench",
+            "speedometer_2": {
+                "display_name": "speedometer2.crossbench",
                 "arguments": []
             }
         }
@@ -704,8 +639,8 @@
     "29": {
         "benchmarks": {
             "system_health.common_desktop": {
-                "begin": 18,
-                "end": 53,
+                "begin": 24,
+                "end": 59,
                 "abridged": false
             }
         }
@@ -713,20 +648,20 @@
     "30": {
         "benchmarks": {
             "system_health.common_desktop": {
-                "begin": 53,
-                "end": 79,
+                "begin": 59,
+                "abridged": false
+            },
+            "system_health.memory_desktop": {
+                "end": 1,
                 "abridged": false
             }
         }
     },
     "31": {
         "benchmarks": {
-            "system_health.common_desktop": {
-                "begin": 79,
-                "abridged": false
-            },
             "system_health.memory_desktop": {
-                "end": 16,
+                "begin": 1,
+                "end": 18,
                 "abridged": false
             }
         }
@@ -734,8 +669,8 @@
     "32": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 16,
-                "end": 27,
+                "begin": 18,
+                "end": 30,
                 "abridged": false
             }
         }
@@ -743,8 +678,8 @@
     "33": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 27,
-                "end": 44,
+                "begin": 30,
+                "end": 48,
                 "abridged": false
             }
         }
@@ -752,8 +687,8 @@
     "34": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 44,
-                "end": 59,
+                "begin": 48,
+                "end": 62,
                 "abridged": false
             }
         }
@@ -761,8 +696,8 @@
     "35": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 59,
-                "end": 66,
+                "begin": 62,
+                "end": 67,
                 "abridged": false
             }
         }
@@ -770,8 +705,8 @@
     "36": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 66,
-                "end": 72,
+                "begin": 67,
+                "end": 75,
                 "abridged": false
             }
         }
@@ -779,11 +714,11 @@
     "37": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 72,
+                "begin": 75,
                 "abridged": false
             },
             "v8.browsing_desktop": {
-                "end": 7,
+                "end": 15,
                 "abridged": false
             }
         }
@@ -791,20 +726,20 @@
     "38": {
         "benchmarks": {
             "v8.browsing_desktop": {
-                "begin": 7,
-                "end": 28,
+                "begin": 15,
+                "abridged": false
+            },
+            "v8.browsing_desktop-future": {
+                "end": 6,
                 "abridged": false
             }
         }
     },
     "39": {
         "benchmarks": {
-            "v8.browsing_desktop": {
-                "begin": 28,
-                "abridged": false
-            },
             "v8.browsing_desktop-future": {
-                "end": 25,
+                "begin": 6,
+                "end": 27,
                 "abridged": false
             }
         }
@@ -812,11 +747,11 @@
     "40": {
         "benchmarks": {
             "v8.browsing_desktop-future": {
-                "begin": 25,
+                "begin": 27,
                 "abridged": false
             },
             "v8.runtime_stats.top_25": {
-                "end": 17,
+                "end": 20,
                 "abridged": false
             }
         }
@@ -824,8 +759,8 @@
     "41": {
         "benchmarks": {
             "v8.runtime_stats.top_25": {
-                "begin": 17,
-                "end": 46,
+                "begin": 20,
+                "end": 48,
                 "abridged": false
             }
         }
@@ -833,8 +768,8 @@
     "42": {
         "benchmarks": {
             "v8.runtime_stats.top_25": {
-                "begin": 46,
-                "end": 74,
+                "begin": 48,
+                "end": 75,
                 "abridged": false
             }
         }
@@ -842,7 +777,7 @@
     "43": {
         "benchmarks": {
             "v8.runtime_stats.top_25": {
-                "begin": 74,
+                "begin": 75,
                 "end": 92,
                 "abridged": false
             }
@@ -863,55 +798,55 @@
         }
     },
     "extra_infos": {
-        "num_stories": 1335,
-        "predicted_min_shard_time": 1086.0,
-        "predicted_min_shard_index": 35,
-        "predicted_max_shard_time": 1826.0,
+        "num_stories": 1315,
+        "predicted_min_shard_time": 1111.0,
+        "predicted_min_shard_index": 0,
+        "predicted_max_shard_time": 1766.0,
         "predicted_max_shard_index": 1,
-        "shard #0": 1171.0,
-        "shard #1": 1826.0,
-        "shard #2": 1622.0,
-        "shard #3": 1295.0,
-        "shard #4": 1304.0,
-        "shard #5": 1314.0,
-        "shard #6": 1318.0,
-        "shard #7": 1306.0,
-        "shard #8": 1325.0,
-        "shard #9": 1345.0,
-        "shard #10": 1273.0,
-        "shard #11": 1307.0,
-        "shard #12": 1374.0,
-        "shard #13": 1343.0,
-        "shard #14": 1303.0,
-        "shard #15": 1317.0,
-        "shard #16": 1299.0,
-        "shard #17": 1289.0,
-        "shard #18": 1295.0,
-        "shard #19": 1317.0,
-        "shard #20": 1295.0,
-        "shard #21": 1285.0,
-        "shard #22": 1292.0,
-        "shard #23": 1323.0,
-        "shard #24": 1273.0,
-        "shard #25": 1310.0,
-        "shard #26": 1284.0,
-        "shard #27": 1405.0,
-        "shard #28": 1317.0,
-        "shard #29": 1306.0,
-        "shard #30": 1264.0,
-        "shard #31": 1269.0,
-        "shard #32": 1302.0,
-        "shard #33": 1263.0,
+        "shard #0": 1111.0,
+        "shard #1": 1766.0,
+        "shard #2": 1562.0,
+        "shard #3": 1265.0,
+        "shard #4": 1281.0,
+        "shard #5": 1220.0,
+        "shard #6": 1282.0,
+        "shard #7": 1279.0,
+        "shard #8": 1277.0,
+        "shard #9": 1274.0,
+        "shard #10": 1356.0,
+        "shard #11": 1294.0,
+        "shard #12": 1358.0,
+        "shard #13": 1254.0,
+        "shard #14": 1278.0,
+        "shard #15": 1278.0,
+        "shard #16": 1298.0,
+        "shard #17": 1267.0,
+        "shard #18": 1259.0,
+        "shard #19": 1267.0,
+        "shard #20": 1264.0,
+        "shard #21": 1292.0,
+        "shard #22": 1263.0,
+        "shard #23": 1300.0,
+        "shard #24": 1272.0,
+        "shard #25": 1276.0,
+        "shard #26": 1282.0,
+        "shard #27": 1312.0,
+        "shard #28": 1319.0,
+        "shard #29": 1259.0,
+        "shard #30": 1209.0,
+        "shard #31": 1335.0,
+        "shard #32": 1293.0,
+        "shard #33": 1308.0,
         "shard #34": 1314.0,
-        "shard #35": 1086.0,
-        "shard #36": 1350.0,
-        "shard #37": 1311.0,
-        "shard #38": 1278.0,
-        "shard #39": 1350.0,
-        "shard #40": 1313.0,
-        "shard #41": 1302.0,
-        "shard #42": 1348.0,
-        "shard #43": 1322.0,
+        "shard #35": 1305.0,
+        "shard #36": 1233.0,
+        "shard #37": 1266.0,
+        "shard #38": 1264.0,
+        "shard #39": 1221.0,
+        "shard #40": 1258.0,
+        "shard #41": 1266.0,
+        "shard #42": 1297.0,
+        "shard #43": 1271.0,
         "shard #44": 1739.0
     }
 }
diff --git a/tools/perf/core/shard_maps/win-11-perf_map.json b/tools/perf/core/shard_maps/win-11-perf_map.json
index eb1fd71..1f2ce49 100644
--- a/tools/perf/core/shard_maps/win-11-perf_map.json
+++ b/tools/perf/core/shard_maps/win-11-perf_map.json
@@ -32,13 +32,9 @@
                 "display_name": "jetstream2.crossbench",
                 "arguments": []
             },
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
@@ -63,13 +59,9 @@
                 "display_name": "jetstream2.crossbench",
                 "arguments": []
             },
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
@@ -100,13 +92,9 @@
                 "display_name": "jetstream2.crossbench",
                 "arguments": []
             },
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
@@ -138,13 +126,9 @@
                 "display_name": "jetstream2.crossbench",
                 "arguments": []
             },
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
@@ -173,13 +157,9 @@
                 "display_name": "jetstream2.crossbench",
                 "arguments": []
             },
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
@@ -232,13 +212,9 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
@@ -265,13 +241,9 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
@@ -302,13 +274,9 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
@@ -329,13 +297,9 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
@@ -356,14 +320,10 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
             "motionmark_1.3": {
                 "display_name": "motionmark1.3.crossbench",
                 "arguments": []
@@ -403,16 +363,12 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
-            "speedometer_2.1": {
-                "display_name": "speedometer2.1.crossbench",
+            "speedometer_2": {
+                "display_name": "speedometer2.crossbench",
                 "arguments": []
             }
         }
@@ -432,13 +388,9 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
@@ -457,13 +409,9 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
@@ -482,13 +430,9 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
@@ -507,13 +451,9 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
@@ -532,13 +472,9 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
@@ -557,13 +493,9 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
@@ -593,13 +525,9 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
@@ -621,13 +549,9 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
@@ -659,41 +583,37 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
-            },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
             }
         }
     },
     "extra_infos": {
-        "num_stories": 781,
-        "predicted_min_shard_time": 899.0,
+        "num_stories": 761,
+        "predicted_min_shard_time": 839.0,
         "predicted_min_shard_index": 16,
-        "predicted_max_shard_time": 1127.0,
+        "predicted_max_shard_time": 1067.0,
         "predicted_max_shard_index": 14,
-        "shard #0": 1078.0,
-        "shard #1": 1027.0,
-        "shard #2": 1004.0,
-        "shard #3": 1023.0,
-        "shard #4": 1028.0,
-        "shard #5": 969.0,
-        "shard #6": 1029.0,
-        "shard #7": 1025.0,
-        "shard #8": 1102.0,
-        "shard #9": 1038.0,
-        "shard #10": 1021.0,
-        "shard #11": 1010.0,
-        "shard #12": 1037.0,
-        "shard #13": 1043.0,
-        "shard #14": 1127.0,
-        "shard #15": 950.0,
-        "shard #16": 899.0,
-        "shard #17": 1035.0,
-        "shard #18": 1038.0,
-        "shard #19": 1114.0
+        "shard #0": 1018.0,
+        "shard #1": 967.0,
+        "shard #2": 944.0,
+        "shard #3": 963.0,
+        "shard #4": 968.0,
+        "shard #5": 909.0,
+        "shard #6": 969.0,
+        "shard #7": 965.0,
+        "shard #8": 1042.0,
+        "shard #9": 978.0,
+        "shard #10": 961.0,
+        "shard #11": 950.0,
+        "shard #12": 977.0,
+        "shard #13": 983.0,
+        "shard #14": 1067.0,
+        "shard #15": 890.0,
+        "shard #16": 839.0,
+        "shard #17": 975.0,
+        "shard #18": 978.0,
+        "shard #19": 1054.0
     }
 }
diff --git a/tools/perf/core/shard_maps/win-11_laptop_low_end-perf_map.json b/tools/perf/core/shard_maps/win-11_laptop_low_end-perf_map.json
index 9e1fa59..97eaa77b 100644
--- a/tools/perf/core/shard_maps/win-11_laptop_low_end-perf_map.json
+++ b/tools/perf/core/shard_maps/win-11_laptop_low_end-perf_map.json
@@ -23,7 +23,7 @@
                 "abridged": false
             },
             "system_health.common_desktop": {
-                "end": 11,
+                "end": 14,
                 "abridged": false
             }
         },
@@ -36,15 +36,11 @@
                 "display_name": "motionmark1.3.crossbench",
                 "arguments": []
             },
-            "speedometer_2.1": {
-                "display_name": "speedometer2.1.crossbench",
+            "speedometer_2": {
+                "display_name": "speedometer2.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             }
@@ -53,7 +49,7 @@
     "1": {
         "benchmarks": {
             "system_health.common_desktop": {
-                "begin": 11,
+                "begin": 14,
                 "abridged": false
             },
             "v8.browsing_desktop": {
@@ -62,12 +58,12 @@
         }
     },
     "extra_infos": {
-        "num_stories": 178,
-        "predicted_min_shard_time": 2787.0,
+        "num_stories": 177,
+        "predicted_min_shard_time": 2775.0,
         "predicted_min_shard_index": 1,
-        "predicted_max_shard_time": 2827.0,
+        "predicted_max_shard_time": 2779.0,
         "predicted_max_shard_index": 0,
-        "shard #0": 2827.0,
-        "shard #1": 2787.0
+        "shard #0": 2779.0,
+        "shard #1": 2775.0
     }
 }
diff --git a/tools/perf/core/shard_maps/win-arm64-snapdragon-elite-perf_map.json b/tools/perf/core/shard_maps/win-arm64-snapdragon-elite-perf_map.json
index 41c3e42b..01f8b4d 100644
--- a/tools/perf/core/shard_maps/win-arm64-snapdragon-elite-perf_map.json
+++ b/tools/perf/core/shard_maps/win-arm64-snapdragon-elite-perf_map.json
@@ -59,26 +59,22 @@
                 "display_name": "motionmark1.3.crossbench",
                 "arguments": []
             },
-            "speedometer_2.1": {
-                "display_name": "speedometer2.1.crossbench",
+            "speedometer_2": {
+                "display_name": "speedometer2.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             }
         }
     },
     "extra_infos": {
-        "num_stories": 181,
-        "predicted_min_shard_time": 2782.0,
+        "num_stories": 180,
+        "predicted_min_shard_time": 2722.0,
         "predicted_min_shard_index": 0,
-        "predicted_max_shard_time": 2782.0,
+        "predicted_max_shard_time": 2722.0,
         "predicted_max_shard_index": 0,
-        "shard #0": 2782.0
+        "shard #0": 2722.0
     }
 }
diff --git a/tools/perf/core/shard_maps/win-arm64-snapdragon-plus-perf_map.json b/tools/perf/core/shard_maps/win-arm64-snapdragon-plus-perf_map.json
index 41c3e42b..01f8b4d 100644
--- a/tools/perf/core/shard_maps/win-arm64-snapdragon-plus-perf_map.json
+++ b/tools/perf/core/shard_maps/win-arm64-snapdragon-plus-perf_map.json
@@ -59,26 +59,22 @@
                 "display_name": "motionmark1.3.crossbench",
                 "arguments": []
             },
-            "speedometer_2.1": {
-                "display_name": "speedometer2.1.crossbench",
+            "speedometer_2": {
+                "display_name": "speedometer2.crossbench",
                 "arguments": []
             },
-            "speedometer_3.1": {
-                "display_name": "speedometer3.1.crossbench",
-                "arguments": []
-            },
-            "speedometer_3.0": {
+            "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": []
             }
         }
     },
     "extra_infos": {
-        "num_stories": 181,
-        "predicted_min_shard_time": 2782.0,
+        "num_stories": 180,
+        "predicted_min_shard_time": 2722.0,
         "predicted_min_shard_index": 0,
-        "predicted_max_shard_time": 2782.0,
+        "predicted_max_shard_time": 2722.0,
         "predicted_max_shard_index": 0,
-        "shard #0": 2782.0
+        "shard #0": 2722.0
     }
 }
diff --git a/tools/perf/process_perf_results.pydeps b/tools/perf/process_perf_results.pydeps
index f38f8f5..21ce947 100644
--- a/tools/perf/process_perf_results.pydeps
+++ b/tools/perf/process_perf_results.pydeps
@@ -112,6 +112,7 @@
 ../../third_party/catapult/devil/devil/utils/__init__.py
 ../../third_party/catapult/devil/devil/utils/cmd_helper.py
 ../../third_party/catapult/devil/devil/utils/geometry.py
+../../third_party/catapult/devil/devil/utils/host_utils.py
 ../../third_party/catapult/devil/devil/utils/lazy/__init__.py
 ../../third_party/catapult/devil/devil/utils/lazy/weak_constant.py
 ../../third_party/catapult/devil/devil/utils/logging_common.py
diff --git a/tools/rust/update_rust.py b/tools/rust/update_rust.py
index 780c2ac..805a9c6e 100755
--- a/tools/rust/update_rust.py
+++ b/tools/rust/update_rust.py
@@ -32,7 +32,7 @@
 # These fields are written by //tools/clang/scripts/upload_revision.py, and
 # should not be changed manually.
 # They are also read by build/config/compiler/BUILD.gn.
-RUST_REVISION = '3350c1eb3fd8fe1bee1ed4c76944d707bd256876'
+RUST_REVISION = '4a0969e06dbeaaa43914d2d00b2e843d49aa3886'
 RUST_SUB_REVISION = 1
 
 # The revision of Crubit to use from https://github.com/google/crubit
diff --git a/tools/update_pgo_profiles.py b/tools/update_pgo_profiles.py
index 4e331b4..d7e68d0a 100755
--- a/tools/update_pgo_profiles.py
+++ b/tools/update_pgo_profiles.py
@@ -76,7 +76,7 @@
   Raises:
     RuntimeError: If failed to download profiles from gcs.
   """
-  profile_name = _read_profile_name(args.target)
+  profile_name = args.override_filename or _read_profile_name(args.target)
   profile_path = os.path.join(_PGO_PROFILE_DIR, profile_name)
   if os.path.isfile(profile_path):
     os.utime(profile_path, None)
@@ -101,7 +101,8 @@
   Raises:
     RuntimeError: If the current profile is missing.
   """
-  profile_path = os.path.join(_PGO_PROFILE_DIR, _read_profile_name(args.target))
+  profile_name = args.override_filename or _read_profile_name(args.target)
+  profile_path = os.path.join(_PGO_PROFILE_DIR, profile_name)
   if not os.path.isfile(profile_path):
     raise RuntimeError(
         'requested profile "%s" doesn\'t exist, please make sure '
@@ -144,6 +145,8 @@
           'android-desktop-x64',
       ],
       help='Identifier of a specific target platform + architecture.')
+  parser.add_argument('--override-filename',
+                      help='The filename to prefer instead of the sha1 file.')
   subparsers = parser.add_subparsers()
 
   parser_update = subparsers.add_parser('update')
diff --git a/ui/accelerated_widget_mac/accelerated_widget_mac_export.h b/ui/accelerated_widget_mac/accelerated_widget_mac_export.h
index 181e822..1899179 100644
--- a/ui/accelerated_widget_mac/accelerated_widget_mac_export.h
+++ b/ui/accelerated_widget_mac/accelerated_widget_mac_export.h
@@ -11,11 +11,7 @@
 
 #if defined(COMPONENT_BUILD)
 
-#if defined(ACCELERATED_WIDGET_MAC_IMPLEMENTATION)
 #define ACCELERATED_WIDGET_MAC_EXPORT __attribute__((visibility("default")))
-#else
-#define ACCELERATED_WIDGET_MAC_EXPORT
-#endif
 
 #else  // defined(COMPONENT_BUILD)
 #define ACCELERATED_WIDGET_MAC_EXPORT
diff --git a/ui/accessibility/ax_base_export.h b/ui/accessibility/ax_base_export.h
index b25d5d4..2a44095 100644
--- a/ui/accessibility/ax_base_export.h
+++ b/ui/accessibility/ax_base_export.h
@@ -13,8 +13,7 @@
 #define AX_BASE_EXPORT __declspec(dllexport)
 #elif defined(COMPONENT_BUILD) && defined(WIN32)
 #define AX_BASE_EXPORT __declspec(dllimport)
-#elif defined(COMPONENT_BUILD) && !defined(WIN32) && \
-    defined(AX_BASE_IMPLEMENTATION)
+#elif defined(COMPONENT_BUILD)
 #define AX_BASE_EXPORT __attribute__((visibility("default")))
 #else
 #define AX_BASE_EXPORT
diff --git a/ui/accessibility/ax_export.h b/ui/accessibility/ax_export.h
index ad444a4..bf998e8 100644
--- a/ui/accessibility/ax_export.h
+++ b/ui/accessibility/ax_export.h
@@ -12,7 +12,7 @@
 #define AX_EXPORT __declspec(dllexport)
 #elif defined(COMPONENT_BUILD) && defined(WIN32)
 #define AX_EXPORT __declspec(dllimport)
-#elif defined(COMPONENT_BUILD) && !defined(WIN32) && defined(AX_IMPLEMENTATION)
+#elif defined(COMPONENT_BUILD)
 #define AX_EXPORT __attribute__((visibility("default")))
 #else
 #define AX_EXPORT
diff --git a/ui/accessibility/platform/ax_platform_node_cocoa.mm b/ui/accessibility/platform/ax_platform_node_cocoa.mm
index dd76d93b..ec9d749 100644
--- a/ui/accessibility/platform/ax_platform_node_cocoa.mm
+++ b/ui/accessibility/platform/ax_platform_node_cocoa.mm
@@ -2433,8 +2433,7 @@
 
 // LINT.IfChange
 - (NSString*)AXSelectedText {
-  NSRange selectedTextRange;
-  [[self AXSelectedTextRange] getValue:&selectedTextRange];
+  NSRange selectedTextRange = [[self AXSelectedTextRange] rangeValue];
   return [[self getAXValueAsString] substringWithRange:selectedTextRange];
 }
 // LINT.ThenChange(accessibilitySelectedText)
diff --git a/ui/android/BUILD.gn b/ui/android/BUILD.gn
index fb151fe..135551ed 100644
--- a/ui/android/BUILD.gn
+++ b/ui/android/BUILD.gn
@@ -486,6 +486,7 @@
     "java/src/org/chromium/ui/text/SpanApplier.java",
     "java/src/org/chromium/ui/util/AccessibilityUtil.java",
     "java/src/org/chromium/ui/util/AttrUtils.java",
+    "java/src/org/chromium/ui/util/ClickWithMetaStateCallback.java",
     "java/src/org/chromium/ui/util/ColorBlendAnimationFactory.java",
     "java/src/org/chromium/ui/util/ColorUtils.java",
     "java/src/org/chromium/ui/util/KeyboardNavigationListener.java",
diff --git a/ui/android/java/src/org/chromium/ui/base/DeviceFormFactor.java b/ui/android/java/src/org/chromium/ui/base/DeviceFormFactor.java
index be25bf5f..9df226d4 100644
--- a/ui/android/java/src/org/chromium/ui/base/DeviceFormFactor.java
+++ b/ui/android/java/src/org/chromium/ui/base/DeviceFormFactor.java
@@ -11,10 +11,10 @@
 
 import org.jni_zero.CalledByNative;
 
-import org.chromium.build.BuildConfig;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.ResettersForTesting;
 import org.chromium.base.ThreadUtils;
+import org.chromium.build.BuildConfig;
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
 import org.chromium.ui.R;
@@ -27,25 +27,39 @@
     /**
      * Desktop form factor.
      *
-     * <p>Identified by <code>isDesktop() == true</code>.
+     * <p>Based on gn build argument, as identified by <code>isDesktop() == true</code>.
      */
     public static final String DESKTOP = "Desktop";
 
     /**
      * Phone form factor.
      *
-     * <p>Identified by <code>isNonMultiDisplayContextOnTablet() == false</code>.
+     * <p>Based on screen size of the device, as identified by <code>
+     * isNonMultiDisplayContextOnTablet() == false</code>.
      */
     public static final String PHONE = "Phone";
 
     /**
-     * Tablet form factor, including {@code #LARGETABLET} below.
+     * Tablet or desktop form factor, including {@code #LARGETABLET} below.
      *
-     * <p>Identified by <code>isNonMultiDisplayContextOnTablet() == true</code>.
+     * <p>Based on screen size of the device, as identified by <code>
+     * isNonMultiDisplayContextOnTablet() == true</code>.
+     *
+     * <p>TODO(crbug.com/415126396): Change to mean <code>
+     * isNonMultiDisplayContextOnTablet() == true &&
+     * isDesktop() == false</code>.
      */
     public static final String TABLET = "Tablet";
 
     /**
+     * Tablet or desktop form factor, including {@code #LARGETABLET} below.
+     *
+     * <p>Based on screen size of the device, as identified by <code>
+     * isNonMultiDisplayContextOnTablet() == true</code>.
+     */
+    public static final String TABLET_OR_DESKTOP = "TabletOrDesktop";
+
+    /**
      * Minimum screen size in dp to be considered a tablet. Matches the value used by res/
      * directories. E.g.: res/values-sw600dp/values.xml
      */
diff --git a/ui/android/java/src/org/chromium/ui/dragdrop/DragDropMetricUtils.java b/ui/android/java/src/org/chromium/ui/dragdrop/DragDropMetricUtils.java
index 5a1610a..30bb1a74 100644
--- a/ui/android/java/src/org/chromium/ui/dragdrop/DragDropMetricUtils.java
+++ b/ui/android/java/src/org/chromium/ui/dragdrop/DragDropMetricUtils.java
@@ -21,9 +21,9 @@
     private DragDropMetricUtils() {}
 
     /**
-     * Enum used by Android.DragDrop.Tab.Type, which records the drag source and drop target when a
-     * tab is dropped successfully. These values are persisted to logs. Entries should not be
-     * renumbered and numeric values should never be reused.
+     * Enum used by Android.DragDrop.Tab.Type and Android.DragDrop.TabGroup.Type, which records the
+     * drag source and drop target when a tab is dropped successfully. These values are persisted to
+     * logs. Entries should not be renumbered and numeric values should never be reused.
      */
     @IntDef({
         DragDropType.TAB_STRIP_TO_TAB_STRIP,
@@ -51,6 +51,7 @@
         UrlIntentSource.UNKNOWN,
         UrlIntentSource.LINK,
         UrlIntentSource.TAB_IN_STRIP,
+        UrlIntentSource.TAB_GROUP_IN_STRIP,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface UrlIntentSource {
@@ -62,97 +63,122 @@
     }
 
     /**
-     * Enum used by Android.DragDrop.Tab.FromStrip.Result.*, which records the tab drag and drop
+     * Enum used by Android.DragDrop.Tab*.FromStrip.Result.* which records the drag and drop
      * results, including successful drops and failed drops with varies reasons. These values are
      * persisted to logs. Entries should not be renumbered and numeric values should never be
      * reused.
      */
     @IntDef({
-        DragDropTabResult.SUCCESS,
-        DragDropTabResult.IGNORED_TOOLBAR,
-        DragDropTabResult.IGNORED_DIFF_MODEL_NOT_SUPPORTED,
-        DragDropTabResult.IGNORED_TAB_SWITCHER,
-        DragDropTabResult.IGNORED_SAME_INSTANCE,
-        DragDropTabResult.ERROR_TAB_NOT_FOUND,
-        DragDropTabResult.IGNORED_MAX_INSTANCES,
+        DragDropResult.SUCCESS,
+        DragDropResult.IGNORED_TOOLBAR,
+        DragDropResult.IGNORED_DIFF_MODEL_NOT_SUPPORTED,
+        DragDropResult.IGNORED_TAB_SWITCHER,
+        DragDropResult.IGNORED_SAME_INSTANCE,
+        DragDropResult.ERROR_CONTENT_NOT_FOUND,
+        DragDropResult.IGNORED_MAX_INSTANCES,
+        DragDropResult.IGNORED_MHTML_TAB,
     })
     @Retention(RetentionPolicy.SOURCE)
-    public @interface DragDropTabResult {
+    public @interface DragDropResult {
         int SUCCESS = 0;
         int IGNORED_TOOLBAR = 1;
         int IGNORED_DIFF_MODEL_NOT_SUPPORTED = 2;
         int IGNORED_TAB_SWITCHER = 3;
         int IGNORED_SAME_INSTANCE = 4;
-        int ERROR_TAB_NOT_FOUND = 5;
+        int ERROR_CONTENT_NOT_FOUND = 5;
         int IGNORED_MAX_INSTANCES = 6;
-        int NUM_ENTRIES = 7;
+        int IGNORED_MHTML_TAB = 7;
+        int NUM_ENTRIES = 8;
     }
 
     /**
-     * Record enumerated histogram Android.DragDrop.Tab.Type for dragged tab data.
+     * Record enumerated histogram Android.DragDrop.Tab.Type and Android.DragDrop.TabGroup.Type for
+     * dragged tab or tab group data.
      *
      * @param dragDropType An enum indicating the drag source and drop target.
      * @param isInDesktopWindow Whether the app is running in a desktop window.
+     * @param isTabGroup True if the dragged item is a tab group; otherwise, it is assumed to be a
+     *     single tab.
      */
-    public static void recordTabDragDropType(
-            @DragDropType int dragDropType, boolean isInDesktopWindow) {
+    public static void recordDragDropType(
+            @DragDropType int dragDropType, boolean isInDesktopWindow, boolean isTabGroup) {
+        String histogram =
+                String.format("Android.DragDrop.%s.Type", isTabGroup ? "TabGroup" : "Tab");
         RecordHistogram.recordEnumeratedHistogram(
-                HISTOGRAM_DRAG_DROP_TAB_TYPE, dragDropType, DragDropType.NUM_ENTRIES);
+                histogram, dragDropType, DragDropType.NUM_ENTRIES);
         if (isInDesktopWindow) {
             RecordHistogram.recordEnumeratedHistogram(
-                    "Android.DragDrop.Tab.Type.DesktopWindow",
-                    dragDropType,
-                    DragDropType.NUM_ENTRIES);
+                    histogram + ".DesktopWindow", dragDropType, DragDropType.NUM_ENTRIES);
         }
     }
 
     /**
-     * Record enumerated histograms Android.DragDrop.Tab.FromStrip.Result.*.
+     * Record enumerated histograms Android.DragDrop.Tab.FromStrip.Result and
+     * Android.DragDrop.TabGroup.FromStrip.Result.
      *
-     * @param result An enum indicating the tab drag and drop results, including successful drops
-     *     and failed drops with varies reasons.
+     * @param result An enum indicating the tab or tab group drag and drop results, including
+     *     successful drops and failed drops with varies reasons.
      * @param isInDesktopWindow Whether the app is running in a desktop window.
+     * @param isTabGroup True if the dragged item is a tab group; otherwise, it is assumed to be a
+     *     single tab.
      */
-    public static void recordTabDragDropResult(
-            @DragDropTabResult int result, boolean isInDesktopWindow) {
-        RecordHistogram.recordEnumeratedHistogram(
-                "Android.DragDrop.Tab.FromStrip.Result", result, DragDropTabResult.NUM_ENTRIES);
+    public static void recordDragDropResult(
+            @DragDropResult int result, boolean isInDesktopWindow, boolean isTabGroup) {
+        String histogram =
+                String.format(
+                        "Android.DragDrop.%s.FromStrip.Result", isTabGroup ? "TabGroup" : "Tab");
+        RecordHistogram.recordEnumeratedHistogram(histogram, result, DragDropResult.NUM_ENTRIES);
         if (isInDesktopWindow) {
             RecordHistogram.recordEnumeratedHistogram(
-                    "Android.DragDrop.Tab.FromStrip.Result.DesktopWindow",
-                    result,
-                    DragDropTabResult.NUM_ENTRIES);
+                    histogram + ".DesktopWindow", result, DragDropResult.NUM_ENTRIES);
         }
     }
 
     /**
-     * Record boolean histogram Android.DragDrop.Tab.ReorderStripWithDragDrop.
+     * Record boolean histogram Android.DragDrop.Tab.ReorderStripWithDragDrop and
+     * Android.DragDrop.TabGroup.ReorderStripWithDragDrop.
      *
      * @param leavingStrip Whether the tab drag has left the source strip.
+     * @param isTabGroup True if the dragged item is a tab group; otherwise, it is assumed to be a
+     *     single tab.
      */
-    public static void recordTabReorderStripWithDragDrop(boolean leavingStrip) {
-        RecordHistogram.recordBooleanHistogram(
-                "Android.DragDrop.Tab.ReorderStripWithDragDrop", leavingStrip);
+    public static void recordReorderStripWithDragDrop(boolean leavingStrip, boolean isTabGroup) {
+        String histogram =
+                String.format(
+                        "Android.DragDrop.%s.ReorderStripWithDragDrop",
+                        isTabGroup ? "TabGroup" : "Tab");
+        RecordHistogram.recordBooleanHistogram(histogram, leavingStrip);
     }
 
     /**
-     * Record times histogram Android.DragDrop.Tab.Duration.WithinDestStrip.
+     * Record times histogram Android.DragDrop.Tab.Duration.WithinDestStrip and
+     * Android.DragDrop.TabGroup.Duration.WithinDestStrip.
      *
      * @param duration scrolling on a destination strip.
+     * @param isTabGroup True if the dragged item is a tab group; otherwise, it is assumed to be a
+     *     single tab.
      */
-    public static void recordTabDurationWithinDestStrip(long duration) {
-        RecordHistogram.deprecatedRecordMediumTimesHistogram(
-                "Android.DragDrop.Tab.Duration.WithinDestStrip", duration);
+    public static void recordDurationWithinDestStrip(long duration, boolean isTabGroup) {
+        String histogram =
+                String.format(
+                        "Android.DragDrop.%s.Duration.WithinDestStrip",
+                        isTabGroup ? "TabGroup" : "Tab");
+        RecordHistogram.recordMediumTimesHistogram(histogram, duration);
     }
 
     /**
-     * Record boolean histogram Android.DragDrop.Tab.SourceWindowClosed.
+     * Record boolean histogram Android.DragDrop.Tab.SourceWindowClosed and
+     * Android.DragDrop.TabGroup.SourceWindowClosed.
      *
      * @param didCloseWindow Whether a successful tab drag/drop resulted in closing the source
      *     Chrome window.
+     * @param isTabGroup True if the dragged item is a tab group; otherwise, it is assumed to be a
+     *     single tab.
      */
-    public static void recordTabDragDropClosedWindow(boolean didCloseWindow) {
-        RecordHistogram.recordBooleanHistogram(
-                "Android.DragDrop.Tab.SourceWindowClosed", didCloseWindow);
+    public static void recordDragDropClosedWindow(boolean didCloseWindow, boolean isTabGroup) {
+        String histogram =
+                String.format(
+                        "Android.DragDrop.%s.SourceWindowClosed", isTabGroup ? "TabGroup" : "Tab");
+        RecordHistogram.recordBooleanHistogram(histogram, didCloseWindow);
     }
 }
diff --git a/ui/android/java/src/org/chromium/ui/util/ClickWithMetaStateCallback.java b/ui/android/java/src/org/chromium/ui/util/ClickWithMetaStateCallback.java
new file mode 100644
index 0000000..3004cbb
--- /dev/null
+++ b/ui/android/java/src/org/chromium/ui/util/ClickWithMetaStateCallback.java
@@ -0,0 +1,15 @@
+// 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.ui.util;
+
+import org.chromium.build.annotations.NullMarked;
+
+/** A callback used to pass meta state information from the time of click. */
+@NullMarked
+@FunctionalInterface
+public interface ClickWithMetaStateCallback {
+    /** Called when a click occurs. {@param metaState} is the meta key state at the time. */
+    void onClickWithMeta(int metaState);
+}
diff --git a/ui/android/java/src/org/chromium/ui/widget/ChromeImageButton.java b/ui/android/java/src/org/chromium/ui/widget/ChromeImageButton.java
index ddcd54f..a206d74 100644
--- a/ui/android/java/src/org/chromium/ui/widget/ChromeImageButton.java
+++ b/ui/android/java/src/org/chromium/ui/widget/ChromeImageButton.java
@@ -13,9 +13,9 @@
 
 import androidx.appcompat.widget.AppCompatImageButton;
 
-import org.chromium.base.Callback;
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
+import org.chromium.ui.util.ClickWithMetaStateCallback;
 
 // TODO(crbug.com/40883889): See if we still need this class.
 /**
@@ -86,7 +86,8 @@
      *
      * @param callback the callback to notify.
      */
-    public void setClickCallback(@Nullable Callback<Integer> callback) {
-        setOnClickListener(callback != null ? (v) -> callback.onResult(mLastEventMetaState) : null);
+    public void setClickCallback(@Nullable ClickWithMetaStateCallback callback) {
+        setOnClickListener(
+                callback != null ? (v) -> callback.onClickWithMeta(mLastEventMetaState) : null);
     }
 }
diff --git a/ui/android/javatests/src/org/chromium/ui/test/util/UiDisableIfSkipCheck.java b/ui/android/javatests/src/org/chromium/ui/test/util/UiDisableIfSkipCheck.java
index 677559d..35423a6 100644
--- a/ui/android/javatests/src/org/chromium/ui/test/util/UiDisableIfSkipCheck.java
+++ b/ui/android/javatests/src/org/chromium/ui/test/util/UiDisableIfSkipCheck.java
@@ -32,16 +32,18 @@
         }
         return ThreadUtils.runOnUiThreadBlocking(
                 () -> {
-                    boolean isDesktop = DeviceFormFactor.isDesktop();
+                    boolean isDesktopBuild = DeviceFormFactor.isDesktop();
                     boolean isTablet =
                             DeviceFormFactor.isNonMultiDisplayContextOnTablet(mTargetContext);
                     switch (type) {
-                        case DeviceFormFactor.DESKTOP:
-                            return DeviceFormFactor.isDesktop();
                         case DeviceFormFactor.PHONE:
-                            return !DeviceFormFactor.isDesktop() && !isTablet;
+                            return !isDesktopBuild && !isTablet;
                         case DeviceFormFactor.TABLET:
-                            return !DeviceFormFactor.isDesktop() && isTablet;
+                            return !isDesktopBuild && isTablet;
+                        case DeviceFormFactor.DESKTOP:
+                            return isDesktopBuild;
+                        case DeviceFormFactor.TABLET_OR_DESKTOP:
+                            return isTablet;
                         default:
                             return false;
                     }
diff --git a/ui/android/javatests/src/org/chromium/ui/test/util/UiRestriction.java b/ui/android/javatests/src/org/chromium/ui/test/util/UiRestriction.java
index 6dd3f59..31d45e21 100644
--- a/ui/android/javatests/src/org/chromium/ui/test/util/UiRestriction.java
+++ b/ui/android/javatests/src/org/chromium/ui/test/util/UiRestriction.java
@@ -44,7 +44,10 @@
         check.addHandler(DeviceFormFactor.DESKTOP, () -> !isDesktop());
         // isTablet() returns True if the display is large enough to be considered a tablet, so
         // it is always True on desktop devices as well.
+        // TODO(crbug.com/415126396): Change PHONE to "isDesktop() || isTablet()"
         check.addHandler(DeviceFormFactor.PHONE, () -> isTablet());
+        // TODO(crbug.com/415126396): Change TABLET to "isDesktop() || !isTablet()"
         check.addHandler(DeviceFormFactor.TABLET, () -> !isTablet());
+        check.addHandler(DeviceFormFactor.TABLET_OR_DESKTOP, () -> !isTablet());
     }
 }
diff --git a/ui/android/ui_android_export.h b/ui/android/ui_android_export.h
index 9f600e9..b5a3c5b 100644
--- a/ui/android/ui_android_export.h
+++ b/ui/android/ui_android_export.h
@@ -13,19 +13,11 @@
 #if defined(WIN32)
 #error Unsupported target architecture.
 #else  // !defined(WIN32)
-
-#if defined(UI_ANDROID_IMPLEMENTATION)
 #define UI_ANDROID_EXPORT __attribute__((visibility("default")))
-#else
-#define UI_ANDROID_EXPORT
-#endif
-
 #endif
 
 #else  // !defined(COMPONENT_BUILD)
-
 #define UI_ANDROID_EXPORT
-
 #endif
 
 #endif  // UI_ANDROID_UI_ANDROID_EXPORT_H_
diff --git a/ui/aura/aura_export.h b/ui/aura/aura_export.h
index be6a08a..90ebdf82 100644
--- a/ui/aura/aura_export.h
+++ b/ui/aura/aura_export.h
@@ -18,11 +18,7 @@
 #endif  // defined(AURA_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(AURA_IMPLEMENTATION)
 #define AURA_EXPORT __attribute__((visibility("default")))
-#else
-#define AURA_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/ui/aura_extra/aura_extra_export.h b/ui/aura_extra/aura_extra_export.h
index 5d95d4d..1773417 100644
--- a/ui/aura_extra/aura_extra_export.h
+++ b/ui/aura_extra/aura_extra_export.h
@@ -18,11 +18,7 @@
 #endif  // defined(AURA_EXTRA_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(AURA_EXTRA_IMPLEMENTATION)
 #define AURA_EXTRA_EXPORT __attribute__((visibility("default")))
-#else
-#define AURA_EXTRA_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/ui/base/ui_base_switches.cc b/ui/base/ui_base_switches.cc
index 8c45e76..2aeb8892 100644
--- a/ui/base/ui_base_switches.cc
+++ b/ui/base/ui_base_switches.cc
@@ -33,6 +33,10 @@
 #if BUILDFLAG(IS_LINUX)
 // Specify the toolkit used to construct the Linux GUI.
 const char kUiToolkitFlag[] = "ui-toolkit";
+// Specify the GTK version to be loaded.
+const char kGtkVersionFlag[] = "gtk-version";
+// Specify the QT version to be loaded.
+const char kQtVersionFlag[] = "qt-version";
 // Disables GTK IME integration.
 const char kDisableGtkIme[] = "disable-gtk-ime";
 #endif
diff --git a/ui/base/ui_base_switches.h b/ui/base/ui_base_switches.h
index 3f4773f..581965d 100644
--- a/ui/base/ui_base_switches.h
+++ b/ui/base/ui_base_switches.h
@@ -28,6 +28,8 @@
 
 #if BUILDFLAG(IS_LINUX)
 COMPONENT_EXPORT(UI_BASE) extern const char kUiToolkitFlag[];
+COMPONENT_EXPORT(UI_BASE) extern const char kGtkVersionFlag[];
+COMPONENT_EXPORT(UI_BASE) extern const char kQtVersionFlag[];
 COMPONENT_EXPORT(UI_BASE) extern const char kDisableGtkIme[];
 #endif
 
diff --git a/ui/chromeos/ui_chromeos_export.h b/ui/chromeos/ui_chromeos_export.h
index 429a9a88..4c140186 100644
--- a/ui/chromeos/ui_chromeos_export.h
+++ b/ui/chromeos/ui_chromeos_export.h
@@ -6,11 +6,7 @@
 #define UI_CHROMEOS_UI_CHROMEOS_EXPORT_H_
 
 #if defined(COMPONENT_BUILD)
-#if defined(UI_CHROMEOS_IMPLEMENTATION)
 #define UI_CHROMEOS_EXPORT __attribute__((visibility("default")))
-#else
-#define UI_CHROMEOS_EXPORT
-#endif
 
 #else  // defined(COMPONENT_BUILD)
 #define UI_CHROMEOS_EXPORT
diff --git a/ui/compositor/compositor_export.h b/ui/compositor/compositor_export.h
index 02e4784..183832754 100644
--- a/ui/compositor/compositor_export.h
+++ b/ui/compositor/compositor_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(COMPOSITOR_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(COMPOSITOR_IMPLEMENTATION)
 #define COMPOSITOR_EXPORT __attribute__((visibility("default")))
-#else
-#define COMPOSITOR_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/ui/display/BUILD.gn b/ui/display/BUILD.gn
index 491b8006..e598004 100644
--- a/ui/display/BUILD.gn
+++ b/ui/display/BUILD.gn
@@ -69,8 +69,6 @@
       "mac/display_link_mac.h",
       "mac/display_link_mac.mm",
       "mac/screen_mac.mm",
-      "mac/screen_mac_headless.h",
-      "mac/screen_mac_headless.mm",
     ]
   }
 
@@ -149,7 +147,6 @@
   }
 
   if (is_mac) {
-    deps += [ "//components/headless/screen_info" ]
     frameworks = [
       "AppKit.framework",
       "CoreGraphics.framework",
diff --git a/ui/display/display_export.h b/ui/display/display_export.h
index 443e9f8..c3087e4 100644
--- a/ui/display/display_export.h
+++ b/ui/display/display_export.h
@@ -20,11 +20,7 @@
 
 #else  // !defined(WIN32)
 
-#if defined(DISPLAY_IMPLEMENTATION)
 #define DISPLAY_EXPORT __attribute__((visibility("default")))
-#else
-#define DISPLAY_EXPORT
-#endif
 
 #endif
 
diff --git a/ui/display/mac/DEPS b/ui/display/mac/DEPS
deleted file mode 100644
index 4a3d7376..0000000
--- a/ui/display/mac/DEPS
+++ /dev/null
@@ -1,5 +0,0 @@
-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 033ebc00..874ac9d 100644
--- a/ui/display/mac/screen_mac.mm
+++ b/ui/display/mac/screen_mac.mm
@@ -21,8 +21,6 @@
 #include "base/apple/bridging.h"
 #include "base/apple/foundation_util.h"
 #include "base/apple/scoped_cftyperef.h"
-#include "base/check_deref.h"
-#include "base/command_line.h"
 #include "base/functional/bind.h"
 #include "base/i18n/rtl.h"
 #include "base/logging.h"
@@ -34,13 +32,11 @@
 #include "components/device_event_log/device_event_log.h"
 #include "ui/display/display.h"
 #include "ui/display/display_change_notifier.h"
-#include "ui/display/mac/screen_mac_headless.h"
 #include "ui/display/util/display_util.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/icc_profile.h"
 #include "ui/gfx/mac/coordinate_conversion.h"
 #include "ui/gfx/native_widget_types.h"
-#include "ui/gfx/switches.h"
 
 extern "C" {
 Boolean CGDisplayUsesForceToGray(void);
@@ -615,13 +611,6 @@
 }
 
 Screen* CreateNativeScreen() {
-  const base::CommandLine& command_line =
-      CHECK_DEREF(base::CommandLine::ForCurrentProcess());
-
-  if (command_line.HasSwitch(switches::kHeadless)) {
-    return new ScreenMacHeadless;
-  }
-
   return new ScreenMac;
 }
 
diff --git a/ui/display/mac/screen_mac_headless.h b/ui/display/mac/screen_mac_headless.h
deleted file mode 100644
index 708cd0b8..0000000
--- a/ui/display/mac/screen_mac_headless.h
+++ /dev/null
@@ -1,40 +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 UI_DISPLAY_MAC_SCREEN_MAC_HEADLESS_H_
-#define UI_DISPLAY_MAC_SCREEN_MAC_HEADLESS_H_
-
-#include "ui/display/display.h"
-#include "ui/display/screen_base.h"
-#include "ui/gfx/geometry/point.h"
-#include "ui/gfx/native_widget_types.h"
-
-namespace display {
-
-class ScreenMacHeadless : public ScreenBase {
- public:
-  ScreenMacHeadless();
-
-  ScreenMacHeadless(const ScreenMacHeadless&) = delete;
-  ScreenMacHeadless& operator=(const ScreenMacHeadless&) = delete;
-
-  ~ScreenMacHeadless() override;
-
-  // display::Screen overrides:
-  gfx::Point GetCursorScreenPoint() override;
-  bool IsWindowUnderCursor(gfx::NativeWindow window) override;
-  gfx::NativeWindow GetWindowAtScreenPoint(const gfx::Point& point) override;
-  gfx::NativeWindow GetLocalProcessWindowAtPoint(
-      const gfx::Point& point,
-      const std::set<gfx::NativeWindow>& ignore) override;
-  Display GetDisplayNearestWindow(gfx::NativeWindow window) const override;
-  bool IsHeadless() const override;
-
- private:
-  void CreateDisplayList();
-};
-
-}  // namespace display
-
-#endif  // UI_DISPLAY_MAC_SCREEN_MAC_HEADLESS_H_
diff --git a/ui/display/mac/screen_mac_headless.mm b/ui/display/mac/screen_mac_headless.mm
deleted file mode 100644
index 2ae6774..0000000
--- a/ui/display/mac/screen_mac_headless.mm
+++ /dev/null
@@ -1,125 +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 "ui/display/mac/screen_mac_headless.h"
-
-#import <AppKit/AppKit.h>
-
-#include <vector>
-
-#include "base/check_deref.h"
-#include "base/command_line.h"
-#include "base/containers/flat_set.h"
-#include "base/types/expected.h"
-#include "components/headless/screen_info/headless_screen_info.h"
-#include "ui/display/util/display_util.h"
-#include "ui/gfx/geometry/rect.h"
-#include "ui/gfx/mac/coordinate_conversion.h"
-#include "ui/gfx/switches.h"
-
-namespace display {
-
-namespace {
-
-// Headless display ids are synthesized sequential numbers.
-constexpr int64_t kHeadlessDisplayIdBase = 1;
-
-std::vector<headless::HeadlessScreenInfo> GetHeadlessScreenInfos() {
-  std::vector<headless::HeadlessScreenInfo> screen_infos;
-
-  const base::CommandLine& command_line =
-      CHECK_DEREF(base::CommandLine::ForCurrentProcess());
-
-  if (command_line.HasSwitch(switches::kScreenInfo)) {
-    const std::string switch_value =
-        command_line.GetSwitchValueASCII(switches::kScreenInfo);
-    base::expected<std::vector<headless::HeadlessScreenInfo>, std::string>
-        screen_infos_or_error =
-            headless::HeadlessScreenInfo::FromString(switch_value);
-    CHECK(screen_infos_or_error.has_value()) << screen_infos_or_error.error();
-    screen_infos = screen_infos_or_error.value();
-  } else {
-    screen_infos.push_back(headless::HeadlessScreenInfo());
-  }
-
-  return screen_infos;
-}
-
-}  // namespace
-
-ScreenMacHeadless::ScreenMacHeadless() {
-  CreateDisplayList();
-}
-
-ScreenMacHeadless::~ScreenMacHeadless() = default;
-
-void ScreenMacHeadless::CreateDisplayList() {
-  std::vector<headless::HeadlessScreenInfo> screen_infos =
-      GetHeadlessScreenInfos();
-  CHECK(!screen_infos.empty());
-
-  bool is_primary = true;
-  base::flat_set<int64_t> internal_display_ids;
-  for (const headless::HeadlessScreenInfo& it : screen_infos) {
-    static int64_t synthesized_display_id = kHeadlessDisplayIdBase;
-    Display display(synthesized_display_id++);
-    display.set_label(it.label);
-    display.set_color_depth(it.color_depth);
-    display.SetScaleAndBounds(it.device_pixel_ratio, it.bounds);
-
-    if (!it.work_area_insets.IsEmpty()) {
-      display.UpdateWorkAreaFromInsets(it.work_area_insets);
-    }
-
-    if (it.rotation) {
-      CHECK(Display::IsValidRotation(it.rotation));
-      display.SetRotationAsDegree(it.rotation);
-    }
-
-    if (it.is_internal) {
-      internal_display_ids.insert(display.id());
-    }
-
-    ProcessDisplayChanged(display, is_primary);
-    is_primary = false;
-  }
-
-  SetInternalDisplayIds(internal_display_ids);
-}
-
-gfx::Point ScreenMacHeadless::GetCursorScreenPoint() {
-  return gfx::Point();
-}
-
-bool ScreenMacHeadless::IsWindowUnderCursor(gfx::NativeWindow window) {
-  return GetWindowAtScreenPoint(GetCursorScreenPoint()) == window;
-}
-
-gfx::NativeWindow ScreenMacHeadless::GetWindowAtScreenPoint(
-    const gfx::Point& point) {
-  return gfx::NativeWindow();
-}
-
-gfx::NativeWindow ScreenMacHeadless::GetLocalProcessWindowAtPoint(
-    const gfx::Point& point,
-    const std::set<gfx::NativeWindow>& ignore) {
-  return gfx::NativeWindow();
-}
-
-Display ScreenMacHeadless::GetDisplayNearestWindow(
-    gfx::NativeWindow window) const {
-  if (window && GetNumDisplays() > 1) {
-    if (NSWindow* ns_window = window.GetNativeNSWindow()) {
-      const gfx::Rect bounds = gfx::ScreenRectFromNSRect([ns_window frame]);
-      return GetDisplayMatching(bounds);
-    }
-  }
-  return GetPrimaryDisplay();
-}
-
-bool ScreenMacHeadless::IsHeadless() const {
-  return true;
-}
-
-}  // namespace display
diff --git a/ui/display/manager/display_manager_export.h b/ui/display/manager/display_manager_export.h
index de4272b..09af89e 100644
--- a/ui/display/manager/display_manager_export.h
+++ b/ui/display/manager/display_manager_export.h
@@ -15,11 +15,7 @@
 #endif
 
 #else  // !defined(WIN32)
-#if defined(DISPLAY_MANAGER_IMPLEMENTATION)
 #define DISPLAY_MANAGER_EXPORT __attribute__((visibility("default")))
-#else
-#define DISPLAY_MANAGER_EXPORT
-#endif
 #endif
 
 #else  // !defined(COMPONENT_BUILD)
diff --git a/ui/display/types/display_types_export.h b/ui/display/types/display_types_export.h
index 3637ea4..9680a24 100644
--- a/ui/display/types/display_types_export.h
+++ b/ui/display/types/display_types_export.h
@@ -20,11 +20,7 @@
 
 #else  // !defined(WIN32)
 
-#if defined(DISPLAY_TYPES_IMPLEMENTATION)
 #define DISPLAY_TYPES_EXPORT __attribute__((visibility("default")))
-#else
-#define DISPLAY_TYPES_EXPORT
-#endif
 
 #endif
 
diff --git a/ui/display/util/display_util_export.h b/ui/display/util/display_util_export.h
index 27248eb..51f58f4 100644
--- a/ui/display/util/display_util_export.h
+++ b/ui/display/util/display_util_export.h
@@ -20,11 +20,7 @@
 
 #else  // !defined(WIN32)
 
-#if defined(DISPLAY_UTIL_IMPLEMENTATION)
 #define DISPLAY_UTIL_EXPORT __attribute__((visibility("default")))
-#else
-#define DISPLAY_UTIL_EXPORT
-#endif
 
 #endif
 
diff --git a/ui/events/devices/events_devices_export.h b/ui/events/devices/events_devices_export.h
index 3f210d33..f4990ce 100644
--- a/ui/events/devices/events_devices_export.h
+++ b/ui/events/devices/events_devices_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(EVENTS_DEVICES_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(EVENTS_DEVICES_IMPLEMENTATION)
 #define EVENTS_DEVICES_EXPORT __attribute__((visibility("default")))
-#else
-#define EVENTS_DEVICES_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/ui/events/devices/x11/events_devices_x11_export.h b/ui/events/devices/x11/events_devices_x11_export.h
index 04087e0..0ccae8a 100644
--- a/ui/events/devices/x11/events_devices_x11_export.h
+++ b/ui/events/devices/x11/events_devices_x11_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(EVENTS_DEVICES_X11_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(EVENTS_DEVICES_X11_IMPLEMENTATION)
 #define EVENTS_DEVICES_X11_EXPORT __attribute__((visibility("default")))
-#else
-#define EVENTS_DEVICES_X11_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/ui/events/events_base_export.h b/ui/events/events_base_export.h
index 800a4e8d..830af7971 100644
--- a/ui/events/events_base_export.h
+++ b/ui/events/events_base_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(EVENTS_BASE_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(EVENTS_BASE_IMPLEMENTATION)
 #define EVENTS_BASE_EXPORT __attribute__((visibility("default")))
-#else
-#define EVENTS_BASE_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/ui/events/events_export.h b/ui/events/events_export.h
index a926b0f5..c791796 100644
--- a/ui/events/events_export.h
+++ b/ui/events/events_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(EVENTS_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(EVENTS_IMPLEMENTATION)
 #define EVENTS_EXPORT __attribute__((visibility("default")))
-#else
-#define EVENTS_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/ui/events/gesture_detection/gesture_detection_export.h b/ui/events/gesture_detection/gesture_detection_export.h
index 278bbc07..5e80720 100644
--- a/ui/events/gesture_detection/gesture_detection_export.h
+++ b/ui/events/gesture_detection/gesture_detection_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(GESTURES_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(GESTURE_DETECTION_IMPLEMENTATION)
 #define GESTURE_DETECTION_EXPORT __attribute__((visibility("default")))
-#else
-#define GESTURE_DETECTION_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/ui/events/keycodes/keycodes_x_export.h b/ui/events/keycodes/keycodes_x_export.h
index d0a21591..567015ed 100644
--- a/ui/events/keycodes/keycodes_x_export.h
+++ b/ui/events/keycodes/keycodes_x_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(KEYCODES_X_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(KEYCODES_X_IMPLEMENTATION)
 #define KEYCODES_X_EXPORT __attribute__((visibility("default")))
-#else
-#define KEYCODES_X_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/ui/events/x/events_x_export.h b/ui/events/x/events_x_export.h
index c0ccb06..fb900ad9 100644
--- a/ui/events/x/events_x_export.h
+++ b/ui/events/x/events_x_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(EVENTS_X_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(EVENTS_X_IMPLEMENTATION)
 #define EVENTS_X_EXPORT __attribute__((visibility("default")))
-#else
-#define EVENTS_X_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/ui/file_manager/file_manager_export.h b/ui/file_manager/file_manager_export.h
index 41d6708..f46a1b5 100644
--- a/ui/file_manager/file_manager_export.h
+++ b/ui/file_manager/file_manager_export.h
@@ -18,11 +18,7 @@
 #endif  // defined(FILE_MANAGER_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(FILE_MANAGER_IMPLEMENTATION)
 #define FILE_MANAGER_EXPORT __attribute__((visibility("default")))
-#else
-#define FILE_MANAGER_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/ui/gfx/animation/animation_export.h b/ui/gfx/animation/animation_export.h
index 586d5d8..0a53336 100644
--- a/ui/gfx/animation/animation_export.h
+++ b/ui/gfx/animation/animation_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(ANIMATION_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(ANIMATION_IMPLEMENTATION)
 #define ANIMATION_EXPORT __attribute__((visibility("default")))
-#else
-#define ANIMATION_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/ui/gfx/animation/keyframe/keyframe_animation_export.h b/ui/gfx/animation/keyframe/keyframe_animation_export.h
index 518c84a..571b9f2 100644
--- a/ui/gfx/animation/keyframe/keyframe_animation_export.h
+++ b/ui/gfx/animation/keyframe/keyframe_animation_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(CC_ANIMATION_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(GFX_KEYFRAME_ANIMATION_IMPLEMENTATION)
 #define GFX_KEYFRAME_ANIMATION_EXPORT __attribute__((visibility("default")))
-#else
-#define GFX_KEYFRAME_ANIMATION_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/ui/gfx/codec/codec_export.h b/ui/gfx/codec/codec_export.h
index 76b31443..083dfb6 100644
--- a/ui/gfx/codec/codec_export.h
+++ b/ui/gfx/codec/codec_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(CODEC_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(CODEC_IMPLEMENTATION)
 #define CODEC_EXPORT __attribute__((visibility("default")))
-#else
-#define CODEC_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/ui/gfx/color_space_export.h b/ui/gfx/color_space_export.h
index 2474ed93..b253d38 100644
--- a/ui/gfx/color_space_export.h
+++ b/ui/gfx/color_space_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(COLOR_SPACE_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(COLOR_SPACE_IMPLEMENTATION)
 #define COLOR_SPACE_EXPORT __attribute__((visibility("default")))
-#else
-#define COLOR_SPACE_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/ui/gfx/gfx_skia_export.h b/ui/gfx/gfx_skia_export.h
index 7287f48..6e9e827 100644
--- a/ui/gfx/gfx_skia_export.h
+++ b/ui/gfx/gfx_skia_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(GEOMETRY_SKIA_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(GFX_SKIA_IMPLEMENTATION)
 #define GFX_SKIA_EXPORT __attribute__((visibility("default")))
-#else
-#define GFX_SKIA_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/ui/gfx/ipc/buffer_types/gfx_ipc_export.h b/ui/gfx/ipc/buffer_types/gfx_ipc_export.h
index ba99e9fd..4fad60a 100644
--- a/ui/gfx/ipc/buffer_types/gfx_ipc_export.h
+++ b/ui/gfx/ipc/buffer_types/gfx_ipc_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(GFX_IPC_BUFFER_TYPES_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(GFX_IPC_BUFFER_TYPES_IMPLEMENTATION)
 #define GFX_IPC_BUFFER_TYPES_EXPORT __attribute__((visibility("default")))
-#else
-#define GFX_IPC_BUFFER_TYPES_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/ui/gfx/ipc/color/gfx_ipc_color_export.h b/ui/gfx/ipc/color/gfx_ipc_color_export.h
index 4ba0e11d..0c0526ee 100644
--- a/ui/gfx/ipc/color/gfx_ipc_color_export.h
+++ b/ui/gfx/ipc/color/gfx_ipc_color_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(GFX_IPC_COLOR_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(GFX_IPC_COLOR_IMPLEMENTATION)
 #define GFX_IPC_COLOR_EXPORT __attribute__((visibility("default")))
-#else
-#define GFX_IPC_COLOR_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/ui/gfx/ipc/geometry/gfx_ipc_geometry_export.h b/ui/gfx/ipc/geometry/gfx_ipc_geometry_export.h
index 912a3b1..49d69087b 100644
--- a/ui/gfx/ipc/geometry/gfx_ipc_geometry_export.h
+++ b/ui/gfx/ipc/geometry/gfx_ipc_geometry_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(GFX_IPC_GEOMETRY_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(GFX_IPC_GEOMETRY_IMPLEMENTATION)
 #define GFX_IPC_GEOMETRY_EXPORT __attribute__((visibility("default")))
-#else
-#define GFX_IPC_GEOMETRY_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/ui/gfx/ipc/gfx_ipc_export.h b/ui/gfx/ipc/gfx_ipc_export.h
index 6cd3002e..c8aa394 100644
--- a/ui/gfx/ipc/gfx_ipc_export.h
+++ b/ui/gfx/ipc/gfx_ipc_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(GFX_IPC_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(GFX_IPC_IMPLEMENTATION)
 #define GFX_IPC_EXPORT __attribute__((visibility("default")))
-#else
-#define GFX_IPC_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/ui/gfx/ipc/skia/gfx_skia_ipc_export.h b/ui/gfx/ipc/skia/gfx_skia_ipc_export.h
index 0872eef..bdfc9e0 100644
--- a/ui/gfx/ipc/skia/gfx_skia_ipc_export.h
+++ b/ui/gfx/ipc/skia/gfx_skia_ipc_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(GFX_SKIA_IPC_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(GFX_SKIA_IPC_IMPLEMENTATION)
 #define GFX_SKIA_IPC_EXPORT __attribute__((visibility("default")))
-#else
-#define GFX_SKIA_IPC_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/ui/gfx/range/gfx_range_export.h b/ui/gfx/range/gfx_range_export.h
index db3491e..fcf717fd6 100644
--- a/ui/gfx/range/gfx_range_export.h
+++ b/ui/gfx/range/gfx_range_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(GFX_RANGE_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(GFX_RANGE_IMPLEMENTATION)
 #define GFX_RANGE_EXPORT __attribute__((visibility("default")))
-#else
-#define GFX_RANGE_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/ui/gfx/switches_export.h b/ui/gfx/switches_export.h
index ba153f2b5..da608aad 100644
--- a/ui/gfx/switches_export.h
+++ b/ui/gfx/switches_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(GFX_SWITCHES_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(GFX_SWITCHES_IMPLEMENTATION)
 #define GFX_SWITCHES_EXPORT __attribute__((visibility("default")))
-#else
-#define GFX_SWITCHES_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/ui/gl/gl_export.h b/ui/gl/gl_export.h
index e7e7cd3..9d1f1c8c 100644
--- a/ui/gl/gl_export.h
+++ b/ui/gl/gl_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(GL_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(GL_IMPLEMENTATION)
 #define GL_EXPORT __attribute__((visibility("default")))
-#else
-#define GL_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/ui/gl/init/gl_init_export.h b/ui/gl/init/gl_init_export.h
index 1a6fe98c..e14db38a 100644
--- a/ui/gl/init/gl_init_export.h
+++ b/ui/gl/init/gl_init_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(GL_INIT_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(GL_INIT_IMPLEMENTATION)
 #define GL_INIT_EXPORT __attribute__((visibility("default")))
-#else
-#define GL_INIT_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/ui/gtk/gtk_compat.cc b/ui/gtk/gtk_compat.cc
index 946eb3e..37bb482 100644
--- a/ui/gtk/gtk_compat.cc
+++ b/ui/gtk/gtk_compat.cc
@@ -15,6 +15,7 @@
 #include "base/nix/xdg_util.h"
 #include "base/no_destructor.h"
 #include "base/strings/string_number_conversions.h"
+#include "ui/base/ui_base_switches.h"
 #include "ui/gfx/color_palette.h"
 #include "ui/gtk/gtk_stubs.h"
 #include "ui/gtk/ime_compat_check.h"
@@ -26,8 +27,6 @@
 
 namespace {
 
-const char kGtkVersionFlag[] = "gtk-version";
-
 struct Gdk3Rgba {
   gdouble r;
   gdouble g;
@@ -136,7 +135,7 @@
 
   auto* cmd = base::CommandLine::ForCurrentProcess();
   unsigned int gtk_version;
-  if (!base::StringToUint(cmd->GetSwitchValueASCII(kGtkVersionFlag),
+  if (!base::StringToUint(cmd->GetSwitchValueASCII(switches::kGtkVersionFlag),
                           &gtk_version)) {
     gtk_version = 0;
   }
diff --git a/ui/gtk/gtk_ui.cc b/ui/gtk/gtk_ui.cc
index 95bdbe6..79cdad0 100644
--- a/ui/gtk/gtk_ui.cc
+++ b/ui/gtk/gtk_ui.cc
@@ -30,6 +30,7 @@
 #include "base/nix/xdg_util.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/observer_list.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
 #include "chrome/browser/themes/theme_properties.h"  // nogncheck
 #include "third_party/skia/include/core/SkBitmap.h"
@@ -552,6 +553,15 @@
   return dark;
 }
 
+std::vector<std::string> GtkUi::GetCmdLineFlagsForCopy() const {
+  const auto& gtk_version = GtkVersion();
+  uint32_t major_version =
+      gtk_version.IsValid() ? gtk_version.components()[0] : 0;
+  return {std::string(switches::kUiToolkitFlag) + "=gtk",
+          std::string(switches::kGtkVersionFlag) + "=" +
+              base::NumberToString(major_version)};
+}
+
 void GtkUi::SetDarkTheme(bool dark) {
   auto* settings = gtk_settings_get_default();
   g_object_set(settings, "gtk-application-prefer-dark-theme", dark, nullptr);
diff --git a/ui/gtk/gtk_ui.h b/ui/gtk/gtk_ui.h
index 421b03a..b1245f2 100644
--- a/ui/gtk/gtk_ui.h
+++ b/ui/gtk/gtk_ui.h
@@ -94,6 +94,7 @@
       ui::WindowButtonOrderObserver* observer) override;
   WindowFrameAction GetWindowFrameAction(
       WindowFrameActionSource source) override;
+  std::vector<std::string> GetCmdLineFlagsForCopy() const override;
 
   // ui::LinuxUiTheme:
   ui::NativeTheme* GetNativeTheme() const override;
diff --git a/ui/linux/fake_linux_ui.cc b/ui/linux/fake_linux_ui.cc
index 00e2e3a..c12919e 100644
--- a/ui/linux/fake_linux_ui.cc
+++ b/ui/linux/fake_linux_ui.cc
@@ -89,6 +89,10 @@
   return WindowFrameAction::kNone;
 }
 
+std::vector<std::string> FakeLinuxUi::GetCmdLineFlagsForCopy() const {
+  return {};
+}
+
 bool FakeLinuxUi::PreferDarkTheme() const {
   return false;
 }
diff --git a/ui/linux/fake_linux_ui.h b/ui/linux/fake_linux_ui.h
index 95922e9..0d9fd7e 100644
--- a/ui/linux/fake_linux_ui.h
+++ b/ui/linux/fake_linux_ui.h
@@ -47,6 +47,7 @@
       ui::WindowButtonOrderObserver* observer) override;
   WindowFrameAction GetWindowFrameAction(
       WindowFrameActionSource source) override;
+  std::vector<std::string> GetCmdLineFlagsForCopy() const override;
 
   // ui::LinuxUiTheme:
   ui::NativeTheme* GetNativeTheme() const override;
diff --git a/ui/linux/fallback_linux_ui.cc b/ui/linux/fallback_linux_ui.cc
index fb511d4..91a1b1f6 100644
--- a/ui/linux/fallback_linux_ui.cc
+++ b/ui/linux/fallback_linux_ui.cc
@@ -6,6 +6,7 @@
 
 #include "base/time/time.h"
 #include "ui/base/ime/linux/linux_input_method_context.h"
+#include "ui/base/ui_base_switches.h"
 #include "ui/events/keycodes/dom/dom_keyboard_layout_map.h"
 #include "ui/gfx/font_render_params.h"
 #include "ui/gfx/geometry/size.h"
@@ -116,6 +117,10 @@
   }
 }
 
+std::vector<std::string> FallbackLinuxUi::GetCmdLineFlagsForCopy() const {
+  return {std::string(switches::kUiToolkitFlag) + "=fallback"};
+}
+
 bool FallbackLinuxUi::PreferDarkTheme() const {
   return theme_is_dark_;
 }
diff --git a/ui/linux/fallback_linux_ui.h b/ui/linux/fallback_linux_ui.h
index d6bf9d3..47ff01e 100644
--- a/ui/linux/fallback_linux_ui.h
+++ b/ui/linux/fallback_linux_ui.h
@@ -50,6 +50,7 @@
       ui::WindowButtonOrderObserver* observer) override;
   WindowFrameAction GetWindowFrameAction(
       WindowFrameActionSource source) override;
+  std::vector<std::string> GetCmdLineFlagsForCopy() const override;
 
   // ui::LinuxUiTheme:
   ui::NativeTheme* GetNativeTheme() const override;
diff --git a/ui/linux/linux_ui.h b/ui/linux/linux_ui.h
index 4ce8218e..c4805571 100644
--- a/ui/linux/linux_ui.h
+++ b/ui/linux/linux_ui.h
@@ -205,6 +205,10 @@
   virtual WindowFrameAction GetWindowFrameAction(
       WindowFrameActionSource source) = 0;
 
+  // Returns the command line flags that should be copied to subprocesses
+  // to have the same toolkit and version as this process.
+  virtual std::vector<std::string> GetCmdLineFlagsForCopy() const = 0;
+
  protected:
   struct CmdLineArgs {
     CmdLineArgs();
diff --git a/ui/message_center/message_center_export.h b/ui/message_center/message_center_export.h
index 3893917c..a4aa3eb 100644
--- a/ui/message_center/message_center_export.h
+++ b/ui/message_center/message_center_export.h
@@ -18,11 +18,7 @@
 #endif  // defined(MESSAGE_CENTER_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(MESSAGE_CENTER_IMPLEMENTATION)
 #define MESSAGE_CENTER_EXPORT __attribute__((visibility("default")))
-#else
-#define MESSAGE_CENTER_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/ui/message_center/public/cpp/message_center_public_export.h b/ui/message_center/public/cpp/message_center_public_export.h
index 553574e..2dff2ac6 100644
--- a/ui/message_center/public/cpp/message_center_public_export.h
+++ b/ui/message_center/public/cpp/message_center_public_export.h
@@ -18,11 +18,7 @@
 #endif  // defined(MESSAGE_CENTER_PUBLIC_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(MESSAGE_CENTER_PUBLIC_IMPLEMENTATION)
 #define MESSAGE_CENTER_PUBLIC_EXPORT __attribute__((visibility("default")))
-#else
-#define MESSAGE_CENTER_PUBLIC_EXPORT
-#endif  // defined(MESSAGE_CENTER_PUBLIC_IMPLEMENTATION)
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/ui/ozone/platform/wayland/host/xdg_foreign_wrapper.cc b/ui/ozone/platform/wayland/host/xdg_foreign_wrapper.cc
index f89eb2f..5548279b 100644
--- a/ui/ozone/platform/wayland/host/xdg_foreign_wrapper.cc
+++ b/ui/ozone/platform/wayland/host/xdg_foreign_wrapper.cc
@@ -203,19 +203,26 @@
 }
 
 XdgForeignWrapper::XdgForeignWrapper(WaylandConnection* connection,
-                                     wl::Object<zxdg_exporter_v1> exporter_v1) {
+                                     wl::Object<zxdg_exporter_v1> exporter_v1)
+    : XdgForeignWrapper(connection) {
   impl_ = std::make_unique<
       XdgForeignWrapperImpl<zxdg_exporter_v1, zxdg_exported_v1>>(
       connection, std::move(exporter_v1));
 }
 
 XdgForeignWrapper::XdgForeignWrapper(WaylandConnection* connection,
-                                     wl::Object<zxdg_exporter_v2> exporter_v2) {
+                                     wl::Object<zxdg_exporter_v2> exporter_v2)
+    : XdgForeignWrapper(connection) {
   impl_ = std::make_unique<
       XdgForeignWrapperImpl<zxdg_exporter_v2, zxdg_exported_v2>>(
       connection, std::move(exporter_v2));
 }
 
+XdgForeignWrapper::XdgForeignWrapper(WaylandConnection* connection) {
+  CHECK(connection);
+  window_removal_observer_.Observe(connection->window_manager());
+}
+
 XdgForeignWrapper::~XdgForeignWrapper() = default;
 
 void XdgForeignWrapper::ExportSurfaceToForeign(WaylandWindow* window,
diff --git a/ui/ozone/platform/wayland/host/xdg_foreign_wrapper.h b/ui/ozone/platform/wayland/host/xdg_foreign_wrapper.h
index c9ee679..cd99693 100644
--- a/ui/ozone/platform/wayland/host/xdg_foreign_wrapper.h
+++ b/ui/ozone/platform/wayland/host/xdg_foreign_wrapper.h
@@ -49,9 +49,13 @@
   void ExportSurfaceToForeign(WaylandWindow* window, OnHandleExported cb);
 
  private:
+  explicit XdgForeignWrapper(WaylandConnection* connection);
+
   // WaylandWindowObserver:
   void OnWindowRemoved(WaylandWindow* window) override;
 
+  base::ScopedObservation<WaylandWindowManager, WaylandWindowObserver>
+      window_removal_observer_{this};
   std::unique_ptr<XdgForeignWrapperInternal> impl_;
 };
 
diff --git a/ui/ozone/platform/wayland/host/xdg_session_manager.cc b/ui/ozone/platform/wayland/host/xdg_session_manager.cc
index 3a461a6b..3c62452 100644
--- a/ui/ozone/platform/wayland/host/xdg_session_manager.cc
+++ b/ui/ozone/platform/wayland/host/xdg_session_manager.cc
@@ -116,6 +116,7 @@
                                      const int32_t window_id) {
   if (auto* session = GetSession(session_id)) {
     session->RemoveToplevel(window_id);
+    return;
   }
   DLOG(WARNING) << "No session found for id=" << session_id;
 }
diff --git a/ui/platform_window/stub/stub_window_export.h b/ui/platform_window/stub/stub_window_export.h
index cf3f3ad9..33bf2b98 100644
--- a/ui/platform_window/stub/stub_window_export.h
+++ b/ui/platform_window/stub/stub_window_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(STUB_WINDOW_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(STUB_WINDOW_IMPLEMENTATION)
 #define STUB_WINDOW_EXPORT __attribute__((visibility("default")))
-#else
-#define STUB_WINDOW_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/ui/platform_window/win/win_window_export.h b/ui/platform_window/win/win_window_export.h
index 09c5839..9b5b28a 100644
--- a/ui/platform_window/win/win_window_export.h
+++ b/ui/platform_window/win/win_window_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(WIN_WINDOW_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(WIN_WINDOW_IMPLEMENTATION)
 #define WIN_WINDOW_EXPORT __attribute__((visibility("default")))
-#else
-#define WIN_WINDOW_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/ui/qt/qt_ui.cc b/ui/qt/qt_ui.cc
index 18b10a9..30364f9 100644
--- a/ui/qt/qt_ui.cc
+++ b/ui/qt/qt_ui.cc
@@ -33,6 +33,7 @@
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/base/ime/linux/linux_input_method_context.h"
 #include "ui/base/ime/text_edit_commands.h"
+#include "ui/base/ui_base_switches.h"
 #include "ui/color/color_mixer.h"
 #include "ui/color/color_provider.h"
 #include "ui/color/color_provider_manager.h"
@@ -61,16 +62,15 @@
 
 namespace {
 
-const char kQtVersionFlag[] = "qt-version";
-
 void* LoadLibrary(const base::FilePath& path) {
   return dlopen(path.value().c_str(), RTLD_NOW | RTLD_GLOBAL);
 }
 
 bool PreferQt6() {
   auto* cmd = base::CommandLine::ForCurrentProcess();
-  if (cmd->HasSwitch(kQtVersionFlag)) {
-    std::string qt_version_string = cmd->GetSwitchValueASCII(kQtVersionFlag);
+  if (cmd->HasSwitch(switches::kQtVersionFlag)) {
+    std::string qt_version_string =
+        cmd->GetSwitchValueASCII(switches::kQtVersionFlag);
     unsigned int qt_version = 0;
     if (base::StringToUint(qt_version_string, &qt_version)) {
       switch (qt_version) {
@@ -395,6 +395,12 @@
   }
 }
 
+std::vector<std::string> QtUi::GetCmdLineFlagsForCopy() const {
+  return {std::string(switches::kUiToolkitFlag) + "=qt",
+          std::string(switches::kQtVersionFlag) + "=" +
+              base::NumberToString(qt_version_)};
+}
+
 DISABLE_CFI_VCALL
 bool QtUi::PreferDarkTheme() const {
   return color_utils::IsDark(
diff --git a/ui/qt/qt_ui.h b/ui/qt/qt_ui.h
index c0cfead..a73906e 100644
--- a/ui/qt/qt_ui.h
+++ b/ui/qt/qt_ui.h
@@ -65,6 +65,7 @@
       ui::WindowButtonOrderObserver* observer) override;
   WindowFrameAction GetWindowFrameAction(
       WindowFrameActionSource source) override;
+  std::vector<std::string> GetCmdLineFlagsForCopy() const override;
 
   // ui::LinuxUiTheme:
   ui::NativeTheme* GetNativeTheme() const override;
diff --git a/ui/shell_dialogs/shell_dialogs_export.h b/ui/shell_dialogs/shell_dialogs_export.h
index 2555a8f5..03a5e9d 100644
--- a/ui/shell_dialogs/shell_dialogs_export.h
+++ b/ui/shell_dialogs/shell_dialogs_export.h
@@ -18,11 +18,7 @@
 #endif  // defined(SHELL_DIALOGS_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(SHELL_DIALOGS_IMPLEMENTATION)
 #define SHELL_DIALOGS_EXPORT __attribute__((visibility("default")))
-#else
-#define SHELL_DIALOGS_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/ui/snapshot/snapshot_export.h b/ui/snapshot/snapshot_export.h
index 07df776..d3ad90a 100644
--- a/ui/snapshot/snapshot_export.h
+++ b/ui/snapshot/snapshot_export.h
@@ -18,11 +18,7 @@
 #endif  // defined(SNAPSHOT_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(SNAPSHOT_IMPLEMENTATION)
 #define SNAPSHOT_EXPORT __attribute__((visibility("default")))
-#else
-#define SNAPSHOT_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/ui/surface/surface_export.h b/ui/surface/surface_export.h
index c1d62f86..662af5e 100644
--- a/ui/surface/surface_export.h
+++ b/ui/surface/surface_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(SURFACE_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(SURFACE_IMPLEMENTATION)
 #define SURFACE_EXPORT __attribute__((visibility("default")))
-#else
-#define SURFACE_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/ui/touch_selection/ui_touch_selection_export.h b/ui/touch_selection/ui_touch_selection_export.h
index 160bc37d..b80d887 100644
--- a/ui/touch_selection/ui_touch_selection_export.h
+++ b/ui/touch_selection/ui_touch_selection_export.h
@@ -22,11 +22,7 @@
 
 #else  // !defined(WIN32)
 
-#if defined(UI_TOUCH_SELECTION_IMPLEMENTATION)
 #define UI_TOUCH_SELECTION_EXPORT __attribute__((visibility("default")))
-#else
-#define UI_TOUCH_SELECTION_EXPORT
-#endif
 
 #endif
 
diff --git a/ui/views/controls/webview/webview_export.h b/ui/views/controls/webview/webview_export.h
index 5673b3b..919ec10 100644
--- a/ui/views/controls/webview/webview_export.h
+++ b/ui/views/controls/webview/webview_export.h
@@ -18,11 +18,7 @@
 #endif  // defined(WEBVIEW_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(WEBVIEW_IMPLEMENTATION)
 #define WEBVIEW_EXPORT __attribute__((visibility("default")))
-#else
-#define WEBVIEW_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/ui/views/examples/views_examples_export.h b/ui/views/examples/views_examples_export.h
index 4d3ea7c4..8471cb9 100644
--- a/ui/views/examples/views_examples_export.h
+++ b/ui/views/examples/views_examples_export.h
@@ -18,11 +18,7 @@
 #endif  // defined(VIEWS_EXAMPLES_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(VIEWS_EXAMPLES_IMPLEMENTATION)
 #define VIEWS_EXAMPLES_EXPORT __attribute__((visibility("default")))
-#else
-#define VIEWS_EXAMPLES_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/ui/views/views_export.h b/ui/views/views_export.h
index 218900b..2bc88a45 100644
--- a/ui/views/views_export.h
+++ b/ui/views/views_export.h
@@ -18,11 +18,7 @@
 #endif  // defined(VIEWS_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(VIEWS_IMPLEMENTATION)
 #define VIEWS_EXPORT __attribute__((visibility("default")))
-#else
-#define VIEWS_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/ui/views_content_client/views_content_client_export.h b/ui/views_content_client/views_content_client_export.h
index 8e05789..43ac0772 100644
--- a/ui/views_content_client/views_content_client_export.h
+++ b/ui/views_content_client/views_content_client_export.h
@@ -18,11 +18,7 @@
 #endif  // defined(VIEWS_CONTENT_CLIENT_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(VIEWS_CONTENT_CLIENT_IMPLEMENTATION)
 #define VIEWS_CONTENT_CLIENT_EXPORT __attribute__((visibility("default")))
-#else
-#define VIEWS_CONTENT_CLIENT_EXPORT
-#endif
 #endif  // defined(VIEWS_CONTENT_CLIENT_IMPLEMENTATION)
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/ui/web_dialogs/web_dialogs_export.h b/ui/web_dialogs/web_dialogs_export.h
index a8d990e..7f616cf 100644
--- a/ui/web_dialogs/web_dialogs_export.h
+++ b/ui/web_dialogs/web_dialogs_export.h
@@ -18,11 +18,7 @@
 #endif  // defined(WEB_DIALOGS_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(WEB_DIALOGS_IMPLEMENTATION)
 #define WEB_DIALOGS_EXPORT __attribute__((visibility("default")))
-#else
-#define WEB_DIALOGS_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/ui/wm/public/wm_public_export.h b/ui/wm/public/wm_public_export.h
index b376333..582ee09 100644
--- a/ui/wm/public/wm_public_export.h
+++ b/ui/wm/public/wm_public_export.h
@@ -18,11 +18,7 @@
 #endif  // defined(WM_PUBLIC_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(WM_PUBLIC_IMPLEMENTATION)
 #define WM_PUBLIC_EXPORT __attribute__((visibility("default")))
-#else
-#define WM_PUBLIC_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/url/ipc/url_ipc_export.h b/url/ipc/url_ipc_export.h
index ca500ab..3780fc9 100644
--- a/url/ipc/url_ipc_export.h
+++ b/url/ipc/url_ipc_export.h
@@ -15,11 +15,7 @@
 #endif  // defined(URL_IPC_IMPLEMENTATION)
 
 #else  // defined(WIN32)
-#if defined(URL_IPC_IMPLEMENTATION)
 #define URL_IPC_EXPORT __attribute__((visibility("default")))
-#else
-#define URL_IPC_EXPORT
-#endif
 #endif
 
 #else  // defined(COMPONENT_BUILD)
diff --git a/v8 b/v8
index 8b2e5e5..3005b00 160000
--- a/v8
+++ b/v8
@@ -1 +1 @@
-Subproject commit 8b2e5e57af618aadee7b3b9f967348c87da61591
+Subproject commit 3005b00df2a95266a853252e20e78f637adc5c2f