diff --git a/DEPS b/DEPS
index 09d94c6..65df147 100644
--- a/DEPS
+++ b/DEPS
@@ -295,7 +295,7 @@
   # 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': '91e81b807fa486c4f6b6400fbef49c6983b23791',
+  'src_internal_revision': 'e5f2041ac763622cc191728d4b6db0d67c86e284',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
@@ -303,11 +303,11 @@
   # 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': '871f1dbb7c6d0378be4993459fe3a9e7ce0643dc',
+  'v8_revision': 'd7fa05296b658db5ecdc143f6cc60185c60001d0',
   # 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': '65180785da62e5d12f0d2a28fa0cca1fe98f83ca',
+  'angle_revision': '71b58cdcd7311f9572b064da0a2871e7c313535e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -375,7 +375,7 @@
   # 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': '8475a50251395107239e490b67ca5b765acbde52',
+  'crossbench_revision': '6b953c0b98c81c00f8458266d07b570f58b40848',
   # 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': '21ef1023f916f40d54fdde5e51e03f1ea7c44b47',
+  'devtools_frontend_revision': 'fa131039ce7473c0f2f6f18c6039e2a9a72b35a4',
   # 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': '892bb94dfc85a3e60b25eb2c6e707adfd58e1079',
+  'dawn_revision': '5c36eda233b37810c8ca6ad00ad0300b0f821890',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -451,7 +451,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling crabbyavif
   # and whatever else without interference from each other.
-  'crabbyavif_revision': '91072f337edd69508dbefaf290f01722e228738a',
+  'crabbyavif_revision': 'eb883022a5886739f07f0241f918e2be97d65ff0',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Speedometer main
   # and whatever else without interference from each other.
@@ -530,7 +530,7 @@
   'libcxx_revision':       'a01c02c9d4acbdae3b7e8a2f3ee58579a9c29f96',
 
   # GN CIPD package version.
-  'gn_version': 'git_revision:0c25d1bbde6ef17e1e6d3938011f56a00c02e457',
+  'gn_version': 'git_revision:cad8f67e2dd0cea00bbe4566efe2fdf4c1821563',
 
   # ninja CIPD package.
   'ninja_package': 'infra/3pp/tools/ninja/',
@@ -1486,12 +1486,12 @@
 
   'src/clank': {
     'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' +
-    'd994a61ddd8f6d1483c152745c394af9f8e31b8b',
+    '51cc9995b6d55b6e3da653848c3dbae2e3d68316',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
   'src/docs/website': {
-    'url': Var('chromium_git') + '/website.git' + '@' + '7122f63ddd3611e2418115a44dba01c08c2527e5',
+    'url': Var('chromium_git') + '/website.git' + '@' + '47f1ea00aea33ec880152302ae07f19171403754',
   },
 
   'src/ios/third_party/earl_grey2/src': {
@@ -1948,7 +1948,7 @@
   # Tools used when building Chrome for Chrome OS. This affects both the Simple
   # Chrome workflow, as well as the chromeos-chrome ebuild.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '8cd89158d9c6823fac6328bccf0c389a058cef94',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '4be93907c73c20225535e7efdc6a59c8eceb3775',
       'condition': 'checkout_chromeos',
   },
 
@@ -1978,7 +1978,7 @@
 
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '196d9416106a6ace0729b09acda46546c4a12ce1',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '0888983e473613d853c419245b97294e46497798',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -2526,7 +2526,7 @@
     Var('pdfium_git') + '/pdfium.git' + '@' +  Var('pdfium_revision'),
 
   'src/third_party/perfetto':
-    Var('chromium_git') + '/external/github.com/google/perfetto.git' + '@' + '5a75e4faa948b13d3bb6567ea2dd6347cabac610',
+    Var('chromium_git') + '/external/github.com/google/perfetto.git' + '@' + '188242925b83331305ff6f8f146b2a6667f21ac2',
 
   'src/base/tracing/test/data': {
     'bucket': 'perfetto',
@@ -2845,13 +2845,13 @@
       'dep_type': 'cipd',
   },
 
-  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@8f94220fd61f543da56fd22b6e1737d9dddd223b',
+  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@bb4c9a8250cf96a68bd4c3ba51ef231a59b6c502',
   'src/third_party/glslang/src': '{chromium_git}/external/github.com/KhronosGroup/glslang@963588074b26326ff0426c8953c1235213309bdb',
   'src/third_party/spirv-cross/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Cross@b8fcf307f1f347089e3c46eb4451d27f32ebc8d3',
   'src/third_party/spirv-headers/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Headers@c9aad99f9276817f18f72a4696239237c83cb775',
   'src/third_party/spirv-tools/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Tools@66fe610946a6d98169f8ebe9ca483f64c4009fa5',
   'src/third_party/vulkan-headers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Headers@75ad707a587e1469fb53a901b9b68fe9f6fbc11f',
-  'src/third_party/vulkan-loader/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Loader@0f4bd6649d19b5df4e76381725ab6f5a2eeb9f30',
+  'src/third_party/vulkan-loader/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Loader@c913466fdc5004584890f89ff91121bdb2ffd4ba',
   'src/third_party/vulkan-tools/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Tools@60b640cb931814fcc6dabe4fc61f4738c56579f6',
   'src/third_party/vulkan-utility-libraries/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Utility-Libraries@4f628210460c4df62029959cc7fb237ac75f7189',
   'src/third_party/vulkan-validation-layers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-ValidationLayers@d22cddc5c410f55093aa56f062a77ab963edaa63',
@@ -2899,7 +2899,7 @@
     Var('chromium_git') + '/webpagereplay.git' + '@' + Var('webpagereplay_revision'),
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '41eb9cd3c7ec1ca38cc9e4037cc2256125d560bd',
+    Var('webrtc_git') + '/src.git' + '@' + '3d059dd3ca2653d372ea4463dadc53a40baf7fa8',
 
   # 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.
@@ -3032,7 +3032,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/help_app/app',
-        'version': 'kXu-e0Kt1hs_puHssQC03ob29zPWTjnMljzFGzvMI3IC',
+        'version': 'xRX7X7u1mp-6a_4zCjxr9qAbE6F6-5Z3Yg3kW1N4oa4C',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -3043,7 +3043,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/media_app/app',
-        'version': 'nrQRyKDe3aW7lLdfksJMBjBcI9MnRyRsQYmGildKjCAC',
+        'version': 'OVbsvGD15732Yzz3g5J-r3cACTFvQGT-vaeQMOmgV-IC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -3103,7 +3103,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/android_deps/autorolled',
-              'version': '5jhxywV7wAc6P8KUwq9kM4caswnIJ4Y0n8PEaEAIpNMC',
+              'version': 'gbOkrsK3-7xG1DV1l2XCjQfdKOR_9eY9G2JQ08TEQkwC',
           },
       ],
       'condition': 'checkout_android and non_git_source',
@@ -4525,7 +4525,7 @@
   # grepping.
   'src/chrome/installer/mac/internal': {
       'url': Var('chrome_git') + '/chrome/installer/mac/internal.git' + '@' +
-        'f6030c49cc22768ce47357e5a35e872cec925a65',
+        'be25bbacee6f69ac6f42e248354047cd048d66cf',
       'condition': 'checkout_src_internal',
   },
 
@@ -4619,7 +4619,7 @@
 
   'src/components/optimization_guide/internal': {
       'url': Var('chrome_git') + '/chrome/components/optimization_guide.git' + '@' +
-        'f58041bc19d8cd401fe481331f0b399ccc3a73a4',
+        '315d910d17d14d2132ec008d5867390276e54386',
       'condition': 'checkout_src_internal',
   },
 
@@ -4685,7 +4685,7 @@
 
   'src/ios_internal':  {
       'url': Var('chrome_git') + '/chrome/ios_internal.git' + '@' +
-        '3c0b4772fb5fc185f901affc85f8c1aa315ca8a8',
+        '5f6cd5274af6da369855f92f45b3721d4707b7e4',
       'condition': 'checkout_ios and checkout_src_internal',
   },
 
diff --git a/android_webview/browser/prefetch/aw_prefetch_manager.cc b/android_webview/browser/prefetch/aw_prefetch_manager.cc
index e5bc3de..4e7a25a 100644
--- a/android_webview/browser/prefetch/aw_prefetch_manager.cc
+++ b/android_webview/browser/prefetch/aw_prefetch_manager.cc
@@ -105,6 +105,21 @@
   // TODO(crbug.com/393344309): Apply deduping to all prefetch requests (not
   // just WebView).
   if (!browser_context_->IsPrefetchDuplicate(pf_url, expected_no_vary_search)) {
+    // Make room for the new prefetch request by evicting the older ones.
+    if (all_prefetches_map_.size() >= max_prefetches_) {
+      int num_prefetches_to_evict =
+          all_prefetches_map_.size() - max_prefetches_ + 1;
+      auto it = all_prefetches_map_.begin();
+
+      while (num_prefetches_to_evict > 0 && it != all_prefetches_map_.end()) {
+        // Because the keys should be sequential based on when the prefetch
+        // associated with it was added, a standard iteration should always
+        // prioritize removing the oldest entry.
+        it = all_prefetches_map_.erase(it);
+        num_prefetches_to_evict--;
+      }
+    }
+
     std::unique_ptr<content::PrefetchHandle> prefetch_handle =
         browser_context_->StartBrowserPrefetchRequest(
             pf_url, AW_PREFETCH_METRICS_SUFFIX,
diff --git a/android_webview/browser/prefetch/aw_prefetch_manager.h b/android_webview/browser/prefetch/aw_prefetch_manager.h
index d47bdd4a..d46661c 100644
--- a/android_webview/browser/prefetch/aw_prefetch_manager.h
+++ b/android_webview/browser/prefetch/aw_prefetch_manager.h
@@ -82,21 +82,7 @@
       std::unique_ptr<content::PrefetchHandle> prefetch_handle) {
     CHECK(prefetch_handle);
     CHECK(max_prefetches_ > 0u);
-
-    // Make room for the new prefetch request by evicting the older ones.
-    if (all_prefetches_map_.size() >= max_prefetches_) {
-      int num_prefetches_to_evict =
-          all_prefetches_map_.size() - max_prefetches_ + 1;
-      auto it = all_prefetches_map_.begin();
-
-      while (num_prefetches_to_evict > 0 && it != all_prefetches_map_.end()) {
-        // Because the keys should be sequential based on when the prefetch
-        // associated with it was added, a standard iteration should always
-        // prioritize removing the oldest entry.
-        it = all_prefetches_map_.erase(it);
-        num_prefetches_to_evict--;
-      }
-    }
+    CHECK(all_prefetches_map_.size() < max_prefetches_);
 
     const int32_t new_prefetch_key = GetNextPrefetchKey();
     all_prefetches_map_[new_prefetch_key] = std::move(prefetch_handle);
@@ -104,13 +90,13 @@
     return new_prefetch_key;
   }
 
-  std::vector<content::PrefetchHandle*> GetAllPrefetchesForTesting() const {
-    std::vector<content::PrefetchHandle*> raw_prefetches;
-    raw_prefetches.reserve(all_prefetches_map_.size());
+  std::vector<int32_t> GetAllPrefetchKeysForTesting() const {
+    std::vector<int32_t> prefetch_keys;
+    prefetch_keys.reserve(all_prefetches_map_.size());
     for (const auto& prefetch_pair : all_prefetches_map_) {
-      raw_prefetches.push_back(prefetch_pair.second.get());
+      prefetch_keys.push_back(prefetch_pair.first);
     }
-    return raw_prefetches;
+    return prefetch_keys;
   }
 
   int GetLastPrefetchKeyForTesting() const { return last_prefetch_key_; }
diff --git a/android_webview/browser/prefetch/aw_prefetch_manager_unittest.cc b/android_webview/browser/prefetch/aw_prefetch_manager_unittest.cc
index 9823e13..ed748c3 100644
--- a/android_webview/browser/prefetch/aw_prefetch_manager_unittest.cc
+++ b/android_webview/browser/prefetch/aw_prefetch_manager_unittest.cc
@@ -78,14 +78,14 @@
   }
 
   // Check the number of prefetches after exceeding the limit.
-  EXPECT_EQ(prefetch_manager.GetAllPrefetchesForTesting().size(), 3u);
+  EXPECT_EQ(prefetch_manager.GetAllPrefetchKeysForTesting().size(), 3u);
 
   // Add one more to trigger a removal
   prefetch_manager.StartPrefetchRequest(
       base::android::AttachCurrentThread(), "https://example.com/last",
       /*prefetch_params=*/nullptr, /*callback=*/nullptr,
       /*callback_executor=*/nullptr);
-  EXPECT_EQ(prefetch_manager.GetAllPrefetchesForTesting().size(),
+  EXPECT_EQ(prefetch_manager.GetAllPrefetchKeysForTesting().size(),
             3u);  // Should still be at the limit
 }
 
@@ -110,8 +110,8 @@
       /*callback_executor=*/nullptr);
 
   // 2. Capture the initial prefetches.
-  std::vector<content::PrefetchHandle*> initial_prefetches =
-      prefetch_manager.GetAllPrefetchesForTesting();
+  std::vector<int32_t> initial_prefetches =
+      prefetch_manager.GetAllPrefetchKeysForTesting();
   EXPECT_EQ(initial_prefetches.size(), 2u);
 
   // 3. Do the third request.
@@ -120,8 +120,8 @@
       /*prefetch_params=*/nullptr, /*callback=*/nullptr,
       /*callback_executor=*/nullptr);
 
-  std::vector<content::PrefetchHandle*> current_prefetches =
-      prefetch_manager.GetAllPrefetchesForTesting();
+  std::vector<int32_t> current_prefetches =
+      prefetch_manager.GetAllPrefetchKeysForTesting();
   EXPECT_EQ(current_prefetches.size(), 2u);
 
   // Verify that the oldest prefetch is removed.
@@ -155,7 +155,7 @@
         /*prefetch_params=*/nullptr, /*callback=*/nullptr,
         /*callback_executor=*/nullptr);
   }
-  EXPECT_EQ(prefetch_manager.GetAllPrefetchesForTesting().size(), 5u);
+  EXPECT_EQ(prefetch_manager.GetAllPrefetchKeysForTesting().size(), 5u);
 
   // Now, let's lower that number with more than 1. Let's say 2.
   prefetch_manager.SetMaxPrefetches(base::android::AttachCurrentThread(),
@@ -168,7 +168,7 @@
       /*callback_executor=*/nullptr);
 
   // Should be on the latest setting, 2.
-  EXPECT_EQ(prefetch_manager.GetAllPrefetchesForTesting().size(), 2u);
+  EXPECT_EQ(prefetch_manager.GetAllPrefetchKeysForTesting().size(), 2u);
 }
 
 TEST_F(AwPrefetchManagerTest, PrefetchHandleKeysAlwaysIncrement) {
diff --git a/android_webview/test/data/web_tests/webexposed/global-interface-listing-expected.txt b/android_webview/test/data/web_tests/webexposed/global-interface-listing-expected.txt
index e903153..36b12d2a 100644
--- a/android_webview/test/data/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/android_webview/test/data/web_tests/webexposed/global-interface-listing-expected.txt
@@ -9906,6 +9906,7 @@
     attribute @@toStringTag
     getter finished
     getter ready
+    getter transitionRoot
     getter types
     getter updateCallbackDone
     method constructor
diff --git a/ash/quick_pair/common/fast_pair/fast_pair_decoder.cc b/ash/quick_pair/common/fast_pair/fast_pair_decoder.cc
index 0b254f6..f74f85b 100644
--- a/ash/quick_pair/common/fast_pair/fast_pair_decoder.cc
+++ b/ash/quick_pair/common/fast_pair/fast_pair_decoder.cc
@@ -16,12 +16,27 @@
 
 constexpr int kMinModelIdLength = 3;
 constexpr int kHeaderIndex = 0;
-constexpr int kHeaderLengthBitmask = 0b00011110;
-constexpr int kHeaderLengthOffset = 1;
-constexpr int kHeaderVersionBitmask = 0b11100000;
-constexpr int kHeaderVersionOffset = 5;
+
+// Format 2018 (0bVVVLLLLR)
+constexpr int kHeaderLengthBitmask2018 = 0b00011110;
+constexpr int kHeaderLengthOffset2018 = 1;
+constexpr int kHeaderVersionBitmask2018 = 0b11100000;
+constexpr int kHeaderVersionOffset2018 = 5;
 constexpr int kMaxModelIdLength = 14;
+const std::string k2018HeaderPrefix = "06";
+
+// Format 2022 (0bVVVVFFFF)
+constexpr int kHeaderVersionBitmask2022 = 0b11110000;
+constexpr int kHeaderVersionOffset2022 = 4;
+constexpr int kHeaderFlagsBitmask2022 = 0b00001111;
+
 constexpr int kHeaderLength = 1;
+constexpr int kExtraFieldHeaderLength = 1;
+constexpr int kExtraFieldLengthBitmask = 0b11110000;
+constexpr int kExtraFieldLengthOffset = 4;
+constexpr int kExtraFieldTypeBitmask = 0b00001111;
+constexpr int kExtraFieldTypeOffset = 0;
+constexpr int kExtraFieldTypeModelId = 7;
 
 }  // namespace
 
@@ -30,20 +45,60 @@
 namespace fast_pair_decoder {
 
 int GetVersion(const std::vector<uint8_t>* service_data) {
+  if (!features::IsFastPairAdvertisingFormat2025Enabled()) {
+    return service_data->size() == kMinModelIdLength
+               ? 0
+               : GetVersion2018(service_data);
+  }
+
+  // If feature flag enabled, only handle 2022 format advertisements.
   return service_data->size() == kMinModelIdLength
              ? 0
-             : ((*service_data)[kHeaderIndex] & kHeaderVersionBitmask) >>
-                   kHeaderVersionOffset;
+             : GetVersion2022(service_data);
 }
 
+/** For format 2018, get version from the first 3 bits (VVVLLLLR) */
+int GetVersion2018(const std::vector<uint8_t>* service_data) {
+  return ((*service_data)[kHeaderIndex] & kHeaderVersionBitmask2018) >>
+         kHeaderVersionOffset2018;
+}
+
+/** For format 2022, get version from the first 4 bits (VVVVFFFF) */
+int GetVersion2022(const std::vector<uint8_t>* service_data) {
+  return ((*service_data)[kHeaderIndex] & kHeaderVersionBitmask2022) >>
+         kHeaderVersionOffset2022;
+}
+
+int GetFlags(const std::vector<uint8_t>* service_data) {
+  CHECK(features::IsFastPairAdvertisingFormat2025Enabled());
+
+  return (*service_data)[kHeaderIndex] & kHeaderFlagsBitmask2022;
+}
+
+int GetExtraFieldLength(const std::vector<uint8_t>* service_data, int index) {
+  return ((*service_data)[index] & kExtraFieldLengthBitmask) >>
+         kExtraFieldLengthOffset;
+}
+
+int GetExtraFieldType(const std::vector<uint8_t>* service_data, int index) {
+  return ((*service_data)[index] & kExtraFieldTypeBitmask) >>
+         kExtraFieldTypeOffset;
+}
+
+// TODO(399163998): Remove deprecated code after feature launch.
 int GetIdLength(const std::vector<uint8_t>* service_data) {
+  CHECK(!features::IsFastPairAdvertisingFormat2025Enabled());
+
   return service_data->size() == kMinModelIdLength
              ? kMinModelIdLength
-             : ((*service_data)[kHeaderIndex] & kHeaderLengthBitmask) >>
-                   kHeaderLengthOffset;
+             : ((*service_data)[kHeaderIndex] & kHeaderLengthBitmask2018) >>
+                   kHeaderLengthOffset2018;
 }
 
+// TODO(399163998): Remove deprecated code after feature launch.
 bool IsIdLengthValid(const std::vector<uint8_t>* service_data) {
+  CHECK(!features::IsFastPairAdvertisingFormat2025Enabled());
+
   int id_length = GetIdLength(service_data);
   return kMinModelIdLength <= id_length && id_length <= kMaxModelIdLength &&
          id_length + kHeaderLength <= static_cast<int>(service_data->size());
@@ -61,6 +116,45 @@
            GetVersion(service_data) == 0 && IsIdLengthValid(service_data)));
 }
 
+// Returns hex-encoded string of field with type |extra_field_type|, or
+// std::nullopt if no match was found or fields are improperly formatted.
+std::optional<std::string> GetExtraField(
+    const std::vector<uint8_t>* service_data,
+    int extra_field_type) {
+  // Iterate through extra fields, which have the following format (including
+  // overall header): Header (0bVVVVFFFF) LT V LT V
+  // ...
+
+  size_t headerIndex = kHeaderLength;
+  while (headerIndex < service_data->size()) {
+    size_t length = GetExtraFieldLength(service_data, headerIndex);
+    int type = GetExtraFieldType(service_data, headerIndex);
+
+    size_t start = headerIndex + kExtraFieldHeaderLength;
+    size_t end = start + length;
+    if (length < 1 || end > service_data->size()) {
+      LOG(ERROR) << __func__ << ": Improper length for extra fields, aborting.";
+      return std::nullopt;
+    }
+
+    if (type == extra_field_type) {
+      // found it!
+      // Extract extra field to new vector.
+      std::vector<uint8_t> extra_field(length);
+
+      for (size_t i = 0; i < length; i++) {
+        extra_field[i] = (*service_data)[i + start];
+      }
+      return base::HexEncode(extra_field);
+    }
+
+    headerIndex = end;
+  }
+
+  LOG(ERROR) << __func__ << ": Extra field type not found.";
+  return std::nullopt;
+}
+
 std::optional<std::string> GetHexModelIdFromServiceData(
     const std::vector<uint8_t>* service_data) {
   if (service_data == nullptr || service_data->size() < kMinModelIdLength) {
@@ -70,26 +164,46 @@
     // If the size is 3, all the bytes are the ID,
     return base::HexEncode(*service_data);
   }
-  // Otherwise, the first byte is a header which contains the length of the
-  // big-endian model ID that follows. The model ID will be trimmed if it
-  // contains leading zeros.
-  int id_index = 1;
-  int end = id_index + GetIdLength(service_data);
 
-  // Ignore leading zeros.
-  while ((*service_data)[id_index] == 0 && end - id_index > kMinModelIdLength) {
-    id_index++;
+  // The following logic is used only to support the deprecated 2018 format.
+  if (!features::IsFastPairAdvertisingFormat2025Enabled()) {
+    // Otherwise, the first byte is a header which contains the length of the
+    // big-endian model ID that follows. The model ID will be trimmed if it
+    // contains leading zeros.
+    int id_index = 1;
+    int end = id_index + GetIdLength(service_data);
+
+    // Ignore leading zeros.
+    while ((*service_data)[id_index] == 0 &&
+           end - id_index > kMinModelIdLength) {
+      id_index++;
+    }
+
+    // Copy appropriate bytes to new array.
+    int bytes_size = end - id_index;
+    base::FixedArray<uint8_t> bytes(bytes_size);
+
+    for (int i = 0; i < bytes_size; i++) {
+      bytes[i] = (*service_data)[i + id_index];
+    }
+
+    return base::HexEncode(base::span<uint8_t>(bytes));
   }
 
-  // Copy appropriate bytes to new array.
-  int bytes_size = end - id_index;
-  base::FixedArray<uint8_t> bytes(bytes_size);
-
-  for (int i = 0; i < bytes_size; i++) {
-    bytes[i] = (*service_data)[i + id_index];
+  std::string service_data_str = base::HexEncode(*service_data);
+  if (service_data_str.starts_with(k2018HeaderPrefix)) {
+    LOG(WARNING) << __func__
+                 << ": Ignoring deprecated 2018 advertising format.";
+    return std::nullopt;
   }
 
-  return base::HexEncode(base::span<uint8_t>(bytes));
+  // As per go/spec_audio_sharing_payload, the 1st byte will be 00 for devices
+  // that support LE audio sharing (introduced 2025).
+  if (GetVersion(service_data) == 0 && GetFlags(service_data) == 0) {
+    return GetExtraField(service_data, kExtraFieldTypeModelId);
+  }
+
+  return std::nullopt;
 }
 
 }  // namespace fast_pair_decoder
diff --git a/ash/quick_pair/common/fast_pair/fast_pair_decoder.h b/ash/quick_pair/common/fast_pair/fast_pair_decoder.h
index 251c1a0..412ba979 100644
--- a/ash/quick_pair/common/fast_pair/fast_pair_decoder.h
+++ b/ash/quick_pair/common/fast_pair/fast_pair_decoder.h
@@ -18,6 +18,7 @@
 COMPONENT_EXPORT(QUICK_PAIR_COMMON)
 int GetVersion(const std::vector<uint8_t>* service_data);
 
+// Deprecated by kFastPairAdvertisingFormat2025.
 COMPONENT_EXPORT(QUICK_PAIR_COMMON)
 int GetIdLength(const std::vector<uint8_t>* service_data);
 
@@ -29,6 +30,19 @@
 std::optional<std::string> GetHexModelIdFromServiceData(
     const std::vector<uint8_t>* service_data);
 
+// Internal helpers
+COMPONENT_EXPORT(QUICK_PAIR_COMMON)
+int GetVersion2022(const std::vector<uint8_t>* service_data);
+int GetVersion2018(const std::vector<uint8_t>* service_data);
+COMPONENT_EXPORT(QUICK_PAIR_COMMON)
+int GetFlags(const std::vector<uint8_t>* service_data);
+COMPONENT_EXPORT(QUICK_PAIR_COMMON)
+std::optional<std::string> GetExtraField(
+    const std::vector<uint8_t>* service_data,
+    int extra_field_type);
+int GetExtraFieldLength(const std::vector<uint8_t>* service_data, int index);
+int GetExtraFieldType(const std::vector<uint8_t>* service_data, int index);
+
 }  // namespace fast_pair_decoder
 }  // namespace quick_pair
 }  // namespace ash
diff --git a/ash/quick_pair/common/fast_pair/fast_pair_decoder_unittest.cc b/ash/quick_pair/common/fast_pair/fast_pair_decoder_unittest.cc
index 6e52883..01429bdc 100644
--- a/ash/quick_pair/common/fast_pair/fast_pair_decoder_unittest.cc
+++ b/ash/quick_pair/common/fast_pair/fast_pair_decoder_unittest.cc
@@ -18,6 +18,12 @@
 namespace {
 
 const std::string kModelId = "AABBCC";
+// Corresponds to version = 0 and flags = 0.
+constexpr uint8_t kHeaderLeAudioSharing2025 = 0b00000000;
+// Corresponds to length of 3 and type of 7 (model ID).
+constexpr uint8_t kModelIdExtraFieldHeader = 0b00110111;
+
+// Only used when feature flag is disabled.
 const std::string kLongModelId = "1122334455667788";
 const std::string kPaddedModelId = "00001111";
 const std::string kTrimmedModelId = "001111";
@@ -115,27 +121,26 @@
   EXPECT_FALSE(HasModelId(&bytes));
 }
 
-// #######################################################################
-// Begin: Tests with kFastPairAdvertisingFormat2025 enabled, or tests to
-// be run for both feature enablement states.
-// #######################################################################
+TEST_F(FastPairDecoderTest, GetHexModelIdFromServiceData_Handles2018Format) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatures(
+      /*enabled_features=*/{},
+      /*disabled_features=*/{ash::features::kFastPairAdvertisingFormat2025});
+  std::vector<uint8_t> service_data = FastPairServiceDataCreator::Builder()
+                                          .SetHeader(0b00000110)
+                                          .SetModelId(kModelId)
+                                          .Build()
+                                          ->CreateServiceData();
 
-TEST_F(FastPairDecoderTest, GetHexModelIdFromServiceData_NoResultForNullData) {
-  EXPECT_EQ(GetHexModelIdFromServiceData(nullptr), std::nullopt);
-}
-
-TEST_F(FastPairDecoderTest, GetHexModelIdFromServiceData_NoResultForEmptyData) {
-  std::vector<uint8_t> empty;
-  EXPECT_EQ(GetHexModelIdFromServiceData(&empty), std::nullopt);
-}
-
-TEST_F(FastPairDecoderTest, GetHexModelIdFromServiceData_ThreeByteData) {
-  std::vector<uint8_t> bytes;
-  base::HexStringToBytes(kModelId, &bytes);
-  EXPECT_EQ(GetHexModelIdFromServiceData(&bytes), kModelId);
+  EXPECT_EQ(GetHexModelIdFromServiceData(&service_data), kModelId);
 }
 
 TEST_F(FastPairDecoderTest, GetHexModelIdFromServiceData_LongModelId) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatures(
+      /*enabled_features=*/{},
+      /*disabled_features=*/{ash::features::kFastPairAdvertisingFormat2025});
+
   std::vector<uint8_t> service_data = FastPairServiceDataCreator::Builder()
                                           .SetHeader(kLongModelIdHeader)
                                           .SetModelId(kLongModelId)
@@ -146,6 +151,11 @@
 }
 
 TEST_F(FastPairDecoderTest, GetHexModelIdFromServiceData_LongModelIdTrimmed) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatures(
+      /*enabled_features=*/{},
+      /*disabled_features=*/{ash::features::kFastPairAdvertisingFormat2025});
+
   std::vector<uint8_t> service_data = FastPairServiceDataCreator::Builder()
                                           .SetHeader(kPaddedLongModelIdHeader)
                                           .SetModelId(kPaddedModelId)
@@ -155,6 +165,185 @@
   EXPECT_EQ(GetHexModelIdFromServiceData(&service_data), kTrimmedModelId);
 }
 
+// #######################################################################
+// Begin: Tests with kFastPairAdvertisingFormat2025 enabled, or tests to
+// be run for both feature enablement states.
+// #######################################################################
+
+TEST_F(FastPairDecoderTest, GetVersion) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatures(
+      /*enabled_features=*/{ash::features::kFastPairAdvertisingFormat2025},
+      /*disabled_features=*/{});
+  std::vector<uint8_t> bytes = FastPairServiceDataCreator::Builder()
+                                   .SetHeader(0b00110001)
+                                   .SetModelId("11223344")
+                                   .Build()
+                                   ->CreateServiceData();
+  // Correctly uses 0bVVVVFFFF (2022) format to get version and flags.
+  EXPECT_EQ(GetVersion2022(&bytes), GetVersion(&bytes));
+  EXPECT_EQ(3, GetVersion(&bytes));
+  EXPECT_EQ(1, GetFlags(&bytes));
+}
+
+TEST_F(FastPairDecoderTest, GetHexModelIdFromServiceData_NoResultForNullData) {
+  EXPECT_EQ(GetHexModelIdFromServiceData(nullptr), std::nullopt);
+}
+
+TEST_F(FastPairDecoderTest, GetHexModelIdFromServiceData_NoResultForEmptyData) {
+  std::vector<uint8_t> empty;
+  EXPECT_EQ(GetHexModelIdFromServiceData(&empty), std::nullopt);
+}
+
+TEST_F(FastPairDecoderTest,
+       GetHexModelIdFromServiceData_NoResultForUnsupportedFormat) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatures(
+      /*enabled_features=*/{ash::features::kFastPairAdvertisingFormat2025},
+      /*disabled_features=*/{});
+  std::vector<uint8_t> service_data = FastPairServiceDataCreator::Builder()
+                                          .SetHeader(0b01100110)
+                                          .SetModelId(kModelId)
+                                          .Build()
+                                          ->CreateServiceData();
+
+  // Since the header does not match 2018 or 2015 Le Audio Sharing format, and
+  // the advertisement data is more than 3 bytes, we will not parse the model
+  // ID.
+  EXPECT_EQ(GetHexModelIdFromServiceData(&service_data), std::nullopt);
+}
+
+TEST_F(FastPairDecoderTest, GetHexModelIdFromServiceData_ThreeByteData) {
+  std::vector<uint8_t> bytes;
+  base::HexStringToBytes(kModelId, &bytes);
+  EXPECT_EQ(GetHexModelIdFromServiceData(&bytes), kModelId);
+}
+
+TEST_F(FastPairDecoderTest, GetHexModelIdFromServiceData_Ignores2018Format) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatures(
+      /*enabled_features=*/{ash::features::kFastPairAdvertisingFormat2025},
+      /*disabled_features=*/{});
+  std::vector<uint8_t> service_data = FastPairServiceDataCreator::Builder()
+                                          .SetHeader(0b00000110)
+                                          .SetModelId(kModelId)
+                                          .Build()
+                                          ->CreateServiceData();
+
+  EXPECT_EQ(GetHexModelIdFromServiceData(&service_data), std::nullopt);
+}
+
+TEST_F(FastPairDecoderTest, GetExtraFields_GetsAllFields) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatures(
+      /*enabled_features=*/{ash::features::kFastPairAdvertisingFormat2025},
+      /*disabled_features=*/{});
+
+  // Extra field headers correctly use 0bLLLLTTTT format to describe
+  // the length and type of the following fields.
+  std::vector<uint8_t> service_data = FastPairServiceDataCreator::Builder()
+                                          .SetHeader(kHeaderLeAudioSharing2025)
+                                          .AddExtraFieldHeader(0b00010001)
+                                          .AddExtraField("AA")
+                                          .AddExtraFieldHeader(0b00100010)
+                                          .AddExtraField("BBBB")
+                                          .AddExtraFieldHeader(0b00110011)
+                                          .AddExtraField("CCCCCC")
+                                          .Build()
+                                          ->CreateServiceData();
+
+  EXPECT_EQ(GetExtraField(&service_data, 1), "AA");
+  EXPECT_EQ(GetExtraField(&service_data, 2), "BBBB");
+  EXPECT_EQ(GetExtraField(&service_data, 3), "CCCCCC");
+}
+
+TEST_F(FastPairDecoderTest, GetExtraFields_HandlesIncorrectFieldLengths) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatures(
+      /*enabled_features=*/{ash::features::kFastPairAdvertisingFormat2025},
+      /*disabled_features=*/{});
+  std::vector<uint8_t> service_data = FastPairServiceDataCreator::Builder()
+                                          .SetHeader(kHeaderLeAudioSharing2025)
+                                          .AddExtraFieldHeader(0b00000000)
+                                          .AddExtraField(kModelId)
+                                          .Build()
+                                          ->CreateServiceData();
+
+  // Handle illegal extra field length (0).
+  EXPECT_EQ(GetExtraField(&service_data, 0), std::nullopt);
+
+  service_data = FastPairServiceDataCreator::Builder()
+                     .SetHeader(kHeaderLeAudioSharing2025)
+                     .AddExtraFieldHeader(0b00100000)
+                     .AddExtraField("A")
+                     .Build()
+                     ->CreateServiceData();
+
+  // Handle incorrect extra field length (2), when length is 1.
+  EXPECT_EQ(GetExtraField(&service_data, 0), std::nullopt);
+}
+
+TEST_F(FastPairDecoderTest,
+       GetHexModelIdFromServiceData_LeAudioSharing2025_HandlesStandardFormat) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatures(
+      /*enabled_features=*/{ash::features::kFastPairAdvertisingFormat2025},
+      /*disabled_features=*/{});
+  std::vector<uint8_t> service_data =
+      FastPairServiceDataCreator::Builder()
+          .SetHeader(kHeaderLeAudioSharing2025)
+          .AddExtraFieldHeader(kModelIdExtraFieldHeader)
+          .AddExtraField(kModelId)
+          .Build()
+          ->CreateServiceData();
+
+  // Standard model is 3 bytes long, type 7.
+  EXPECT_EQ(GetExtraField(&service_data, 7), kModelId);
+  EXPECT_EQ(GetHexModelIdFromServiceData(&service_data), kModelId);
+}
+
+TEST_F(FastPairDecoderTest,
+       GetHexModelIdFromServiceData_LeAudioSharing2025_HandlesOtherFormats) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatures(
+      /*enabled_features=*/{ash::features::kFastPairAdvertisingFormat2025},
+      /*disabled_features=*/{});
+  std::vector<uint8_t> service_data = FastPairServiceDataCreator::Builder()
+                                          .SetHeader(kHeaderLeAudioSharing2025)
+                                          .AddExtraFieldHeader(0b00010001)
+                                          .AddExtraField("DC")
+                                          .AddExtraFieldHeader(0b01000111)
+                                          .AddExtraField("AABBCCDD")
+                                          .AddExtraFieldHeader(0b00010011)
+                                          .AddExtraField("DC")
+                                          .Build()
+                                          ->CreateServiceData();
+
+  // In this example, we sandwiched model ID among other fields and made it 4
+  // bytes long.
+  EXPECT_EQ(GetExtraField(&service_data, 7), "AABBCCDD");
+  EXPECT_EQ(GetHexModelIdFromServiceData(&service_data), "AABBCCDD");
+}
+
+TEST_F(FastPairDecoderTest,
+       GetHexModelIdFromServiceData_LeAudioSharing2025_ModelIdNotFound) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatures(
+      /*enabled_features=*/{ash::features::kFastPairAdvertisingFormat2025},
+      /*disabled_features=*/{});
+  std::vector<uint8_t> service_data = FastPairServiceDataCreator::Builder()
+                                          .SetHeader(kHeaderLeAudioSharing2025)
+                                          .AddExtraFieldHeader(0b00010001)
+                                          .AddExtraField("DC")
+                                          .AddExtraFieldHeader(0b00010011)
+                                          .AddExtraField("DC")
+                                          .Build()
+                                          ->CreateServiceData();
+
+  // We did not include an extra field with model ID here, so it won't be found.
+  EXPECT_EQ(GetHexModelIdFromServiceData(&service_data), std::nullopt);
+}
+
 }  // namespace fast_pair_decoder
 }  // namespace quick_pair
 }  // namespace ash
diff --git a/ash/webui/diagnostics_ui/OWNERS b/ash/webui/diagnostics_ui/OWNERS
index 0ea7d73..9e0b1bd 100644
--- a/ash/webui/diagnostics_ui/OWNERS
+++ b/ash/webui/diagnostics_ui/OWNERS
@@ -5,4 +5,3 @@
 jimmyxgong@chromium.org
 michaelcheco@google.com
 ashleydp@google.com
-dpad@google.com
diff --git a/ash/wm/layer_tree_synchronizer.cc b/ash/wm/layer_tree_synchronizer.cc
index 1e2e542..4c02832 100644
--- a/ash/wm/layer_tree_synchronizer.cc
+++ b/ash/wm/layer_tree_synchronizer.cc
@@ -4,7 +4,9 @@
 
 #include "ash/wm/layer_tree_synchronizer.h"
 
+#include <array>
 #include <cmath>
+#include <cstdint>
 #include <cstdlib>
 #include <utility>
 #include <vector>
@@ -27,6 +29,11 @@
 
 constexpr float kEpsilon = std::numeric_limits<float>::epsilon();
 
+// The expected number of elements in the `original_layers_info_` map.
+// Currently, we expect a maximum of 8 layers to be modified when
+// synchronizing the rounded corners of a window with its transient children.
+constexpr int kExpectedModifiedLayers = 8;
+
 float Square(float n) {
   return std::pow(n, 2);
 }
@@ -299,14 +306,6 @@
 bool ShouldOverrideCornerRadius(const gfx::RRectF& rect,
                                 const gfx::RRectF& containing_rect,
                                 Corner corner) {
-  if (!containing_rect.HasRoundedCorners()) {
-    return false;
-  }
-
-  if (!containing_rect.rect().Contains(rect.rect())) {
-    return false;
-  }
-
   const gfx::Vector2dF rect_corner_radii = rect.GetCornerRadii(corner);
   const gfx::Vector2dF containing_rect_corner_radii =
       containing_rect.GetCornerRadii(corner);
@@ -352,10 +351,20 @@
 // corner radius of containing_rect.
 Corners FindCornersToOverrideRadius(const gfx::RRectF& rect,
                                     const gfx::RRectF& containing_rect) {
+  static constexpr uint8_t kNumberOfCorners = 4;
+  static constexpr std::array<Corner, kNumberOfCorners> kCorners{
+      Corner::kUpperLeft, Corner::kUpperRight, Corner::kLowerRight,
+      Corner::kLowerLeft};
+
   Corners corners;
 
-  for (auto corner : {Corner::kUpperLeft, Corner::kUpperRight,
-                      Corner::kLowerRight, Corner::kLowerLeft}) {
+  if (!containing_rect.HasRoundedCorners() ||
+      !containing_rect.rect().Contains(rect.rect())) {
+    return corners;
+  }
+
+  corners.reserve(kNumberOfCorners);
+  for (auto corner : kCorners) {
     if (ShouldOverrideCornerRadius(rect, containing_rect, corner)) {
       corners.insert(corner);
     }
@@ -394,7 +403,9 @@
 // LayerTreeSynchronizerBase:
 
 LayerTreeSynchronizerBase::LayerTreeSynchronizerBase(bool restore_tree)
-    : restore_tree_(restore_tree) {}
+    : restore_tree_(restore_tree) {
+  original_layers_info_.reserve(kExpectedModifiedLayers);
+}
 
 LayerTreeSynchronizerBase::~LayerTreeSynchronizerBase() = default;
 
@@ -564,7 +575,6 @@
     const gfx::RRectF& reference_bounds,
     TransientTreeIgnorePredicate ignore_predicate) {
   CHECK(root_window->Contains(window));
-
   for (auto* window_iter : GetTransientTreeIterator(window, ignore_predicate)) {
     const bool altered = SynchronizeLayerTreeRoundedCorners(
         window_iter->layer(), root_window->layer(), reference_bounds);
diff --git a/ash/wm/overview/scoped_overview_transform_window.cc b/ash/wm/overview/scoped_overview_transform_window.cc
index eb49d97b..c6c0e12 100644
--- a/ash/wm/overview/scoped_overview_transform_window.cc
+++ b/ash/wm/overview/scoped_overview_transform_window.cc
@@ -573,10 +573,17 @@
   wm::TranslateRectFromScreen(window_->GetRootWindow(),
                               &contents_bounds_in_root);
 
-  const gfx::RRectF rounded_contents_bounds(
-      contents_bounds_in_root,
+  const gfx::RoundedCornersF contents_rounded_corners =
       window_util::GetMiniWindowRoundedCorners(
-          window(), /*include_header_rounding=*/false));
+          window(), /*include_header_rounding=*/false);
+
+  const gfx::RRectF rounded_contents_bounds(contents_bounds_in_root,
+                                            contents_rounded_corners);
+  const gfx::RRectF rounded_contents_bounds_at_origin(
+      gfx::RectF(contents_bounds_in_root.size()), contents_rounded_corners);
+  if (synchronized_bounds_at_origin_ == rounded_contents_bounds_at_origin) {
+    return;
+  }
 
   auto window_tree_synchronizer([&]() {
     return window_tree_synchronizer_during_drag_
@@ -597,6 +604,8 @@
         return window->GetProperty(kHideInOverviewKey) ||
                window->GetProperty(kExcludeFromTransientTreeTransformKey);
       }));
+
+  synchronized_bounds_at_origin_ = rounded_contents_bounds_at_origin;
 }
 
 void ScopedOverviewTransformWindow::OnTransientChildWindowAdded(
diff --git a/ash/wm/overview/scoped_overview_transform_window.h b/ash/wm/overview/scoped_overview_transform_window.h
index dabda6b..e3177893 100644
--- a/ash/wm/overview/scoped_overview_transform_window.h
+++ b/ash/wm/overview/scoped_overview_transform_window.h
@@ -20,6 +20,7 @@
 #include "ui/aura/window_observer.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/rect_f.h"
+#include "ui/gfx/geometry/rrect_f.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/geometry/transform.h"
 
@@ -223,7 +224,7 @@
   gfx::Rect original_clip_rect_;
 
   // Removes clipping on `window_` during destruction in the case it was not
-  // removed in `RestoreWindw()`. See destructor for more information.
+  // removed in `RestoreWindow()`. See destructor for more information.
   bool reset_clip_on_shutdown_ = true;
 
   std::unique_ptr<ScopedOverviewHideWindows> hidden_transient_children_;
@@ -234,6 +235,8 @@
   std::unique_ptr<WindowTreeSynchronizer> window_tree_synchronizer_;
   std::unique_ptr<WindowTreeSynchronizer> window_tree_synchronizer_during_drag_;
 
+  std::optional<gfx::RRectF> synchronized_bounds_at_origin_;
+
   base::WeakPtrFactory<ScopedOverviewTransformWindow> weak_ptr_factory_{this};
 };
 
diff --git a/base/profiler/OWNERS b/base/profiler/OWNERS
index ab903ae1..57d5f79 100644
--- a/base/profiler/OWNERS
+++ b/base/profiler/OWNERS
@@ -1,4 +1,4 @@
 kartarsingh@google.com
 wittman@chromium.org
 thiabaud@google.com
-spvw@google.com
+spvw@chromium.org
diff --git a/base/test/test_simple_task_runner.h b/base/test/test_simple_task_runner.h
index 4639ab0..54907b75 100644
--- a/base/test/test_simple_task_runner.h
+++ b/base/test/test_simple_task_runner.h
@@ -42,6 +42,9 @@
 // handles the running of tasks that in turn call back into itself
 // (e.g., to post more tasks).
 //
+// Tasks are only run with explicit invocation of `TestSimpleTaskRunner::Run*()`
+// methods.
+//
 // Note that, like any TaskRunner, TestSimpleTaskRunner is
 // ref-counted.
 class TestSimpleTaskRunner : public SingleThreadTaskRunner {
diff --git a/build/android/OWNERS b/build/android/OWNERS
index 561fda4..48266e0c 100644
--- a/build/android/OWNERS
+++ b/build/android/OWNERS
@@ -1,6 +1,5 @@
 agrieve@chromium.org
 bjoyce@chromium.org
-mheikal@chromium.org
 pasko@chromium.org
 smaier@chromium.org
 wnwen@chromium.org
diff --git a/build/android/test_runner.py b/build/android/test_runner.py
index db10bc3..2249732 100755
--- a/build/android/test_runner.py
+++ b/build/android/test_runner.py
@@ -1004,12 +1004,14 @@
   raise Exception('Unknown test type.')
 
 
-def _SinkTestResult(test_result, test_file_name, result_sink_client):
+def _SinkTestResult(test_result, test_file_name, test_instance,
+                    result_sink_client):
   """Upload test result to result_sink.
 
   Args:
     test_result: A BaseTestResult object
     test_file_name: A string representing the file location of the test
+    test_instance: A TestInstance object.
     result_sink_client: A ResultSinkClient object
 
   Returns:
@@ -1031,16 +1033,60 @@
                    link_url, test_result.GetName())
   if https_artifacts:
     html_artifact += '<ul>%s</ul>' % '\n'.join(https_artifacts)
+
   result_sink_client.Post(test_result.GetNameForResultSink(),
                           test_result.GetType(),
                           test_result.GetDuration(),
                           log_decoded,
                           test_file_name,
+                          test_id_structured=_CreateStructuredTestDict(
+                              test_instance, test_result),
                           variant=test_result.GetVariantForResultSink(),
                           failure_reason=test_result.GetFailureReason(),
                           html_artifact=html_artifact)
 
 
+def _CreateStructuredTestDict(test_instance, test_result):
+  """Fills in fields for the structured_test_dict.
+
+  Args:
+    test_instance: A test instance object.
+    test_result: A test result object.
+
+  Returns:
+    A dictionary containing strucuted test id fields.
+  """
+  # Source comes from:
+  # infra/go/src/go.chromium.org/luci/resultdb/sink/proto/v1/test_result.proto
+  struct_test_dict = {
+      'coarseName': '',
+      'fineName': '',
+      'caseNameComponents': [''],
+  }
+
+  test_id = test_result.GetNameForResultSink()
+
+  if test_instance.TestType() in ['instrumentation', 'junit']:
+    re_match = re.search(r'(.*)\.(\w+\$?\w+)#(.*)', test_id)
+    if not re_match:
+      logging.error(
+          'Test id did not match known format, so could not be parsed.')
+      return None
+    struct_test_dict['coarseName'] = re_match.group(1)
+    struct_test_dict['fineName'] = re_match.group(2)
+    # The proto requires a list.
+    struct_test_dict['caseNameComponents'] = [re_match.group(3)]
+  elif test_instance.TestType() == 'gtest':
+    struct_test_dict['coarseName'] = None  # Not used.
+    struct_test_dict['fineName'] = test_instance.suite
+    # The proto requires a list.
+    struct_test_dict['caseNameComponents'] = [test_id]
+  else:
+    return None
+
+  return struct_test_dict
+
+
 _SUPPORTED_IN_PLATFORM_MODE = [
   # TODO(jbudorick): Add support for more test types.
   'gtest',
@@ -1198,7 +1244,8 @@
               match = re.search(r'^(.+\..+)#', r.GetName())
               test_file_name = test_class_to_file_name_dict.get(
                   match.group(1)) if match else None
-              _SinkTestResult(r, test_file_name, result_sink_client)
+              _SinkTestResult(r, test_file_name, test_instance,
+                              result_sink_client)
 
   @contextlib.contextmanager
   def upload_logcats_file():
diff --git a/build/android/test_runner_test.py b/build/android/test_runner_test.py
index 93794cc..b4f13ed 100755
--- a/build/android/test_runner_test.py
+++ b/build/android/test_runner_test.py
@@ -80,6 +80,59 @@
     self.mm_recorder.clear.assert_called_once()
 
 
+class TestRunnerHelperTest(unittest.TestCase):
+
+  def testCreateStructuredTestDict(self):
+    # pylint: disable=protected-access
+    t_instance_mock = mock.MagicMock()
+    t_result_mock = mock.MagicMock()
+    test_id = 'foo.bar.class#test1[28]'
+    t_result_mock.GetNameForResultSink.return_value = test_id
+    t_instance_mock.suite = 'foo_suite'
+
+    # junit tests
+    t_instance_mock.TestType.return_value = 'junit'
+    test_dict = test_runner._CreateStructuredTestDict(t_instance_mock,
+                                                      t_result_mock)
+    self.assertEqual(test_dict['coarseName'], 'foo.bar')
+    self.assertEqual(test_dict['fineName'], 'class')
+    self.assertTrue('test1[28]' in test_dict['caseNameComponents'])
+
+    # instrumentation tests
+    t_instance_mock.TestType.return_value = 'instrumentation'
+    test_dict = test_runner._CreateStructuredTestDict(t_instance_mock,
+                                                      t_result_mock)
+    self.assertEqual(test_dict['coarseName'], 'foo.bar')
+    self.assertEqual(test_dict['fineName'], 'class')
+    self.assertTrue('test1[28]' in test_dict['caseNameComponents'])
+
+    # Can't be parsed as an instrumentation test as it's missing the #.
+    test_id = 'foo.bar.class.test1[28]'
+    t_result_mock.GetNameForResultSink.return_value = test_id
+    test_dict = test_runner._CreateStructuredTestDict(t_instance_mock,
+                                                      t_result_mock)
+    self.assertIsNone(test_dict)
+
+    test_id = 'foo.bar.class$parameter#test1[28]'
+    t_result_mock.GetNameForResultSink.return_value = test_id
+    test_dict = test_runner._CreateStructuredTestDict(t_instance_mock,
+                                                      t_result_mock)
+    self.assertEqual(test_dict['coarseName'], 'foo.bar')
+    self.assertEqual(test_dict['fineName'], 'class$parameter')
+    self.assertTrue('test1[28]' in test_dict['caseNameComponents'])
+
+    # gtest
+    t_instance_mock.TestType.return_value = 'gtest'
+    test_id = 'foo.bar.class.test1[28]'
+    t_result_mock.GetNameForResultSink.return_value = test_id
+    test_dict = test_runner._CreateStructuredTestDict(t_instance_mock,
+                                                      t_result_mock)
+    self.assertIsNone(test_dict['coarseName'])
+    self.assertEqual(test_dict['fineName'], 'foo_suite')
+    self.assertTrue(test_id in test_dict['caseNameComponents'])
+    # pylint: disable=protected-access
+
+
 if __name__ == '__main__':
   # Suppress logging messages.
   unittest.main(buffer=True)
diff --git a/build/rust/BUILD.gn b/build/rust/BUILD.gn
index c2920d4..68a9290f 100644
--- a/build/rust/BUILD.gn
+++ b/build/rust/BUILD.gn
@@ -83,9 +83,15 @@
   ]
 }
 
-# Forbids unsafe code in crates with this config.
+# Forbids unsafe code in crates with this config.  See also `allow_unsafe` which
+# may be set in 1) `rust_static_library`, `rust_executable` and other targets,
+# 2) `gnrt_config.toml` entries for individual third-party crates.
 config("forbid_unsafe") {
-  rustflags = [ "-Funsafe_code" ]
+  rustflags = [
+    # `-F` = "forbid".  Documentation of the warning can be found in
+    # https://doc.rust-lang.org/rustc/lints/listing/allowed-by-default.html#unsafe-code
+    "-Funsafe_code",
+  ]
 }
 
 config("panic_immediate_abort") {
diff --git a/build/rust/cargo_crate.gni b/build/rust/cargo_crate.gni
index 38fe248..0e73b54 100644
--- a/build/rust/cargo_crate.gni
+++ b/build/rust/cargo_crate.gni
@@ -8,27 +8,24 @@
 
 # This template allows for building Cargo crates within gn.
 #
-# It is intended for use with pre-existing (third party) code and
-# is none too efficient. (It will stall the build pipeline whilst
-# it runs build scripts to work out what flags are needed). First
-# party code should directly use first-class gn targets, such as
-# //build/rust/rust_static_library.gni or similar.
+# It is intended for use with pre-existing (third party) code, from `BUILD.gn`
+# files auto-generated by the `gnrt` tool.
 #
-# Because it's intended for third-party code, it automatically
-# defaults to //build/config/compiler:no_chromium_code which
-# suppresses some warnings. If you *do* use this for first party
-# code, you should remove that config and add the equivalent
-# //build/config/compiler:chromium_code config.
+# First party code should prefer to use first-class gn targets, such as
+# `//build/rust/rust_static_library.gni` or similar.  One reason is that the
+# `cargo_crate` template is not very efficient (it will stall the build
+# pipeline whilst it runs build scripts to work out what flags are needed).
 #
 # Arguments:
-#  sources
+#  aliased_deps
+#  allow_unsafe
+#  build_native_rust_unit_tests
+#  crate_name
 #  crate_root
 #  deps
-#  aliased_deps
-#  features
-#  build_native_rust_unit_tests
 #  edition
-#  crate_name
+#  features
+#  sources
 #    All just as in rust_static_library.gni
 #  library_configs/executable_configs
 #    All just as in rust_target.gni
@@ -299,9 +296,17 @@
       }
       rustenv = _rustenv
 
-      # TODO(crbug.com/40259764): don't default to true. This requires changes to
-      # third_party.toml and gnrt when generating third-party build targets.
+      # When the `invoker` doesn't specify `allow_unsafe`, then fall back to
+      # allowing `unsafe`.  This avoids creating additional friction when a new
+      # team experiments with Rust.  OTOH we enforce that when crates actually
+      # land in Chromium, then they need to explicitly declare their
+      # `allow_unsafe` needs - this enforcement is done via presubmits under
+      # `third_party/rust/chromium_crates_io` (see
+      # `CheckExplicitAllowUnsafeForAllCrates` in a `.py` module over there).
       allow_unsafe = true
+      if (defined(invoker.allow_unsafe)) {
+        allow_unsafe = invoker.allow_unsafe
+      }
       rustflags +=
           [ "-Awarnings" ]  # Suppress other warnings in 3rd-party crates.
 
diff --git a/build/util/lib/results/result_sink.py b/build/util/lib/results/result_sink.py
index 34cc154..02a2007 100644
--- a/build/util/lib/results/result_sink.py
+++ b/build/util/lib/results/result_sink.py
@@ -81,6 +81,7 @@
            duration,
            test_log,
            test_file,
+           test_id_structured=None,
            variant=None,
            artifacts=None,
            failure_reason=None,
@@ -97,6 +98,7 @@
       duration: An int representing time in ms.
       test_log: A string representing the test's output.
       test_file: A string representing the file location of the test.
+      test_id_structured: A dictionary containing structured test id fields.
       variant: An optional dict of variant key value pairs as the
           additional variant sent from test runners, which can override
           or add to the variants passed to `rdb stream` command.
@@ -139,6 +141,9 @@
         }
     }
 
+    if test_id_structured:
+      tr['testIdStructured'] = test_id_structured
+
     if tags:
       tr['tags'].extend({
           'key': key_name,
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index 4159ace..8240bcda 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -373,6 +373,7 @@
   "java/src/org/chromium/chrome/browser/contextmenu/ContextMenuItemWithCheckboxViewBinder.java",
   "java/src/org/chromium/chrome/browser/contextmenu/ContextMenuItemWithIconButtonProperties.java",
   "java/src/org/chromium/chrome/browser/contextmenu/ContextMenuItemWithIconButtonViewBinder.java",
+  "java/src/org/chromium/chrome/browser/contextmenu/ContextMenuItemWithRadioButtonViewBinder.java",
   "java/src/org/chromium/chrome/browser/contextmenu/ContextMenuListView.java",
   "java/src/org/chromium/chrome/browser/contextmenu/LensChipDelegate.java",
   "java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchContext.java",
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 84fcbc6d..04aa12ab 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
@@ -26,13 +26,22 @@
 /** Helper for closing all tabs via {@link CloseAllTabsDialog}. */
 @NullMarked
 public class CloseAllTabsHelper {
-    /** Closes all tabs hiding tab groups. */
-    public static void closeAllTabsHidingTabGroups(TabModelSelector tabModelSelector) {
+    /**
+     * Closes all tabs hiding tab groups.
+     *
+     * @param tabModelSelector {@link TabModelSelector} for the activity.
+     * @param allowUndo See {@link TabClosureParams#allowUndo}.
+     */
+    public static void closeAllTabsHidingTabGroups(
+            TabModelSelector tabModelSelector, boolean allowUndo) {
         tabModelSelector
                 .getModel(/* incognito= */ true)
                 .getTabRemover()
                 .closeTabs(
-                        TabClosureParams.closeAllTabs().hideTabGroups(true).build(),
+                        TabClosureParams.closeAllTabs()
+                                .allowUndo(allowUndo)
+                                .hideTabGroups(true)
+                                .build(),
                         /* allowDialog= */ false);
 
         // To support CloseAllTabs for archived tabs, the tabs are restored/deleted but remain
@@ -51,6 +60,7 @@
                 .getTabRemover()
                 .closeTabs(
                         TabClosureParams.closeAllTabs()
+                                .allowUndo(allowUndo)
                                 .hideTabGroups(true)
                                 .withUndoRunnable(restoreArchivedTabsRunnable)
                                 .build(),
@@ -60,22 +70,26 @@
     /**
      * Create a runnable to close all tabs using appropriate animations where applicable.
      *
-     * @param tabModelSelector The tab model selector for the activity.
+     * @param tabModelSelector The {@link TabModelSelector} for the activity.
      * @param isIncognitoOnly Whether to only close incognito tabs.
+     * @param allowUndo See {@link TabClosureParams#allowUndo}.
      */
     public static Runnable buildCloseAllTabsRunnable(
-            TabModelSelector tabModelSelector, boolean isIncognitoOnly) {
-        return () -> closeAllTabs(tabModelSelector, isIncognitoOnly);
+            TabModelSelector tabModelSelector, boolean isIncognitoOnly, boolean allowUndo) {
+        return () -> closeAllTabs(tabModelSelector, isIncognitoOnly, allowUndo);
     }
 
-    private static void closeAllTabs(TabModelSelector tabModelSelector, boolean isIncognitoOnly) {
+    private static void closeAllTabs(
+            TabModelSelector tabModelSelector, boolean isIncognitoOnly, boolean allowUndo) {
         if (isIncognitoOnly) {
             tabModelSelector
                     .getModel(/* incognito= */ true)
                     .getTabRemover()
-                    .closeTabs(TabClosureParams.closeAllTabs().build(), /* allowDialog= */ false);
+                    .closeTabs(
+                            TabClosureParams.closeAllTabs().allowUndo(allowUndo).build(),
+                            /* allowDialog= */ false);
         } else {
-            closeAllTabsHidingTabGroups(tabModelSelector);
+            closeAllTabsHidingTabGroups(tabModelSelector, allowUndo);
         }
     }
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/CloseAllTabsHelperUnitTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/CloseAllTabsHelperUnitTest.java
index 6b8dbad..499c0d1 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/CloseAllTabsHelperUnitTest.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/CloseAllTabsHelperUnitTest.java
@@ -17,6 +17,7 @@
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatcher;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
@@ -26,6 +27,7 @@
 import org.chromium.chrome.browser.app.tabmodel.ArchivedTabModelOrchestrator;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.TabArchiver;
+import org.chromium.chrome.browser.tabmodel.TabClosureParams;
 import org.chromium.chrome.browser.tabmodel.TabGroupModelFilter;
 import org.chromium.chrome.browser.tabmodel.TabGroupModelFilterProvider;
 import org.chromium.chrome.browser.tabmodel.TabModel;
@@ -73,34 +75,65 @@
     }
 
     @Test
-    public void testCloseAllTabsHidingTabGroups() {
-        CloseAllTabsHelper.closeAllTabsHidingTabGroups(mTabModelSelector);
-
-        verify(mRegularTabRemover).closeTabs(argThat(params -> params.isAllTabs), eq(false));
-        verify(mIncognitoTabRemover).closeTabs(argThat(params -> params.isAllTabs), eq(false));
+    public void testCloseAllTabsHidingTabGroups_AllowUndo() {
+        testCloseAllTabsHidingTabGroups(/* shouldAllowUndo= */ true);
     }
 
     @Test
-    public void testBuildCloseAllTabsRunnable_Regular() {
-        Runnable r =
-                CloseAllTabsHelper.buildCloseAllTabsRunnable(
-                        mTabModelSelector, /* isIncognitoOnly= */ false);
-        r.run();
+    public void testCloseAllTabsHidingTabGroups_DisallowUndo() {
+        testCloseAllTabsHidingTabGroups(/* shouldAllowUndo= */ false);
+    }
 
-        verify(mRegularTabRemover).closeTabs(argThat(params -> params.isAllTabs), eq(false));
-        verify(mIncognitoTabRemover).closeTabs(argThat(params -> params.isAllTabs), eq(false));
+    private void testCloseAllTabsHidingTabGroups(boolean shouldAllowUndo) {
+        CloseAllTabsHelper.closeAllTabsHidingTabGroups(mTabModelSelector, shouldAllowUndo);
+
+        ArgumentMatcher<TabClosureParams> tabClosureParamsMatcher =
+                params -> params.isAllTabs && (params.allowUndo == shouldAllowUndo);
+        verify(mRegularTabRemover)
+                .closeTabs(argThat(tabClosureParamsMatcher), /* allowDialog= */ eq(false));
+        verify(mIncognitoTabRemover)
+                .closeTabs(argThat(tabClosureParamsMatcher), /* allowDialog= */ eq(false));
     }
 
     @Test
-    public void testBuildCloseAllTabsRunnable_Incognito() {
+    public void testBuildCloseAllTabsRunnable_Regular_AllowUndo() {
+        testBuildCloseAllTabsRunnable(/* isIncognitoOnly= */ false, /* allowUndo= */ true);
+    }
+
+    @Test
+    public void testBuildCloseAllTabsRunnable_Regular_DisAllowUndo() {
+        testBuildCloseAllTabsRunnable(/* isIncognitoOnly= */ false, /* allowUndo= */ false);
+    }
+
+    @Test
+    public void testBuildCloseAllTabsRunnable_Incognito_AllowUndo() {
+        testBuildCloseAllTabsRunnable(/* isIncognitoOnly= */ true, /* allowUndo= */ true);
+    }
+
+    @Test
+    public void testBuildCloseAllTabsRunnable_Incognito_DisAllowUndo() {
+        testBuildCloseAllTabsRunnable(/* isIncognitoOnly= */ true, /* allowUndo= */ false);
+    }
+
+    private void testBuildCloseAllTabsRunnable(boolean isIncognitoOnly, boolean allowUndo) {
         Runnable r =
                 CloseAllTabsHelper.buildCloseAllTabsRunnable(
-                        mTabModelSelector, /* isIncognitoOnly= */ true);
+                        mTabModelSelector, isIncognitoOnly, allowUndo);
+
         r.run();
 
-        verify(mIncognitoTabRemover).closeTabs(argThat(params -> params.isAllTabs), eq(false));
-
-        verify(mRegularTabRemover, never()).closeTabs(any(), anyBoolean());
+        ArgumentMatcher<TabClosureParams> tabClosureParamsMatcher =
+                params -> params.isAllTabs && (params.allowUndo == allowUndo);
+        if (isIncognitoOnly) {
+            verify(mRegularTabRemover, never()).closeTabs(any(), anyBoolean());
+            verify(mIncognitoTabRemover)
+                    .closeTabs(argThat(tabClosureParamsMatcher), /* allowDialog= */ eq(false));
+        } else {
+            verify(mRegularTabRemover)
+                    .closeTabs(argThat(tabClosureParamsMatcher), /* allowDialog= */ eq(false));
+            verify(mIncognitoTabRemover)
+                    .closeTabs(argThat(tabClosureParamsMatcher), /* allowDialog= */ eq(false));
+        }
     }
 
     @Test
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/CloseAllTabsDialogTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/CloseAllTabsDialogTest.java
index 89b9c5e..3bad6b6 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/CloseAllTabsDialogTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/CloseAllTabsDialogTest.java
@@ -11,10 +11,18 @@
 
 import static org.hamcrest.Matchers.greaterThanOrEqualTo;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertThat;
 
 import static org.chromium.ui.test.util.ViewUtils.onViewWaiting;
 
+import android.os.SystemClock;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+import android.view.MotionEvent.PointerCoords;
+import android.view.MotionEvent.PointerProperties;
+
 import androidx.test.espresso.matcher.ViewMatchers;
 import androidx.test.filters.LargeTest;
 import androidx.test.filters.MediumTest;
@@ -34,11 +42,14 @@
 import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.base.test.util.TestAnimations.EnableAnimations;
+import org.chromium.build.annotations.Nullable;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.ui.appmenu.AppMenuTestSupport;
+import org.chromium.chrome.browser.ui.messages.snackbar.Snackbar;
 import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
+import org.chromium.chrome.test.R;
 import org.chromium.ui.base.DeviceFormFactor;
 
 import java.util.Arrays;
@@ -52,16 +63,24 @@
     @ParameterAnnotations.ClassParameter
     private static final List<ParameterSet> sClassParams =
             Arrays.asList(
-                    new ParameterSet().value(false).name("NonIncognito"),
-                    new ParameterSet().value(true).name("Incognito"));
+                    new ParameterSet()
+                            .value(false, false)
+                            .name("NonIncognito_ClickAppMenuViaTouchScreen"),
+                    new ParameterSet().value(false, true).name("NonIncognito_ClickAppMenuViaMouse"),
+                    new ParameterSet()
+                            .value(true, false)
+                            .name("Incognito_ClickAppMenuViaTouchScreen"),
+                    new ParameterSet().value(true, true).name("Incognito_ClickAppMenuViaMouse"));
 
     @Rule
     public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
 
     private final boolean mIsIncognito;
+    private final boolean mClickAppMenuViaMouse;
 
-    public CloseAllTabsDialogTest(boolean isIncognito) {
+    public CloseAllTabsDialogTest(boolean isIncognito, boolean clickAppMenuViaMouse) {
         mIsIncognito = isIncognito;
+        mClickAppMenuViaMouse = clickAppMenuViaMouse;
     }
 
     @Before
@@ -84,6 +103,7 @@
         ThreadUtils.runOnUiThreadBlocking(
                 () -> {
                     assertEquals(0, selector.getModel(mIsIncognito).getCount());
+                    assertUndoSnackbar(/* wasCloseAllTabsConfirmed= */ true);
                 });
     }
 
@@ -103,6 +123,7 @@
         ThreadUtils.runOnUiThreadBlocking(
                 () -> {
                     assertEquals(1, selector.getModel(mIsIncognito).getCount());
+                    assertUndoSnackbar(/* wasCloseAllTabsConfirmed= */ false);
                 });
     }
 
@@ -124,6 +145,8 @@
         navigateToCloseAllTabsDialog(selector);
         onViewWaiting(withId(org.chromium.chrome.test.R.id.positive_button), true).perform(click());
 
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> assertUndoSnackbar(/* wasCloseAllTabsConfirmed= */ true));
         CriteriaHelper.pollUiThread(() -> 0 == selector.getModel(mIsIncognito).getCount());
     }
 
@@ -142,20 +165,70 @@
                 .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)));
 
         // Click close all tabs.
-        if (mIsIncognito) {
-            ThreadUtils.runOnUiThreadBlocking(
-                    () -> {
+        ThreadUtils.runOnUiThreadBlocking(
+                () ->
                         AppMenuTestSupport.callOnItemClick(
                                 mActivityTestRule.getAppMenuCoordinator(),
-                                org.chromium.chrome.test.R.id.close_all_incognito_tabs_menu_id);
-                    });
+                                mIsIncognito
+                                        ? R.id.close_all_incognito_tabs_menu_id
+                                        : R.id.close_all_tabs_menu_id,
+                                mClickAppMenuViaMouse
+                                        ? createClickTriggeringMotionFromMouse()
+                                        : null));
+    }
+
+    /** Creates a {@link MotionEvent} that matches one from a mouse and would trigger a click. */
+    private static MotionEvent createClickTriggeringMotionFromMouse() {
+        long downTime = SystemClock.uptimeMillis();
+
+        PointerProperties pointerProperties = new MotionEvent.PointerProperties();
+        pointerProperties.id = 0;
+        pointerProperties.toolType = MotionEvent.TOOL_TYPE_MOUSE;
+
+        PointerCoords pointerCoords = new PointerCoords();
+        pointerCoords.x = 0;
+        pointerCoords.y = 0;
+
+        return MotionEvent.obtain(
+                downTime,
+                downTime + 50,
+                MotionEvent.ACTION_UP,
+                /* pointerCount= */ 1,
+                new PointerProperties[] {pointerProperties},
+                new PointerCoords[] {pointerCoords},
+                /* metaState= */ 0,
+                /* buttonState= */ 0,
+                /* xPrecision= */ 1.0f,
+                /* yPrecision= */ 1.0f,
+                /* deviceId= */ 0,
+                /* edgeFlags= */ 0,
+                InputDevice.SOURCE_MOUSE,
+                /* flags= */ 0);
+    }
+
+    /**
+     * Asserts presence of undo snackbar after "close all tabs" dialog is closed.
+     *
+     * @param wasCloseAllTabsConfirmed whether "close all tabs" was confirmed via the dialog, i.e.,
+     *     whether the positive button was clicked.
+     */
+    private void assertUndoSnackbar(boolean wasCloseAllTabsConfirmed) {
+        @Nullable Snackbar snackbar =
+                mActivityTestRule.getActivity().getSnackbarManager().getCurrentSnackbarForTesting();
+        if (!wasCloseAllTabsConfirmed) {
+            assertNull("Cancelling the dialog should never show the undo snackbar", snackbar);
             return;
         }
-        ThreadUtils.runOnUiThreadBlocking(
-                () -> {
-                    AppMenuTestSupport.callOnItemClick(
-                            mActivityTestRule.getAppMenuCoordinator(),
-                            org.chromium.chrome.test.R.id.close_all_tabs_menu_id);
-                });
+
+        if (mIsIncognito) {
+            assertNull("Incognito mode should never show the undo snackbar", snackbar);
+            return;
+        }
+
+        if (mClickAppMenuViaMouse) {
+            assertNull("Closing all tabs with a mouse shouldn't show the undo snackbar", snackbar);
+        } else {
+            assertNotNull("Non-incognito mode should show the undo snackbar", snackbar);
+        }
     }
 }
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/FeedV2NewTabPageTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/FeedV2NewTabPageTest.java
index ffaa29bf..d7e74b1 100644
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/FeedV2NewTabPageTest.java
+++ b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/FeedV2NewTabPageTest.java
@@ -146,7 +146,6 @@
             ChromeRenderTestRule.Builder.withPublicCorpus()
                     .setBugComponent(
                             ChromeRenderTestRule.Component.UI_BROWSER_CONTENT_SUGGESTIONS_FEED)
-                    .setRevision(1)
                     .build();
 
     public final SigninTestRule mSigninTestRule = new SigninTestRule();
diff --git a/chrome/android/java/res/values/ids.xml b/chrome/android/java/res/values/ids.xml
index 13de87b..7bcdcd72 100644
--- a/chrome/android/java/res/values/ids.xml
+++ b/chrome/android/java/res/values/ids.xml
@@ -120,4 +120,12 @@
     <!-- Custom Tabs -->
     <item type="id" name="view_id_tag_key" />
     <item type="id" name="custom_tabs_toolbar_tintable" />
+    <item type="id" name="custom_tabs_app_menu_item_id_0" />
+    <item type="id" name="custom_tabs_app_menu_item_id_1" />
+    <item type="id" name="custom_tabs_app_menu_item_id_2" />
+    <item type="id" name="custom_tabs_app_menu_item_id_3" />
+    <item type="id" name="custom_tabs_app_menu_item_id_4" />
+    <item type="id" name="custom_tabs_app_menu_item_id_5" />
+    <item type="id" name="custom_tabs_app_menu_item_id_6" />
+
 </resources>
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 9551b34..b61811a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -232,6 +232,7 @@
 import org.chromium.chrome.browser.tabmodel.MismatchedIndicesHandler;
 import org.chromium.chrome.browser.tabmodel.NextTabPolicy.NextTabPolicySupplier;
 import org.chromium.chrome.browser.tabmodel.TabClosureParams;
+import org.chromium.chrome.browser.tabmodel.TabClosureParamsUtils;
 import org.chromium.chrome.browser.tabmodel.TabGroupColorUtils;
 import org.chromium.chrome.browser.tabmodel.TabGroupMetadata;
 import org.chromium.chrome.browser.tabmodel.TabGroupModelFilter;
@@ -1708,7 +1709,8 @@
 
     private void handleDebugIntent(Intent intent) {
         if (ACTION_CLOSE_TABS.equals(intent.getAction())) {
-            CloseAllTabsHelper.closeAllTabsHidingTabGroups(getTabModelSelectorSupplier().get());
+            CloseAllTabsHelper.closeAllTabsHidingTabGroups(
+                    getTabModelSelectorSupplier().get(), /* allowUndo= */ true);
         } else if (MemoryPressureListener.handleDebugIntent(
                 ChromeTabbedActivity.this, intent.getAction())) {
             // Handled.
@@ -3334,13 +3336,14 @@
                             TabClosureParams.closeTab(currentTab).build(), /* allowDialog= */ true);
             RecordUserAction.record("MobileTabClosed");
         } else if (id == R.id.close_all_tabs_menu_id) {
-            // TODO(crbug.com/375468032): use triggeringMotionEvent to decide
-            // TabClosureParams.allowUndo when building closeAllTabsRunnable.
+            boolean allowUndo = TabClosureParamsUtils.shouldAllowUndo(triggeringMotionEvent);
 
             // Close both incognito and normal tabs.
             Runnable closeAllTabsRunnable =
                     CloseAllTabsHelper.buildCloseAllTabsRunnable(
-                            getTabModelSelectorSupplier().get(), /* isIncognitoOnly= */ false);
+                            getTabModelSelectorSupplier().get(),
+                            /* isIncognitoOnly= */ false,
+                            allowUndo);
             CloseAllTabsDialog.show(
                     this,
                     getModalDialogManagerSupplier(),
@@ -3348,13 +3351,14 @@
                     closeAllTabsRunnable);
             RecordUserAction.record("MobileMenuCloseAllTabs");
         } else if (id == R.id.close_all_incognito_tabs_menu_id) {
-            // TODO(crbug.com/375468032): use triggeringMotionEvent to decide
-            // TabClosureParams.allowUndo when building closeAllTabsRunnable.
+            boolean allowUndo = TabClosureParamsUtils.shouldAllowUndo(triggeringMotionEvent);
 
             // Close only incognito tabs
             Runnable closeAllTabsRunnable =
                     CloseAllTabsHelper.buildCloseAllTabsRunnable(
-                            getTabModelSelectorSupplier().get(), /* isIncognitoOnly= */ true);
+                            getTabModelSelectorSupplier().get(),
+                            /* isIncognitoOnly= */ true,
+                            allowUndo);
             CloseAllTabsDialog.show(
                     this,
                     getModalDialogManagerSupplier(),
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 9687a1f..2882fac 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
@@ -147,6 +147,7 @@
     private boolean mLoadStateCalled;
     private boolean mRestoreTabsCalled;
     private boolean mRescueTabsCalled;
+    private boolean mRescueTabGroupsCalled;
     private CallbackController mCallbackController = new CallbackController();
     private @Nullable HistoricalTabModelObserver mHistoricalTabModelObserver;
     private boolean mTriggerAutodeleteAfterDataCreated;
@@ -293,15 +294,8 @@
         }
 
         // If the flag is turned off, clear all {@link SavedTabGroup}s of possible archived status.
-        if (!ChromeFeatureList.sAndroidTabDeclutterArchiveTabGroups.isEnabled()
-                && mTabGroupSyncService != null) {
-            for (String syncGroupId : mTabGroupSyncService.getAllGroupIds()) {
-                SavedTabGroup savedTabGroup = mTabGroupSyncService.getGroup(syncGroupId);
-
-                if (savedTabGroup != null && savedTabGroup.archivalTimeMs != null) {
-                    mTabGroupSyncService.updateArchivalStatus(syncGroupId, false);
-                }
-            }
+        if (!ChromeFeatureList.sAndroidTabDeclutterArchiveTabGroups.isEnabled()) {
+            rescueArchivedTabGroups();
         }
     }
 
@@ -479,6 +473,7 @@
                 mCallbackController.makeCancelable(() -> rescueArchivedTabsImpl(orchestrator)),
                 getTabModelSelector(),
                 orchestrator.getTabModelSelector());
+        rescueArchivedTabGroups();
     }
 
     private void rescueArchivedTabsImpl(TabbedModeTabModelOrchestrator orchestrator) {
@@ -492,6 +487,22 @@
         resumeSaveTabList(orchestrator);
     }
 
+    private void rescueArchivedTabGroups() {
+        if (mTabGroupSyncService == null) return;
+
+        if (mRescueTabGroupsCalled) return;
+        mRescueTabGroupsCalled = true;
+
+        // Clear all {@link SavedTabGroup}s of possible archived status as the rescue operation.
+        for (String syncGroupId : mTabGroupSyncService.getAllGroupIds()) {
+            SavedTabGroup savedTabGroup = mTabGroupSyncService.getGroup(syncGroupId);
+
+            if (savedTabGroup != null && savedTabGroup.archivalTimeMs != null) {
+                mTabGroupSyncService.updateArchivalStatus(syncGroupId, false);
+            }
+        }
+    }
+
     public void initializeHistoricalTabModelObserver(Supplier<TabModel> regularTabModelSupplier) {
         if (mHistoricalTabModelObserver != null) {
             mHistoricalTabModelObserver.addSecondaryTabModelSupplier(regularTabModelSupplier);
@@ -585,6 +596,10 @@
         mRescueTabsCalled = false;
     }
 
+    public void resetRescueArchivedTabGroupsForTesting() {
+        mRescueTabGroupsCalled = false;
+    }
+
     public void setTabModelSelectorForTesting(TabModelSelectorBase tabModelSelector) {
         mTabModelSelector = tabModelSelector;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillAccessibilityUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillAccessibilityUtils.java
index b9556f15..658d806 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillAccessibilityUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillAccessibilityUtils.java
@@ -10,10 +10,12 @@
 import org.jni_zero.JNINamespace;
 import org.jni_zero.JniType;
 
+import org.chromium.build.annotations.NullMarked;
 import org.chromium.ui.accessibility.AccessibilityState;
 
 /** Helper methods for accessibility. */
 @JNINamespace("autofill")
+@NullMarked
 public class AutofillAccessibilityUtils {
     // Avoid instantiation by accident.
     private AutofillAccessibilityUtils() {}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillExpirationDateFixFlowPrompt.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillExpirationDateFixFlowPrompt.java
index 67905f6..cf22293 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillExpirationDateFixFlowPrompt.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillExpirationDateFixFlowPrompt.java
@@ -4,6 +4,8 @@
 
 package org.chromium.chrome.browser.autofill;
 
+import static org.chromium.build.NullUtil.assumeNonNull;
+
 import android.content.Context;
 import android.text.Editable;
 import android.view.View;
@@ -119,6 +121,7 @@
 
     @Override
     public void onClick(PropertyModel model, int buttonType) {
+        assumeNonNull(mModalDialogManager);
         if (buttonType == ModalDialogProperties.ButtonType.POSITIVE) {
             String monthString = mMonthInput.getText().toString().trim();
             String yearString = mYearInput.getText().toString().trim();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillNameFixFlowPrompt.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillNameFixFlowPrompt.java
index 4b1f746a..36cf8054 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillNameFixFlowPrompt.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillNameFixFlowPrompt.java
@@ -4,6 +4,8 @@
 
 package org.chromium.chrome.browser.autofill;
 
+import static org.chromium.build.NullUtil.assumeNonNull;
+
 import android.content.Context;
 import android.content.res.Resources;
 import android.text.Editable;
@@ -169,6 +171,7 @@
 
     @Override
     public void onClick(PropertyModel model, int buttonType) {
+        assumeNonNull(mModalDialogManager);
         if (buttonType == ModalDialogProperties.ButtonType.POSITIVE) {
             mDelegate.onUserAcceptCardholderName(mUserNameInput.getText().toString());
             mModalDialogManager.dismissDialog(model, DialogDismissalCause.POSITIVE_BUTTON_CLICKED);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillSaveCardPromptBase.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillSaveCardPromptBase.java
index 595a266..77aab1e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillSaveCardPromptBase.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillSaveCardPromptBase.java
@@ -4,6 +4,8 @@
 
 package org.chromium.chrome.browser.autofill;
 
+import static org.chromium.build.NullUtil.assumeNonNull;
+
 import android.app.Activity;
 import android.content.Context;
 import android.content.res.Resources;
@@ -19,8 +21,9 @@
 
 import androidx.annotation.DrawableRes;
 import androidx.annotation.LayoutRes;
-import androidx.annotation.Nullable;
 
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
 import org.chromium.chrome.R;
 import org.chromium.components.autofill.payments.LegalMessageLine;
 import org.chromium.ui.modaldialog.DialogDismissalCause;
@@ -31,14 +34,15 @@
 /**
  * Base class for creating autofill save card prompts that support displaying legal message line.
  */
+@NullMarked
 public abstract class AutofillSaveCardPromptBase implements ModalDialogProperties.Controller {
     private final AutofillSaveCardPromptBaseDelegate mBaseDelegate;
 
     protected PropertyModel mDialogModel;
-    protected ModalDialogManager mModalDialogManager;
+    protected @Nullable ModalDialogManager mModalDialogManager;
     protected Context mContext;
     protected View mDialogView;
-    private SpannableStringBuilder mSpannableStringBuilder;
+    private @Nullable SpannableStringBuilder mSpannableStringBuilder;
 
     interface AutofillSaveCardPromptBaseDelegate {
         /** Called when a link is clicked. */
@@ -195,6 +199,7 @@
     }
 
     public void dismiss(@DialogDismissalCause int dismissalCause) {
+        assumeNonNull(mModalDialogManager);
         mModalDialogManager.dismissDialog(mDialogModel, dismissalCause);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillSnackbarController.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillSnackbarController.java
index cc6607c..6c57375 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillSnackbarController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillSnackbarController.java
@@ -4,6 +4,8 @@
 
 package org.chromium.chrome.browser.autofill;
 
+import static org.chromium.build.NullUtil.assumeNonNull;
+
 import androidx.annotation.VisibleForTesting;
 
 import org.jni_zero.CalledByNative;
@@ -11,6 +13,8 @@
 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.ui.messages.snackbar.Snackbar;
 import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
 import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManagerProvider;
@@ -22,6 +26,7 @@
  * button.
  */
 @JNINamespace("autofill")
+@NullMarked
 public class AutofillSnackbarController implements SnackbarManager.SnackbarController {
     private final SnackbarManager mSnackbarManager;
     private long mNativeAutofillSnackbarView;
@@ -33,7 +38,7 @@
     }
 
     @Override
-    public void onAction(Object actionData) {
+    public void onAction(@Nullable Object actionData) {
         if (mNativeAutofillSnackbarView == 0) {
             return;
         }
@@ -45,7 +50,7 @@
     }
 
     @Override
-    public void onDismissNoAction(Object actionData) {
+    public void onDismissNoAction(@Nullable Object actionData) {
         if (mNativeAutofillSnackbarView == 0) {
             return;
         }
@@ -55,8 +60,9 @@
     @CalledByNative
     static AutofillSnackbarController create(
             long nativeAutofillSnackbarView, WindowAndroid windowAndroid) {
-        return new AutofillSnackbarController(
-                nativeAutofillSnackbarView, SnackbarManagerProvider.from(windowAndroid));
+        SnackbarManager snackbarManager = SnackbarManagerProvider.from(windowAndroid);
+        assumeNonNull(snackbarManager);
+        return new AutofillSnackbarController(nativeAutofillSnackbarView, snackbarManager);
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java
index f2c7653..8ccb124 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java
@@ -4,6 +4,8 @@
 
 package org.chromium.chrome.browser.autofill;
 
+import static org.chromium.build.NullUtil.assumeNonNull;
+
 import android.app.Activity;
 import android.content.Context;
 import android.os.Handler;
@@ -25,13 +27,14 @@
 import android.widget.TextView;
 
 import androidx.annotation.IntDef;
-import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 import androidx.core.view.ViewCompat;
 
 import org.chromium.base.ResettersForTesting;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.task.AsyncTask;
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.autofill.AutofillUiUtils.ErrorType;
 import org.chromium.components.autofill.ImageSize;
@@ -47,15 +50,16 @@
 import java.util.Calendar;
 
 /** A prompt that bugs users to enter their CVC when unmasking a Wallet instrument (credit card). */
+@NullMarked
 public class CardUnmaskPrompt
         implements EmptyTextWatcher,
                 OnClickListener,
                 ModalDialogProperties.Controller,
                 CompoundButton.OnCheckedChangeListener {
-    private static CardUnmaskObserverForTest sObserverForTest;
+    private static @Nullable CardUnmaskObserverForTest sObserverForTest;
 
     private final CardUnmaskPromptDelegate mDelegate;
-    private PropertyModel mDialogModel;
+    private @Nullable PropertyModel mDialogModel;
     private boolean mShouldRequestExpirationDate;
 
     private final View mMainView;
@@ -77,8 +81,8 @@
 
     private int mThisYear;
     private int mThisMonth;
-    private ModalDialogManager mModalDialogManager;
-    private Context mContext;
+    private @Nullable ModalDialogManager mModalDialogManager;
+    private @Nullable Context mContext;
 
     private boolean mDidFocusOnMonth;
     private boolean mDidFocusOnYear;
@@ -257,6 +261,7 @@
         mCardUnmaskInput.setOnEditorActionListener(
                 (v14, actionId, event) -> {
                     if (actionId == EditorInfo.IME_ACTION_DONE) {
+                        assumeNonNull(mDialogModel);
                         if (!mDialogModel.get(ModalDialogProperties.POSITIVE_BUTTON_DISABLED)) {
                             onClick(mDialogModel, ModalDialogProperties.ButtonType.POSITIVE);
                         } else if (sObserverForTest != null) {
@@ -324,6 +329,7 @@
         mContext = activity;
         mModalDialogManager = modalDialogManager;
 
+        assumeNonNull(mDialogModel);
         mModalDialogManager.showDialog(mDialogModel, ModalDialogManager.ModalDialogType.APP);
 
         showExpirationDateInputsInputs();
@@ -350,6 +356,7 @@
     }
 
     public void dismiss(@DialogDismissalCause int dismissalCause) {
+        assumeNonNull(mModalDialogManager);
         mModalDialogManager.dismissDialog(mDialogModel, dismissalCause);
     }
 
@@ -405,6 +412,8 @@
      * is wrong. Finally checks whether the focus should move to the next field.
      */
     private void validate() {
+        assumeNonNull(mContext);
+        assumeNonNull(mDialogModel);
         @ErrorType int errorType = getExpirationAndCvcErrorType();
         mDialogModel.set(
                 ModalDialogProperties.POSITIVE_BUTTON_DISABLED, errorType != ErrorType.NONE);
@@ -449,6 +458,7 @@
     }
 
     private void setInitialFocus() {
+        assumeNonNull(mContext);
         InputMethodManager imm =
                 (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
         View view = mShouldRequestExpirationDate ? mMonthInput : mCardUnmaskInput;
@@ -542,6 +552,7 @@
      *        obscures them.
      */
     private void setInputsEnabled(boolean enabled) {
+        assumeNonNull(mDialogModel);
         mCardUnmaskInput.setEnabled(enabled);
         mMonthInput.setEnabled(enabled);
         mYearInput.setEnabled(enabled);
@@ -577,6 +588,7 @@
     private void clearInputError() {
         AutofillUiUtils.clearInputError(mErrorMessage);
         // Remove the highlight on the input fields.
+        assumeNonNull(mContext);
         AutofillUiUtils.updateColorForInputs(
                 ErrorType.NONE, mContext, mMonthInput, mYearInput, mCardUnmaskInput);
     }
@@ -612,6 +624,7 @@
                     mUseScreenlockCheckbox.isChecked(),
                     mUseScreenlockCheckbox.getVisibility() == View.VISIBLE);
         } else if (buttonType == ModalDialogProperties.ButtonType.NEGATIVE) {
+            assumeNonNull(mModalDialogManager);
             mModalDialogManager.dismissDialog(model, DialogDismissalCause.NEGATIVE_BUTTON_CLICKED);
         }
     }
@@ -634,7 +647,7 @@
         ResettersForTesting.register(() -> sObserverForTest = oldValue);
     }
 
-    public PropertyModel getDialogForTest() {
+    public @Nullable PropertyModel getDialogForTest() {
         return mDialogModel;
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/CreditCardScanner.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/CreditCardScanner.java
index 73e55f5..fb14c49 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/CreditCardScanner.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/CreditCardScanner.java
@@ -4,6 +4,8 @@
 
 package org.chromium.chrome.browser.autofill;
 
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
 import org.chromium.ui.base.IntentRequestTracker;
 
 /**
@@ -11,12 +13,13 @@
  * cards. The default implementation cannot scan cards. An implementing subclass must provide a
  * factory that builds its instances.
  */
+@NullMarked
 public class CreditCardScanner {
     /**
      * Can be used to build subclasses of the scanner without the user of the class knowing about
      * the subclass name.
      */
-    private static Factory sFactory;
+    private static @Nullable Factory sFactory;
 
     /** The delegate to notify of scanning result. */
     protected final Delegate mDelegate;
@@ -91,7 +94,7 @@
      *
      * @param tracker The intent request tracker used to show the scanner intent.
      */
-    public void scan(IntentRequestTracker tracker) {
+    public void scan(@Nullable IntentRequestTracker tracker) {
         mDelegate.onScanCancelled();
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/SaveUpdateAddressProfilePromptController.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/SaveUpdateAddressProfilePromptController.java
index f771960b..137d00e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/SaveUpdateAddressProfilePromptController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/SaveUpdateAddressProfilePromptController.java
@@ -10,10 +10,12 @@
 import org.jni_zero.JNINamespace;
 import org.jni_zero.NativeMethods;
 
+import org.chromium.build.annotations.NullMarked;
 import org.chromium.components.autofill.AutofillProfile;
 
 /** JNI wrapper for C++ SaveUpdateAddressProfilePromptController. */
 @JNINamespace("autofill")
+@NullMarked
 final class SaveUpdateAddressProfilePromptController {
     private long mNativeSaveUpdateAddressProfilePromptController;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/vcn/AutofillVcnEnrollBottomSheetContent.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/vcn/AutofillVcnEnrollBottomSheetContent.java
index 78c565f..b14a0cd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/vcn/AutofillVcnEnrollBottomSheetContent.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/vcn/AutofillVcnEnrollBottomSheetContent.java
@@ -8,14 +8,15 @@
 import android.view.View;
 import android.widget.ScrollView;
 
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 import androidx.annotation.StringRes;
 
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
 import org.chromium.chrome.R;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
 
 /** The contents for the virtual card number (VCN) enrollment bottom sheet. */
+@NullMarked
 /*package*/ class AutofillVcnEnrollBottomSheetContent implements BottomSheetContent {
     private final View mContentView;
     private final ScrollView mScrollView;
@@ -41,9 +42,8 @@
         return mContentView;
     }
 
-    @Nullable
     @Override
-    public View getToolbarView() {
+    public @Nullable View getToolbarView() {
         return null;
     }
 
@@ -73,7 +73,7 @@
     }
 
     @Override
-    public @NonNull String getSheetContentDescription(Context context) {
+    public String getSheetContentDescription(Context context) {
         return context.getString(R.string.autofill_virtual_card_enroll_content_description);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/vcn/AutofillVcnEnrollBottomSheetLifecycle.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/vcn/AutofillVcnEnrollBottomSheetLifecycle.java
index 382b6e8..dfbcae2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/vcn/AutofillVcnEnrollBottomSheetLifecycle.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/vcn/AutofillVcnEnrollBottomSheetLifecycle.java
@@ -4,10 +4,12 @@
 
 package org.chromium.chrome.browser.autofill.vcn;
 
-import androidx.annotation.Nullable;
+import static org.chromium.build.NullUtil.assumeNonNull;
 
 import org.chromium.base.Callback;
 import org.chromium.base.supplier.ObservableSupplier;
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
 import org.chromium.chrome.browser.layouts.LayoutStateProvider;
 import org.chromium.chrome.browser.layouts.LayoutStateProvider.LayoutStateObserver;
 import org.chromium.chrome.browser.layouts.LayoutType;
@@ -23,15 +25,16 @@
  * a tab or layout changes (e.g., going into the "tab overview"), so the bottom sheet can be
  * dismissed. Ignores page navigations.
  */
+@NullMarked
 /*package*/ class AutofillVcnEnrollBottomSheetLifecycle
         implements Callback<TabModelSelector>, TabModelObserver, LayoutStateObserver {
     private final Callback<TabModel> mCurrentTabModelObserver = this::onTabModelSelected;
     private final LayoutStateProvider mLayoutStateProvider;
     private final ObservableSupplier<TabModelSelector> mTabModelSelectorSupplier;
-    private Runnable mOnEndOfLifecycle;
+    private @Nullable Runnable mOnEndOfLifecycle;
     private boolean mHasBegun;
-    private TabModelSelector mTabModelSelector;
-    private TabModel mTabModel;
+    private @Nullable TabModelSelector mTabModelSelector;
+    private @Nullable TabModel mTabModel;
 
     /**
      * Constructs the lifecycle for the virtual card number (VCN) enrollment bottom sheet.
@@ -81,8 +84,9 @@
         mLayoutStateProvider.removeObserver(this);
         mTabModelSelectorSupplier.removeObserver(this);
 
-        if (mTabModelSelector != null)
+        if (mTabModelSelector != null) {
             mTabModelSelector.getCurrentTabModelSupplier().removeObserver(mCurrentTabModelObserver);
+        }
         if (mTabModel != null) mTabModel.removeObserver(this);
 
         mHasBegun = false;
@@ -90,6 +94,7 @@
 
     private void endLifecycleAndNotifyCaller() {
         end();
+        assumeNonNull(mOnEndOfLifecycle);
         mOnEndOfLifecycle.run();
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/vcn/AutofillVcnEnrollBottomSheetProperties.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/vcn/AutofillVcnEnrollBottomSheetProperties.java
index 26afe0d..6907d3b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/vcn/AutofillVcnEnrollBottomSheetProperties.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/vcn/AutofillVcnEnrollBottomSheetProperties.java
@@ -9,6 +9,8 @@
 
 import androidx.annotation.DrawableRes;
 
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.components.autofill.AutofillFeatures;
 import org.chromium.components.autofill.VirtualCardEnrollmentLinkType;
@@ -22,6 +24,7 @@
 import java.util.function.Function;
 
 /** The model of the autofill virtual card number (VCN) enrollment bottom sheet UI. */
+@NullMarked
 /*package*/ abstract class AutofillVcnEnrollBottomSheetProperties {
     /** Opens links. */
     static interface LinkOpener {
@@ -106,13 +109,13 @@
     /** Issuer icon. */
     static class IssuerIcon {
         /** The bitmap for the issuer icon. */
-        final Bitmap mBitmap;
+        final @Nullable Bitmap mBitmap;
 
         /** The resource id for the issuer icon. */
         final @DrawableRes int mIconResource;
 
         /** The url for an issuer icon. */
-        final GURL mIconUrl;
+        final @Nullable GURL mIconUrl;
 
         /** The width of the issuer icon. */
         final int mWidth;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/vcn/AutofillVcnEnrollBottomSheetView.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/vcn/AutofillVcnEnrollBottomSheetView.java
index 6770d63..0912a833 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/vcn/AutofillVcnEnrollBottomSheetView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/vcn/AutofillVcnEnrollBottomSheetView.java
@@ -13,11 +13,13 @@
 import android.widget.ScrollView;
 import android.widget.TextView;
 
+import org.chromium.build.annotations.NullMarked;
 import org.chromium.chrome.R;
 import org.chromium.ui.base.LocalizationUtils;
 import org.chromium.ui.widget.LoadingView;
 
 /** The view of the autofill virtual card enrollment bottom sheet UI. */
+@NullMarked
 /*package*/ class AutofillVcnEnrollBottomSheetView {
     /** The view that contains all other views. */
     final ViewGroup mContentView;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinator.java
index 587f279..f17be45 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinator.java
@@ -76,7 +76,6 @@
  * responsible for creating a list of menu items, setting up the menu and displaying the menu.
  */
 public class TabGroupContextMenuCoordinator extends TabGroupOverflowMenuCoordinator {
-    private static final String MENU_USER_ACTION_PREFIX = "MobileToolbarTabGroupMenu.";
     private View mContentView;
     private EditText mGroupTitleEditText;
     private ColorPickerCoordinator mColorPickerCoordinator;
@@ -189,7 +188,7 @@
 
             if (menuId == org.chromium.chrome.R.id.ungroup_tab) {
                 TabUiUtils.ungroupTabGroup(tabGroupModelFilter, tabGroupId);
-                recordUserAction("Ungroup");
+                RecordUserAction.record("MobileToolbarTabGroupMenu.Ungroup");
             } else if (menuId == org.chromium.chrome.R.id.close_tab_group) {
                 boolean allowUndo = TabClosureParamsUtils.shouldAllowUndo(listViewTouchTracker);
                 TabUiUtils.closeTabGroup(
@@ -198,7 +197,7 @@
                         allowUndo,
                         /* hideTabGroups= */ true,
                         /* didCloseCallback= */ null);
-                recordUserAction("CloseGroup");
+                RecordUserAction.record("MobileToolbarTabGroupMenu.CloseGroup");
             } else if (menuId == org.chromium.chrome.R.id.delete_tab_group) {
                 boolean allowUndo = TabClosureParamsUtils.shouldAllowUndo(listViewTouchTracker);
                 TabUiUtils.closeTabGroup(
@@ -207,14 +206,14 @@
                         allowUndo,
                         /* hideTabGroups= */ false,
                         /* didCloseCallback= */ null);
-                recordUserAction("DeleteGroup");
+                RecordUserAction.record("MobileToolbarTabGroupMenu.DeleteGroup");
             } else if (menuId == org.chromium.chrome.R.id.open_new_tab_in_group) {
                 TabGroupUtils.openUrlInGroup(
                         tabGroupModelFilter,
                         UrlConstants.NTP_URL,
                         tabId,
                         TabLaunchType.FROM_TAB_GROUP_UI);
-                recordUserAction("NewTabInGroup");
+                RecordUserAction.record("MobileToolbarTabGroupMenu.NewTabInGroup");
             } else if (menuId == R.id.move_to_other_window_menu_id) {
                 TabModel tabModel = tabModelSupplier.get();
                 TabGroupMetadata tabGroupMetadata =
@@ -224,6 +223,11 @@
                                 tabModel.getTabAt(tabModel.index()).getId(),
                                 TabShareUtils.isCollaborationIdValid(collaborationId));
                 multiInstanceManager.moveTabGroupToOtherWindow(tabGroupMetadata);
+                if (MultiWindowUtils.getInstanceCount() == 1) {
+                    RecordUserAction.record("MobileToolbarTabGroupMenu.MoveGroupToNewWindow");
+                } else {
+                    RecordUserAction.record("MobileToolbarTabGroupMenu.MoveGroupToAnotherWindow");
+                }
             } else if (menuId == org.chromium.chrome.R.id.share_group) {
                 // Create the group share flow and display the share bottom sheet.
                 dataSharingTabManager.createOrManageFlow(
@@ -231,29 +235,29 @@
                         CollaborationServiceShareOrManageEntryPoint
                                 .ANDROID_TAB_GROUP_CONTEXT_MENU_SHARE,
                         /* createGroupFinishedCallback= */ null);
-                recordUserAction("ShareGroup");
+                RecordUserAction.record("MobileToolbarTabGroupMenu.ShareGroup");
             } else if (menuId == R.id.manage_sharing) {
                 dataSharingTabManager.createOrManageFlow(
                         eitherId,
                         CollaborationServiceShareOrManageEntryPoint
                                 .ANDROID_TAB_GROUP_CONTEXT_MENU_MANAGE,
                         /* createGroupFinishedCallback= */ null);
-                recordUserAction("ManageSharing");
+                RecordUserAction.record("MobileToolbarTabGroupMenu.ManageSharing");
             } else if (menuId == R.id.recent_activity) {
                 dataSharingTabManager.showRecentActivity(activity, collaborationId);
-                recordUserAction("RecentActivity");
+                RecordUserAction.record("MobileToolbarTabGroupMenu.RecentActivity");
             } else if (menuId == R.id.delete_shared_group) {
                 dataSharingTabManager.leaveOrDeleteFlow(
                         eitherId,
                         CollaborationServiceLeaveOrDeleteEntryPoint
                                 .ANDROID_TAB_GROUP_CONTEXT_MENU_DELETE);
-                recordUserAction("DeleteSharedGroup");
+                RecordUserAction.record("MobileToolbarTabGroupMenu.DeleteSharedGroup");
             } else if (menuId == R.id.leave_group) {
                 dataSharingTabManager.leaveOrDeleteFlow(
                         eitherId,
                         CollaborationServiceLeaveOrDeleteEntryPoint
                                 .ANDROID_TAB_GROUP_CONTEXT_MENU_LEAVE);
-                recordUserAction("LeaveSharedGroup");
+                RecordUserAction.record("MobileToolbarTabGroupMenu.LeaveSharedGroup");
             }
         };
     }
@@ -276,7 +280,7 @@
                 /* animStyle= */ ResourcesCompat.ID_NULL,
                 HorizontalOrientation.LAYOUT_DIRECTION,
                 mWindowAndroid.getActivity().get());
-        recordUserAction("Shown");
+        RecordUserAction.record("MobileToolbarTabGroupMenu.Shown");
     }
 
     @Override
@@ -456,7 +460,7 @@
     private void updateTabGroupColor() {
         @TabGroupColorId int newColor = mColorPickerCoordinator.getSelectedColorSupplier().get();
         if (TabUiUtils.updateTabGroupColor(mTabGroupModelFilter, mGroupRootId, newColor)) {
-            recordUserAction("ColorChanged");
+            RecordUserAction.record("MobileToolbarTabGroupMenu.ColorChanged");
         }
     }
 
@@ -471,10 +475,10 @@
             return;
         } else if (TextUtils.isEmpty(newTitle) || newTitle.equals(getDefaultTitle())) {
             mTabGroupModelFilter.deleteTabGroupTitle(mGroupRootId);
-            recordUserAction("TitleReset");
+            RecordUserAction.record("MobileToolbarTabGroupMenu.TitleReset");
             setExistingOrDefaultTitle(getDefaultTitle());
         } else if (TabUiUtils.updateTabGroupTitle(mTabGroupModelFilter, mGroupRootId, newTitle)) {
-            recordUserAction("TitleChanged");
+            RecordUserAction.record("MobileToolbarTabGroupMenu.TitleChanged");
         }
         mCurrentModifiedTitle = null;
     }
@@ -566,10 +570,6 @@
         }
     }
 
-    private static void recordUserAction(String action) {
-        RecordUserAction.record(MENU_USER_ACTION_PREFIX + action);
-    }
-
     EditText getGroupTitleEditTextForTesting() {
         return mGroupTitleEditText;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuCoordinator.java
index f3d191d..52e0ce6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuCoordinator.java
@@ -351,6 +351,10 @@
                 ListItemType.CONTEXT_MENU_ITEM_WITH_CHECKBOX,
                 new LayoutViewBuilder<>(R.layout.checkbox_layout),
                 ContextMenuItemWithCheckboxViewBinder::bind);
+        adapter.registerType(
+                ListItemType.CONTEXT_MENU_ITEM_WITH_RADIO_BUTTON,
+                new LayoutViewBuilder<>(R.layout.radio_button_layout_element),
+                ContextMenuItemWithRadioButtonViewBinder::bind);
 
         mListView.setOnItemClickListener(
                 (p, v, pos, id) -> {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuItemWithRadioButtonViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuItemWithRadioButtonViewBinder.java
new file mode 100644
index 0000000..7ac145f
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuItemWithRadioButtonViewBinder.java
@@ -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.
+
+package org.chromium.chrome.browser.contextmenu;
+
+import static org.chromium.ui.listmenu.ContextMenuRadioItemProperties.ENABLED;
+import static org.chromium.ui.listmenu.ContextMenuRadioItemProperties.SELECTED;
+import static org.chromium.ui.listmenu.ContextMenuRadioItemProperties.TITLE;
+
+import android.view.View;
+import android.widget.RadioButton;
+
+import org.chromium.ui.listmenu.ContextMenuRadioItemProperties;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
+
+/**
+ * View binder for a context menu item with checkbox (of type {@code
+ * ListItemType.CONTEXT_MENU_ITEM_WITH_RADIO_BUTTON}, with property keys {@link
+ * ContextMenuRadioItemProperties}).
+ */
+class ContextMenuItemWithRadioButtonViewBinder {
+    public static void bind(PropertyModel model, View view, PropertyKey propertyKey) {
+        RadioButton radioButton = (RadioButton) view.getRootView();
+        if (propertyKey == TITLE) {
+            radioButton.setText(model.get(TITLE));
+        } else if (propertyKey == ENABLED) {
+            radioButton.setEnabled(model.get(ENABLED));
+        } else if (propertyKey == SELECTED) {
+            radioButton.setChecked(model.get(SELECTED));
+        }
+        // MENU_ITEM_ID and ON_CLICK are used by the ContextMenuCoordinator.
+        // Note that because this will be an item inside of a list, this view itself does not need
+        // to implement the on-click behavior (it's handled by ContextMenuCoordinator).
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabAppMenuPropertiesDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabAppMenuPropertiesDelegate.java
index 81988e9..ec555a5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabAppMenuPropertiesDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabAppMenuPropertiesDelegate.java
@@ -12,8 +12,8 @@
 import android.view.View;
 import android.widget.TextView;
 
+import androidx.annotation.IdRes;
 import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
 import androidx.appcompat.content.res.AppCompatResources;
 import androidx.core.graphics.drawable.DrawableCompat;
 
@@ -39,7 +39,6 @@
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.toolbar.ToolbarManager;
 import org.chromium.chrome.browser.ui.appmenu.AppMenuHandler;
-import org.chromium.chrome.browser.ui.appmenu.AppMenuPropertiesDelegate;
 import org.chromium.chrome.browser.ui.appmenu.CustomViewBinder;
 import org.chromium.components.embedder_support.util.UrlConstants;
 import org.chromium.components.webapps.WebappsUtils;
@@ -67,7 +66,6 @@
     private final boolean mIsStartIconMenu;
 
     private final List<String> mMenuEntries;
-    private final Map<String, Integer> mTitleToItemIdMap = new HashMap<String, Integer>();
     private final Map<Integer, Integer> mItemIdToIndexMap = new HashMap<Integer, Integer>();
     private final Supplier<ContextualPageActionController> mContextualPageActionControllerSupplier;
 
@@ -267,8 +265,22 @@
 
             // --- App Specific Items / Divider ---
             for (int i = 0; i < mMenuEntries.size(); i++) {
-                MenuItem item = menu.add(0, i, 1, mMenuEntries.get(i));
-                mTitleToItemIdMap.put(mMenuEntries.get(i), item.getItemId());
+                @IdRes
+                int id =
+                        switch (i) {
+                            case 0 -> R.id.custom_tabs_app_menu_item_id_0;
+                            case 1 -> R.id.custom_tabs_app_menu_item_id_1;
+                            case 2 -> R.id.custom_tabs_app_menu_item_id_2;
+                            case 3 -> R.id.custom_tabs_app_menu_item_id_3;
+                            case 4 -> R.id.custom_tabs_app_menu_item_id_4;
+                            case 5 -> R.id.custom_tabs_app_menu_item_id_5;
+                            case 6 -> R.id.custom_tabs_app_menu_item_id_6;
+                            default -> {
+                                assert false : "Only 7 custom menu items are currently allowed.";
+                                yield 0;
+                            }
+                        };
+                MenuItem item = menu.add(0, id, 1, mMenuEntries.get(i));
                 mItemIdToIndexMap.put(item.getItemId(), i);
             }
 
@@ -396,19 +408,6 @@
         }
     }
 
-    /**
-     * Get the menu item's id object associated with the given title. If multiple menu items have
-     * the same title, a random one will be returned. If the menu item is not found, return -1. This
-     * method is for testing purpose _only_.
-     */
-    @VisibleForTesting
-    int getItemIdForTitle(String title) {
-        if (mTitleToItemIdMap.containsKey(title)) {
-            return mTitleToItemIdMap.get(title).intValue();
-        }
-        return AppMenuPropertiesDelegate.INVALID_ITEM_ID;
-    }
-
     @Override
     public boolean isMenuIconAtStart() {
         return mIsStartIconMenu;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/search/SearchBoxViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/search/SearchBoxViewBinder.java
index 15d4b31..1e6e20e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/search/SearchBoxViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/search/SearchBoxViewBinder.java
@@ -15,6 +15,7 @@
 import androidx.core.widget.ImageViewCompat;
 
 import org.chromium.chrome.R;
+import org.chromium.components.omnibox.OmniboxFeatures;
 import org.chromium.ui.base.ViewUtils;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
@@ -37,8 +38,13 @@
         } else if (SearchBoxProperties.ALPHA == propertyKey) {
             searchBoxContainer.setAlpha(model.get(SearchBoxProperties.ALPHA));
             // Disable the search box contents if it is the process of being animated away.
-            ViewUtils.setEnabledRecursive(
-                    searchBoxContainer, searchBoxContainer.getAlpha() == 1.0f);
+            // If the DSE icon is always visible on the NTP, we need to leave the container enabled
+            // (even though it will have alpha 0) because it, not the omnibox, will handle click
+            // events until the omnibox is "pinned" to the top.
+            if (!OmniboxFeatures.sOmniboxMobileParityUpdate.isEnabled()) {
+                ViewUtils.setEnabledRecursive(
+                        searchBoxContainer, searchBoxContainer.getAlpha() == 1.0f);
+            }
         } else if (SearchBoxProperties.VOICE_SEARCH_COLOR_STATE_LIST == propertyKey) {
             ImageViewCompat.setImageTintList(
                     voiceSearchButton,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java
index 33ab778..c507706 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java
@@ -650,9 +650,7 @@
                 isIncognito
                         ? R.string.hub_search_empty_hint_incognito
                         : R.string.hub_search_empty_hint;
-        mLocationBarCoordinator
-                .getUrlBarCoordinator()
-                .setUrlBarHintText(getResources().getString(hintTextRes));
+        mLocationBarCoordinator.getUrlBarCoordinator().setUrlBarHintText(hintTextRes);
     }
 
     /* package */ boolean loadUrl(@NonNull OmniboxLoadUrlParams params, boolean isIncognito) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelImpl.java
index 0a922a6..f59c318 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelImpl.java
@@ -597,11 +597,15 @@
         }
     }
 
-    private void closeAllTabs(boolean uponExit, @Nullable Runnable undoRunnable) {
+    private void closeAllTabs(
+            boolean uponExit, boolean allowUndo, @Nullable Runnable undoRunnable) {
         for (TabModelObserver obs : mObservers) obs.willCloseAllTabs(isIncognito());
 
-        // Force close immediately upon exit or if Chrome needs to close with a zero-state.
-        if (uponExit || HomepageManager.getInstance().shouldCloseAppWithZeroTabs()) {
+        // Force close immediately if:
+        // 1. the tabs are to be closed upon app exit,
+        // 2. the operation doesn't allow undo, or
+        // 3. Chrome needs to close with a zero-state.
+        if (uponExit || !allowUndo || HomepageManager.getInstance().shouldCloseAppWithZeroTabs()) {
             commitAllTabClosures();
 
             for (int i = 0; i < getCount(); i++) getTabAt(i).setClosing(true);
@@ -690,7 +694,10 @@
                         tabClosureParams.undoRunnable);
                 return true;
             case TabCloseType.ALL:
-                closeAllTabs(tabClosureParams.uponExit, tabClosureParams.undoRunnable);
+                closeAllTabs(
+                        tabClosureParams.uponExit,
+                        tabClosureParams.allowUndo,
+                        tabClosureParams.undoRunnable);
                 return true;
             default:
                 assert false : "Not reached.";
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 bff52ab..60e639d 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
@@ -17,7 +17,9 @@
 import static org.junit.Assert.assertFalse;
 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.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -52,6 +54,7 @@
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.TabArchiveSettings;
 import org.chromium.chrome.browser.tab.TabArchiverImpl;
+import org.chromium.chrome.browser.tab_group_sync.TabGroupSyncServiceFactory;
 import org.chromium.chrome.browser.tabmodel.TabCreator;
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorBase;
@@ -62,6 +65,8 @@
 import org.chromium.chrome.test.R;
 import org.chromium.chrome.test.transit.ChromeTransitTestRules;
 import org.chromium.chrome.test.transit.FreshCtaTransitTestRule;
+import org.chromium.components.tab_group_sync.SavedTabGroup;
+import org.chromium.components.tab_group_sync.TabGroupSyncService;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -80,6 +85,7 @@
 public class ArchivedTabModelOrchestratorTest {
     private static final String TEST_PATH = "/chrome/test/data/android/about.html";
     private static final String TEST_PATH_2 = "/chrome/test/data/android/google.html";
+    private static final String SYNC_GROUP_ID1 = "test_sync_group_id1";
 
     private static class FakeDeferredStartupHandler extends DeferredStartupHandler {
         private final List<Runnable> mTasks = new ArrayList<>();
@@ -109,6 +115,7 @@
     @Mock private TabPersistentStore mArchivedTabPersistentStore;
     @Mock private TabPersistentStore mNormalTabPersistentStore;
     @Mock private TabModelSelectorBase mTabModelSelector;
+    @Mock private TabGroupSyncService mTabGroupSyncService;
 
     private Profile mProfile;
     private FakeDeferredStartupHandler mDeferredStartupHandler;
@@ -121,6 +128,9 @@
 
     @Before
     public void setUp() throws Exception {
+        TabGroupSyncServiceFactory.setForTesting(mTabGroupSyncService);
+        when(mTabGroupSyncService.getAllGroupIds()).thenReturn(new String[] {});
+
         mDeferredStartupHandler = new FakeDeferredStartupHandler();
         DeferredStartupHandler.setInstanceForTests(mDeferredStartupHandler);
         mActivityTestRule.startOnBlankPage();
@@ -290,6 +300,7 @@
     @Test
     @MediumTest
     public void testRescueTabs_FeatureFlag() {
+        setupSavedTabGroup();
         finishLoading();
         mActivityTestRule.loadUrlInNewTab(
                 mActivityTestRule.getTestServer().getURL(TEST_PATH), /* incognito= */ false);
@@ -321,6 +332,7 @@
                     mOrchestrator.setTabPersistentStoreForTesting(mArchivedTabPersistentStore);
 
                     mOrchestrator.resetRescueArchivedTabsForTesting();
+                    mOrchestrator.resetRescueArchivedTabGroupsForTesting();
                     mOrchestrator.rescueArchivedTabs(
                             (TabbedModeTabModelOrchestrator)
                                     mActivityTestRule
@@ -331,6 +343,7 @@
 
         CriteriaHelper.pollUiThread(() -> 2 == mRegularTabModel.getCount());
         assertEquals(0, mArchivedTabModel.getCount());
+        verify(mTabGroupSyncService, times(2)).updateArchivalStatus(eq(SYNC_GROUP_ID1), eq(false));
         verify(mArchivedTabPersistentStore).pauseSaveTabList();
         verify(mArchivedTabPersistentStore).resumeSaveTabList();
         verify(mNormalTabPersistentStore).pauseSaveTabList();
@@ -340,6 +353,7 @@
     @Test
     @MediumTest
     public void testRescueTabs_ArchiveDisabled() {
+        setupSavedTabGroup();
         finishLoading();
         mActivityTestRule.loadUrlInNewTab(
                 mActivityTestRule.getTestServer().getURL(TEST_PATH), /* incognito= */ false);
@@ -362,16 +376,19 @@
         runOnUiThreadBlocking(
                 () -> {
                     mOrchestrator.resetRescueArchivedTabsForTesting();
+                    mOrchestrator.resetRescueArchivedTabGroupsForTesting();
                     mTabArchiveSettings.setArchiveEnabled(false);
                 });
 
         CriteriaHelper.pollUiThread(() -> mRegularTabModel.getCount() == 2);
         assertEquals(0, mArchivedTabModel.getCount());
+        verify(mTabGroupSyncService, times(2)).updateArchivalStatus(eq(SYNC_GROUP_ID1), eq(false));
     }
 
     @Test
     @MediumTest
     public void testRescueTabs_ArchiveDisabledWhileNoOrchestatorsRegistered() {
+        setupSavedTabGroup();
         finishLoading();
         mActivityTestRule.loadUrlInNewTab(
                 mActivityTestRule.getTestServer().getURL(TEST_PATH), /* incognito= */ false);
@@ -397,6 +414,7 @@
                 () -> {
                     mOrchestrator.unregisterTabModelOrchestrator(mTabbedModeOrchestrator);
                     mOrchestrator.resetRescueArchivedTabsForTesting();
+                    mOrchestrator.resetRescueArchivedTabGroupsForTesting();
                     mTabArchiveSettings.setArchiveEnabled(false);
                 });
         CriteriaHelper.pollUiThread(() -> mArchivedTabModel.getCount() == 1);
@@ -408,6 +426,7 @@
                 });
         CriteriaHelper.pollUiThread(() -> mRegularTabModel.getCount() == 2);
         assertEquals(0, mArchivedTabModel.getCount());
+        verify(mTabGroupSyncService, times(2)).updateArchivalStatus(eq(SYNC_GROUP_ID1), eq(false));
     }
 
     @Test
@@ -543,4 +562,13 @@
         onView(allOf(withId(R.id.line_2), withText(containsString(TEST_PATH))))
                 .check(doesNotExist());
     }
+
+    private void setupSavedTabGroup() {
+        SavedTabGroup savedTabGroup = new SavedTabGroup();
+        savedTabGroup.syncId = SYNC_GROUP_ID1;
+        savedTabGroup.archivalTimeMs = System.currentTimeMillis();
+
+        when(mTabGroupSyncService.getAllGroupIds()).thenReturn(new String[] {SYNC_GROUP_ID1});
+        when(mTabGroupSyncService.getGroup(SYNC_GROUP_ID1)).thenReturn(savedTabGroup);
+    }
 }
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 1f01dbb..74542bbd 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
@@ -134,7 +134,7 @@
         runOnUiThreadBlocking(
                 () -> {
                     CloseAllTabsHelper.closeAllTabsHidingTabGroups(
-                            cta.getTabModelSelectorSupplier().get());
+                            cta.getTabModelSelectorSupplier().get(), /* allowUndo= */ true);
                 });
         CriteriaHelper.pollUiThread(() -> 0 == mArchivedTabModel.getCount());
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityAppMenuTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityAppMenuTest.java
index 63ff656..d4acc5f 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityAppMenuTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityAppMenuTest.java
@@ -24,6 +24,8 @@
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
+import android.text.TextUtils;
+import android.view.View;
 
 import androidx.browser.customtabs.CustomTabsCallback;
 import androidx.browser.customtabs.CustomTabsIntent;
@@ -74,12 +76,12 @@
 import org.chromium.chrome.browser.ui.appmenu.AppMenuCoordinator;
 import org.chromium.chrome.browser.ui.appmenu.AppMenuHandler;
 import org.chromium.chrome.browser.ui.appmenu.AppMenuItemProperties;
-import org.chromium.chrome.browser.ui.appmenu.AppMenuPropertiesDelegate;
 import org.chromium.chrome.browser.ui.appmenu.AppMenuTestSupport;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.R;
 import org.chromium.chrome.test.util.ChromeTabUtils;
 import org.chromium.components.webapps.WebappsUtils;
+import org.chromium.ui.modelutil.MVCListAdapter;
 import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.test.util.DeviceRestriction;
@@ -562,13 +564,22 @@
         PostTask.runOrPostTask(
                 TaskTraits.UI_DEFAULT,
                 () -> {
-                    int itemId =
+                    ModelList menuModelList =
                             ((CustomTabAppMenuPropertiesDelegate)
                                             AppMenuTestSupport.getAppMenuPropertiesDelegate(
                                                     mCustomTabActivityTestRule
                                                             .getAppMenuCoordinator()))
-                                    .getItemIdForTitle(TEST_MENU_TITLE);
-                    Assert.assertNotEquals(AppMenuPropertiesDelegate.INVALID_ITEM_ID, itemId);
+                                    .getModelList();
+                    int itemId = View.NO_ID;
+                    for (MVCListAdapter.ListItem menuItem : menuModelList) {
+                        if (menuItem.model.containsKey(AppMenuItemProperties.TITLE)
+                                && TextUtils.equals(
+                                        TEST_MENU_TITLE,
+                                        menuItem.model.get(AppMenuItemProperties.TITLE))) {
+                            itemId = menuItem.model.get(AppMenuItemProperties.MENU_ITEM_ID);
+                        }
+                    }
+                    Assert.assertNotEquals(View.NO_ID, itemId);
                     AppMenuTestSupport.onOptionsItemSelected(
                             mCustomTabActivityTestRule.getAppMenuCoordinator(), itemId);
                 });
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/incognito/IncognitoNotificationServiceTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/incognito/IncognitoNotificationServiceTest.java
index 47f3a71..bd4f52ed 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/incognito/IncognitoNotificationServiceTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/incognito/IncognitoNotificationServiceTest.java
@@ -97,13 +97,20 @@
                 });
 
         boolean isIncognitoNotificationDisplayed = false;
-        for (StatusBarNotificationProxy statusBarNotification : getActiveNotifications()) {
-            if (IncognitoNotificationManager.INCOGNITO_TABS_OPEN_TAG.equals(
-                    statusBarNotification.getTag())) {
-                isIncognitoNotificationDisplayed = true;
-            }
-        }
-        assertTrue(isIncognitoNotificationDisplayed);
+        CriteriaHelper.pollInstrumentationThread(
+                () -> {
+                    List<? extends StatusBarNotificationProxy> activeNotifications =
+                            getActiveNotifications();
+                    boolean found = false;
+                    for (StatusBarNotificationProxy notification : activeNotifications) {
+                        if (IncognitoNotificationManager.INCOGNITO_TABS_OPEN_TAG.equals(
+                                notification.getTag())) {
+                            found = true;
+                            break;
+                        }
+                    }
+                    Criteria.checkThat(found, Matchers.is(true));
+                });
     }
 
     @Test
@@ -224,7 +231,6 @@
     @Test
     @MediumTest
     @Feature("Incognito")
-    @DisabledTest(message = "https://crbug.com/418782004")
     public void testCloseAllIncognitoNotificationIsDisplayed() {
         launchIncognitoTabAndEnsureNotificationDisplayed();
     }
@@ -232,7 +238,6 @@
     @Test
     @MediumTest
     @Feature("Incognito")
-    @DisabledTest(message = "https://crbug.com/418782004")
     public void testCloseAllIncognitoNotificationForIncognitoCct_DoesNotCloseCct()
             throws PendingIntent.CanceledException {
         launchIncognitoTabAndEnsureNotificationDisplayed();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarTest.java
index 2f538d43..9bfad54 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarTest.java
@@ -531,20 +531,19 @@
 
         mActivityTestRule.loadUrl(UrlConstants.NTP_URL);
 
+        Mockito.reset(mVoiceRecognitionHandler);
         ViewUtils.waitForVisibleView(withId(R.id.voice_search_button));
 
         ThreadUtils.runOnUiThreadBlocking(
                 () -> {
-                    Mockito.reset(mVoiceRecognitionHandler);
                     doReturn(true).when(mVoiceRecognitionHandler).isVoiceSearchEnabled();
 
                     // Updating the fraction once should query voice search visibility.
                     mLocationBarMediator.setUrlFocusChangeFraction(.5f, .5f);
-                    Mockito.verify(mVoiceRecognitionHandler).isVoiceSearchEnabled();
 
                     // Further updates to the fraction shouldn't trigger a button visibility update.
                     mLocationBarMediator.setUrlFocusChangeFraction(.6f, .6f);
-                    Mockito.verify(mVoiceRecognitionHandler, Mockito.times(1))
+                    Mockito.verify(mVoiceRecognitionHandler, Mockito.atMost(1))
                             .isVoiceSearchEnabled();
                 });
     }
@@ -769,7 +768,11 @@
         startActivityNormally();
 
         mActivityTestRule.loadUrl(UrlConstants.NTP_URL);
-        onView(withId(R.id.location_bar_status_icon)).check(matches(not(isDisplayed())));
+        if (OmniboxFeatures.sOmniboxMobileParityUpdate.isEnabled()) {
+            onView(withId(R.id.location_bar_status_icon)).check(matches(isDisplayed()));
+        } else {
+            onView(withId(R.id.location_bar_status_icon)).check(matches(not(isDisplayed())));
+        }
     }
 
     @Test
@@ -780,7 +783,11 @@
         startActivityNormally();
 
         mActivityTestRule.loadUrl(UrlConstants.NTP_URL);
-        onView(withId(R.id.location_bar_status_icon)).check(matches(not(isDisplayed())));
+        if (OmniboxFeatures.sOmniboxMobileParityUpdate.isEnabled()) {
+            onView(withId(R.id.location_bar_status_icon)).check(matches(isDisplayed()));
+        } else {
+            onView(withId(R.id.location_bar_status_icon)).check(matches(not(isDisplayed())));
+        }
     }
 
     @Test
@@ -813,7 +820,11 @@
         startActivityNormally();
 
         mActivityTestRule.loadUrl(UrlConstants.NTP_URL);
-        onView(withId(R.id.location_bar_status_icon)).check(matches(not(isDisplayed())));
+        if (OmniboxFeatures.sOmniboxMobileParityUpdate.isEnabled()) {
+            onView(withId(R.id.location_bar_status_icon)).check(matches(isDisplayed()));
+        } else {
+            onView(withId(R.id.location_bar_status_icon)).check(matches(not(isDisplayed())));
+        }
 
         mActivityTestRule.loadUrl(UrlConstants.ABOUT_URL);
         onView(withId(R.id.location_bar_status_icon)).check(matches(isDisplayed()));
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/OmniboxTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/OmniboxTest.java
index 4710651..1f4c1505 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/OmniboxTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/OmniboxTest.java
@@ -114,7 +114,7 @@
                 mActivityTestRule
                         .getActivity()
                         .getResources()
-                        .getString(R.string.omnibox_empty_hint_with_dse_name, "Google"),
+                        .getString(R.string.omnibox_empty_hint),
                 urlBar.getHint().toString());
 
         // Type something in the omnibox.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/TabSwitcherDrawableRenderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/TabSwitcherDrawableRenderTest.java
index 1c315bb..f0af1bef 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/TabSwitcherDrawableRenderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/TabSwitcherDrawableRenderTest.java
@@ -46,7 +46,7 @@
     public final ChromeRenderTestRule mRenderTestRule =
             ChromeRenderTestRule.Builder.withPublicCorpus()
                     .setBugComponent(RenderTestRule.Component.UI_BROWSER_MOBILE_TAB_GROUPS)
-                    .setRevision(3)
+                    .setRevision(2)
                     .build();
 
     @ClassRule
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhoneTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhoneTest.java
index 348e7b9..652bce8 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhoneTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhoneTest.java
@@ -62,6 +62,7 @@
 import org.chromium.base.test.util.Criteria;
 import org.chromium.base.test.util.CriteriaHelper;
 import org.chromium.base.test.util.DisableIf;
+import org.chromium.base.test.util.Features.DisableFeatures;
 import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider.ControlsPosition;
@@ -683,6 +684,7 @@
 
     @Test
     @LargeTest
+    @DisableFeatures(OmniboxFeatureList.OMNIBOX_MOBILE_PARITY_UPDATE)
     public void testNtpAnimation_onGTSExit() {
         // Load NTP
         mActivityTestRule.loadUrl(UrlConstants.NTP_URL);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/test/smoke/ChromeBundleSmokeTest.java b/chrome/android/javatests/src/org/chromium/chrome/test/smoke/ChromeBundleSmokeTest.java
index b79b8e42..5faa286 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/test/smoke/ChromeBundleSmokeTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/test/smoke/ChromeBundleSmokeTest.java
@@ -21,7 +21,6 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.BaseJUnit4ClassRunner;
-import org.chromium.base.test.util.DisabledTest;
 import org.chromium.chrome.test.pagecontroller.rules.ChromeUiApplicationTestRule;
 import org.chromium.chrome.test.pagecontroller.rules.ChromeUiAutomatorTestRule;
 import org.chromium.chrome.test.pagecontroller.utils.IUi2Locator;
@@ -33,7 +32,8 @@
 /** Smoke Test for Chrome bundles. */
 @LargeTest
 @RunWith(BaseJUnit4ClassRunner.class)
-@DisabledTest(message = "crbug.com/372840602")
+// Do not disable this test unless you are confident you can re-enable it quickly.
+// This is the main test that prevents crash-on-launch bugs.
 public class ChromeBundleSmokeTest {
     private static final String TARGET_ACTIVITY =
             "org.chromium.chrome.browser.test_dummy.TestDummyActivity";
diff --git a/chrome/android/javatests/src/org/chromium/chrome/test/smoke/ChromeTabSwitcherTest.java b/chrome/android/javatests/src/org/chromium/chrome/test/smoke/ChromeTabSwitcherTest.java
index 9b87a87..870de0e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/test/smoke/ChromeTabSwitcherTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/test/smoke/ChromeTabSwitcherTest.java
@@ -23,7 +23,6 @@
 
 import org.chromium.base.Log;
 import org.chromium.base.test.BaseJUnit4ClassRunner;
-import org.chromium.base.test.util.DisabledTest;
 import org.chromium.chrome.R;
 import org.chromium.chrome.test.pagecontroller.rules.ChromeUiApplicationTestRule;
 import org.chromium.chrome.test.pagecontroller.rules.ChromeUiAutomatorTestRule;
@@ -36,6 +35,8 @@
 /** Basic Test for Chrome Android to switch Tabs. */
 @LargeTest
 @RunWith(BaseJUnit4ClassRunner.class)
+// Do not disable this test unless you are confident you can re-enable it quickly.
+// This is the main test that prevents crash-on-launch bugs.
 public class ChromeTabSwitcherTest {
     private static final String TAG = "SmokeTest";
     private static final String ACTIVITY_NAME = "com.google.android.apps.chrome.IntentDispatcher";
@@ -71,7 +72,8 @@
     }
 
     @Test
-    @DisabledTest(message = "crbug.com/372837954")
+    // Do not disable this test unless you are confident you can re-enable it quickly.
+    // This is the main test that prevents crash-on-launch bugs.
     public void testTabSwitcher() throws Exception {
         Context context = ApplicationProvider.getApplicationContext();
         String url = sEmbeddedTestServerRule.getServer().getURL(TEST_PAGE);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/searchwidget/SearchActivityUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/searchwidget/SearchActivityUnitTest.java
index 5faa6fc..2611f74 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/searchwidget/SearchActivityUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/searchwidget/SearchActivityUnitTest.java
@@ -888,9 +888,7 @@
         ShadowProfileManager.setProfile(mProfile);
         mActivity.finishNativeInitialization();
 
-        String expectedText = mActivity.getResources().getString(R.string.hub_search_empty_hint);
-
-        verify(urlBarCoordinator).setUrlBarHintText(expectedText);
+        verify(urlBarCoordinator).setUrlBarHintText(R.string.hub_search_empty_hint);
     }
 
     @Test
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/tabmodel/UndoTabModelUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/tabmodel/UndoTabModelUnitTest.java
index 0683f4c..012a86ab 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/tabmodel/UndoTabModelUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/tabmodel/UndoTabModelUnitTest.java
@@ -329,15 +329,16 @@
                 undoable);
     }
 
-    private void closeAllTabs(final TabModel model) throws TimeoutException {
+    private void closeAllTabs(final TabModel model, final boolean undoable)
+            throws TimeoutException {
         closeMultipleTabsInternal(
                 model,
                 () ->
                         model.getTabRemover()
                                 .closeTabs(
-                                        TabClosureParams.closeAllTabs().build(),
+                                        TabClosureParams.closeAllTabs().allowUndo(undoable).build(),
                                         /* allowDialog= */ false),
-                /* undoable= */ true);
+                undoable);
     }
 
     private void cancelTabClosure(final TabModel model, final Tab tab) throws TimeoutException {
@@ -468,6 +469,8 @@
 
     /**
      * Test undo with a single tab with the following actions/expected states:
+     *
+     * <pre>
      *     Action                     Model List         Close List        Comprehensive List
      * 1.  Initial State              [ 0s ]             -                 [ 0s ]
      * 2.  CloseTab(0, allow undo)    -                  [ 0 ]             [ 0s ]
@@ -479,7 +482,7 @@
      * 8.  CommitAllClose             -                  -                 -
      * 9.  CreateTab(0)               [ 0s ]             -                 [ 0s ]
      * 10. CloseTab(0, disallow undo) -                  -                 -
-     *
+     * </pre>
      */
     @Test
     @SmallTest
@@ -543,6 +546,8 @@
 
     /**
      * Test undo with two tabs with the following actions/expected states:
+     *
+     * <pre>
      *     Action                     Model List         Close List        Comprehensive List
      * 1.  Initial State              [ 0 1s ]           -                 [ 0 1s ]
      * 2.  CloseTab(0, allow undo)    [ 1s ]             [ 0 ]             [ 0 1s ]
@@ -569,7 +574,7 @@
      * 23. CloseTab(0, allow undo)    [ 1s ]             [ 0 ]             [ 1s 0 ]
      * 24. CloseTab(1, allow undo)    -                  [ 1 0 ]           [ 1s 0 ]
      * 25. CommitAllClose             -                  -                 -
-     *
+     * </pre>
      */
     @Test
     @SmallTest
@@ -714,6 +719,8 @@
 
     /**
      * Test restoring in the same order of closing with the following actions/expected states:
+     *
+     * <pre>
      *     Action                     Model List         Close List        Comprehensive List
      * 1.  Initial State              [ 0 1 2 3s ]       -                 [ 0 1 2 3s ]
      * 2.  CloseTab(0, allow undo)    [ 1 2 3s ]         [ 0 ]             [ 0 1 2 3s ]
@@ -739,7 +746,7 @@
      * 22. CancelClose(3)             [ 1s 3 ]           [ 0 2 ]           [ 0 1s 2 3 ]
      * 23. CancelClose(0)             [ 0 1s 3 ]         [ 2 ]             [ 0 1s 2 3 ]
      * 24. CancelClose(2)             [ 0 1s 2 3 ]       -                 [ 0 1s 2 3 ]
-     *
+     * </pre>
      */
     @Test
     @SmallTest
@@ -856,6 +863,8 @@
 
     /**
      * Test restoring in the reverse of closing with the following actions/expected states:
+     *
+     * <pre>
      *     Action                     Model List         Close List        Comprehensive List
      * 1.  Initial State              [ 0 1 2 3s ]       -                 [ 0 1 2 3s ]
      * 2.  CloseTab(0, allow undo)    [ 1 2 3s ]         [ 0 ]             [ 0 1 2 3s ]
@@ -881,7 +890,7 @@
      * 22. CancelClose(2)             [ 1s 2 ]           [ 3 0 ]           [ 0 1s 2 3 ]
      * 23. CancelClose(0)             [ 0 1s 2 ]         [ 3 ]             [ 0 1s 2 3 ]
      * 24. CancelClose(3)             [ 0 1s 2 3 ]       -                 [ 0 1s 2 3 ]
-     *
+     * </pre>
      */
     @Test
     @SmallTest
@@ -998,6 +1007,8 @@
 
     /**
      * Test restoring out of order with the following actions/expected states:
+     *
+     * <pre>
      *     Action                     Model List         Close List        Comprehensive List
      * 1.  Initial State              [ 0 1 2 3s ]       -                 [ 0 1 2 3s ]
      * 2.  CloseTab(0, allow undo)    [ 1 2 3s ]         [ 0 ]             [ 0 1 2 3s ]
@@ -1016,7 +1027,7 @@
      * 15. CommitClose(0)             [ 2s ]             [ 1 ]             [ 1 2s ]
      * 16. CancelClose(1)             [ 1 2s ]           -                 [ 1 2s ]
      * 17. CloseTab(2, disallow undo) [ 1s ]             -                 [ 1s ]
-     *
+     * </pre>
      */
     @Test
     @SmallTest
@@ -1108,6 +1119,8 @@
 
     /**
      * Test restoring out of order with the following actions/expected states:
+     *
+     * <pre>
      *     Action                     Model List         Close List        Comprehensive List
      * 1.  Initial State              [ 0 1 2 3s ]       -                 [ 0 1 2 3s ]
      * 2.  CloseTab(1, allow undo)    [ 0 2 3s ]         [ 1 ]             [ 0 1 2 3s ]
@@ -1123,7 +1136,7 @@
      * 12. CloseTab(3, allow undo)    [ 1s ]             [ 3 ]             [ 1s 3 ]
      * 13. CloseTab(1, allow undo)    -                  [ 1 3 ]           [ 1s 3 ]
      * 14. CommitAll                  -                  -                 -
-     *
+     * </pre>
      */
     @Test
     @SmallTest
@@ -1202,6 +1215,8 @@
 
     /**
      * Test undo {@link TabModel#closeAllTabs()} with the following actions/expected states:
+     *
+     * <pre>
      *     Action                     Model List         Close List        Comprehensive List
      * 1.  Initial State              [ 0 1 2 3s ]       -                 [ 0  1 2 3s ]
      * 2.  CloseTab(1, allow undo)    [ 0 2 3s ]         [ 1 ]             [ 0  1 2 3s ]
@@ -1212,11 +1227,11 @@
      * 7.  CommitAllClose             -                  -                 -
      * 8.  CreateTab(0)               [ 0s ]             -                 [ 0s ]
      * 9.  CloseAll                   -                  [ 0 ]             [ 0s ]
-     *
+     * </pre>
      */
     @Test
     @SmallTest
-    public void testCloseAll() throws TimeoutException {
+    public void testCloseAll_UndoSupported() throws TimeoutException {
         final boolean isIncognito = false;
         final TabModel model = createTabModel(isIncognito);
         createTab(model, isIncognito);
@@ -1235,15 +1250,15 @@
         checkState(model, fullList, tab3, sEmptyList, fullList, tab3);
 
         // 2.
-        closeTab(model, tab1, true);
+        closeTab(model, tab1, /* undoable= */ true);
         checkState(model, new Tab[] {tab0, tab2, tab3}, tab3, new Tab[] {tab1}, fullList, tab3);
 
         // 3.
-        closeTab(model, tab2, true);
+        closeTab(model, tab2, /* undoable= */ true);
         checkState(model, new Tab[] {tab0, tab3}, tab3, new Tab[] {tab1, tab2}, fullList, tab3);
 
         // 4.
-        closeAllTabs(model);
+        closeAllTabs(model, /* undoable= */ true);
         checkState(model, sEmptyList, null, fullList, fullList, tab0);
 
         // 5.
@@ -1251,7 +1266,7 @@
         checkState(model, fullList, tab0, sEmptyList, fullList, tab0);
 
         // 6.
-        closeAllTabs(model);
+        closeAllTabs(model, /* undoable= */ true);
         checkState(model, sEmptyList, null, fullList, fullList, tab0);
 
         // 7.
@@ -1273,22 +1288,125 @@
         checkState(model, new Tab[] {tab0}, tab0, sEmptyList, fullList, tab0);
 
         // 9.
-        closeAllTabs(model);
+        closeAllTabs(model, /* undoable= */ true);
         checkState(model, sEmptyList, null, fullList, fullList, tab0);
         assertTrue(tab0.isClosing());
         assertTrue(tab0.isInitialized());
     }
 
     /**
-     * Test {@link TabModel#closeTab(Tab)} when not allowing a close commits all pending
-     * closes:
+     * Test {@link TabModel#closeAllTabs()} when not allowing undo, with the following
+     * actions/expected states:
+     *
+     * <pre>
+     *     Action                     Model List         Close List        Comprehensive List
+     * 1.  Initial State              [ 0 1 2 3s ]       -                 [ 0  1 2 3s ]
+     * 2.  CloseTab(1, allow undo)    [ 0 2 3s ]         [ 1 ]             [ 0  1 2 3s ]
+     * 3.  CloseTab(2, allow undo)    [ 0 3s ]           [ 2 1 ]           [ 0  1 2 3s ]
+     * 4.  CloseAll(disallow undo)    -                  -                 -
+     * 5.  CreateTab(0)               [ 0s ]             -                 [ 0s ]
+     * 6.  CloseAll(disallow undo)    -                  -                 -
+     * </pre>
+     */
+    @Test
+    @SmallTest
+    public void testCloseAll_UndoNotSupported() throws TimeoutException {
+        final boolean isIncognito = false;
+        final TabModel model = createTabModel(isIncognito);
+        createTab(model, isIncognito);
+        createTab(model, isIncognito);
+        createTab(model, isIncognito);
+        createTab(model, isIncognito);
+
+        Tab tab0 = model.getTabAt(0);
+        Tab tab1 = model.getTabAt(1);
+        Tab tab2 = model.getTabAt(2);
+        Tab tab3 = model.getTabAt(3);
+
+        Tab[] fullList = new Tab[] {tab0, tab1, tab2, tab3};
+
+        // 1.
+        checkState(
+                model,
+                /* tabsList= */ fullList,
+                /* selectedTab= */ tab3,
+                /* closingTabs= */ sEmptyList,
+                /* fullTabsList= */ fullList,
+                /* fullSelectedTab= */ tab3);
+
+        // 2.
+        closeTab(model, tab1, /* undoable= */ true);
+        checkState(
+                model,
+                /* tabsList= */ new Tab[] {tab0, tab2, tab3},
+                /* selectedTab= */ tab3,
+                /* closingTabs= */ new Tab[] {tab1},
+                /* fullTabsList= */ fullList,
+                /* fullSelectedTab= */ tab3);
+
+        // 3.
+        closeTab(model, tab2, /* undoable= */ true);
+        checkState(
+                model,
+                /* tabsList= */ new Tab[] {tab0, tab3},
+                /* selectedTab= */ tab3,
+                /* closingTabs= */ new Tab[] {tab1, tab2},
+                /* fullTabsList= */ fullList,
+                /* fullSelectedTab= */ tab3);
+
+        // 4.
+        closeAllTabs(model, /* undoable= */ false);
+        checkState(
+                model,
+                /* tabsList= */ sEmptyList,
+                /* selectedTab= */ null,
+                /* closingTabs= */ sEmptyList,
+                /* fullTabsList= */ sEmptyList,
+                /* fullSelectedTab= */ null);
+        assertTrue(tab0.isClosing());
+        assertTrue(tab1.isClosing());
+        assertTrue(tab2.isClosing());
+        assertTrue(tab3.isClosing());
+        assertFalse(tab0.isInitialized());
+        assertFalse(tab1.isInitialized());
+        assertFalse(tab2.isInitialized());
+        assertFalse(tab3.isInitialized());
+
+        // 5.
+        createTab(model, isIncognito);
+        tab0 = model.getTabAt(0);
+        fullList = new Tab[] {tab0};
+        checkState(
+                model,
+                /* tabsList= */ new Tab[] {tab0},
+                /* selectedTab= */ tab0,
+                /* closingTabs= */ sEmptyList,
+                /* fullTabsList= */ fullList,
+                /* fullSelectedTab= */ tab0);
+
+        // 6.
+        closeAllTabs(model, /* undoable= */ false);
+        checkState(
+                model,
+                /* tabsList= */ sEmptyList,
+                /* selectedTab= */ null,
+                /* closingTabs= */ sEmptyList,
+                /* fullTabsList= */ sEmptyList,
+                /* fullSelectedTab= */ null);
+        assertTrue(tab0.isClosing());
+        assertFalse(tab0.isInitialized());
+    }
+
+    /**
+     * Test {@link TabModel#closeTab(Tab)} when not allowing a close commits all pending closes:
+     *
+     * <pre>
      *     Action                     Model List         Close List        Comprehensive List
      * 1.  Initial State              [ 0 1 2 3s ]       -                 [ 0 1 2 3s ]
      * 2.  CloseTab(1, allow undo)    [ 0 2 3s ]         [ 1 ]             [ 0 1 2 3s ]
      * 3.  CloseTab(2, allow undo)    [ 0 3s ]           [ 2 1 ]           [ 0 1 2 3s ]
      * 4.  CloseTab(3, disallow undo) [ 0s ]             -                 [ 0s ]
-     *
-     *
+     * </pre>
      */
     @Test
     @SmallTest
@@ -1330,12 +1448,14 @@
 
     /**
      * Test {@link TabModel#moveTab(int, int)} commits all pending closes:
+     *
+     * <pre>
      *     Action                     Model List         Close List        Comprehensive List
      * 1.  Initial State              [ 0 1 2 3s ]       -                 [ 0 1 2 3s ]
      * 2.  CloseTab(1, allow undo)    [ 0 2 3s ]         [ 1 ]             [ 0 1 2 3s ]
      * 3.  CloseTab(2, allow undo)    [ 0 3s ]           [ 2 1 ]           [ 0 1 2 3s ]
      * 4.  MoveTab(0, 2)              [ 3s 0 ]           -                 [ 3s 0 ]
-     *
+     * </pre>
      */
     @Test
     @SmallTest
@@ -1377,6 +1497,8 @@
 
     /**
      * Test adding a {@link Tab} to a {@link TabModel} commits all pending closes:
+     *
+     * <pre>
      *     Action                     Model List         Close List        Comprehensive List
      * 1.  Initial State              [ 0 1 2 3s ]       -                 [ 0 1 2 3s ]
      * 2.  CloseTab(1, allow undo)    [ 0 2 3s ]         [ 1 ]             [ 0 1 2 3s ]
@@ -1386,6 +1508,7 @@
      * 6.  CloseTab(3, allow undo)    [ 4s ]             [ 3 0 ]           [ 0 3 4s ]
      * 7.  CloseTab(4, allow undo)    -                  [ 4 3 0 ]         [ 0s 3 4 ]
      * 8.  CreateTab(5)               [ 5s ]             -                 [ 5s ]
+     * </pre>
      */
     @Test
     @SmallTest
@@ -1452,14 +1575,17 @@
 
     /**
      * Test a {@link TabModel} where undo is not supported:
+     *
+     * <pre>
      *     Action                     Model List         Close List        Comprehensive List
      * 1.  Initial State              [ 0 1 2 3s ]       -                 [ 0 1 2 3s ]
      * 2.  CloseTab(1, allow undo)    [ 0 2 3s ]         -                 [ 0 2 3s ]
      * 3.  CloseAll                   -                  -                 -
+     * </pre>
      */
     @Test
     @SmallTest
-    public void testUndoNotSupported() throws TimeoutException {
+    public void testIncognito_UndoAlwaysNotSupported() throws TimeoutException {
         final boolean isIncognito = true;
         final TabModel model = createTabModel(isIncognito);
         createTab(model, isIncognito);
@@ -1479,14 +1605,16 @@
         assertFalse(model.supportsPendingClosures());
 
         // 2.
-        closeTab(model, tab1, true);
+        // Note: Despite the "undoable=true" setup, incognito tabs won't support undo.
+        closeTab(model, tab1, /* undoable= */ true);
         fullList = new Tab[] {tab0, tab2, tab3};
         checkState(model, new Tab[] {tab0, tab2, tab3}, tab3, sEmptyList, fullList, tab3);
         assertTrue(tab1.isClosing());
         assertFalse(tab1.isInitialized());
 
         // 3.
-        closeAllTabs(model);
+        // Note: Despite the "undoable=true" setup, incognito tabs won't support undo.
+        closeAllTabs(model, /* undoable= */ true);
         checkState(model, sEmptyList, null, sEmptyList, sEmptyList, null);
         assertTrue(tab0.isClosing());
         assertTrue(tab2.isClosing());
@@ -1497,17 +1625,21 @@
     }
 
     /**
-     * Test a {@link TabModel} where undo is not supported and
-     * {@link TabModelObserver#onFinishingMultipleTabClosure()} is called.
+     * Test a {@link TabModel} where undo is not supported and {@link
+     * TabModelObserver#onFinishingMultipleTabClosure()} is called.
+     *
+     * <pre>
      *     Action                     Model List         Close List        Comprehensive List
      * 1.  Initial State              [ 0 1 2 3 4s ]     -                 [ 0 1 2 3 4s ]
      * 2.  CloseTab(1)                [ 0 2 3 4s ]       -                 [ 0 2 3 4s ]
      * 3.  CloseMultipleTabs(2, 4)    [ 0 3s ]           -                 [ 0 3s ]
      * 4.  CloseAll                   -                  -                 -
+     * </pre>
      */
     @Test
     @SmallTest
-    public void testUndoNotSupportedOnFinishingMultipleTabClosure() throws TimeoutException {
+    public void testIncognito_UndoAlwaysNotSupportedOnFinishingMultipleTabClosure()
+            throws TimeoutException {
         final boolean isIncognito = true;
         final TabModel model = createTabModel(isIncognito);
         createTab(model, isIncognito);
@@ -1539,7 +1671,8 @@
                 });
 
         // 2.
-        closeTab(model, tab1, true);
+        // Note: Despite the "undoable=true" setup, incognito tabs won't support undo.
+        closeTab(model, tab1, /* undoable= */ true);
         fullList = new Tab[] {tab0, tab2, tab3, tab4};
         checkState(model, fullList, tab4, sEmptyList, fullList, tab4);
         assertTrue(tab1.isClosing());
@@ -1547,7 +1680,8 @@
         assertArrayEquals(new Tab[] {tab1}, lastClosedTabs.toArray(new Tab[0]));
 
         // 3.
-        closeMultipleTabs(model, Arrays.asList(new Tab[] {tab2, tab4}), true);
+        // Note: Despite the "undoable=true" setup, incognito tabs won't support undo.
+        closeMultipleTabs(model, Arrays.asList(tab2, tab4), /* undoable= */ true);
         fullList = new Tab[] {tab0, tab3};
         checkState(model, fullList, tab0, sEmptyList, fullList, tab0);
         assertTrue(tab2.isClosing());
@@ -1557,7 +1691,8 @@
         assertArrayEquals(new Tab[] {tab2, tab4}, lastClosedTabs.toArray(new Tab[0]));
 
         // 4.
-        closeAllTabs(model);
+        // Note: Despite the "undoable=true" setup, incognito tabs won't support undo.
+        closeAllTabs(model, /* undoable= */ true);
         checkState(model, sEmptyList, null, sEmptyList, sEmptyList, null);
         assertTrue(tab0.isClosing());
         assertTrue(tab3.isClosing());
diff --git a/chrome/android/profiles/arm.newest.txt b/chrome/android/profiles/arm.newest.txt
index 20d4512..4b8bdecd 100644
--- a/chrome/android/profiles/arm.newest.txt
+++ b/chrome/android/profiles/arm.newest.txt
@@ -1 +1 @@
-chromeos-chrome-arm-138.0.7184.0_rc-r1-merged.afdo.bz2
+chromeos-chrome-arm-138.0.7190.0_rc-r1-merged.afdo.bz2
diff --git a/chrome/android/webapk/README.md b/chrome/android/webapk/README.md
index 6c3262d..756f0cd 100644
--- a/chrome/android/webapk/README.md
+++ b/chrome/android/webapk/README.md
@@ -10,42 +10,44 @@
 
 Code layout
 --
-- shell_apk/ - Code for APK which is generated by the Chrome server. Not compiled
-into Chrome.  
-- libs/client/ - Library which is compiled into Chrome. Useful if another browser
-implemented WebAPKs.  
-- libs/common/ - Library with code which is used by both WebAPK shell and Chrome. WebAPK shell
+- `shell_apk/` - Code for APK which is generated by the Chrome server. Not compiled
+into Chrome.
+- `libs/client/` - Library which is compiled into Chrome. Useful if another browser
+implemented WebAPKs.
+- `libs/common/` - Library with code which is used by both WebAPK shell and Chrome. WebAPK shell
 and Chrome might use different versions of the library.
-- libs/runtime_library/ - Library which is stored in the Chrome APK's assets and which is extracted
+- `libs/runtime_library/` - Library which is stored in the Chrome APK's assets and which is extracted
 from the Chrome APK by the WebAPK at runtime. This approach ensures
 that the majority of the WebAPK logic is shared and can be updated as often as
-CHrome, without the need for updating each individual WebAPK.
+Chrome, without the need for updating each individual WebAPK.
 
 Installing WebAPK from Chrome Developer Build
 --
 To enable a developer build of Chrome to install WebAPKs run the following
 commands:
 
-`adb root`  
-`adb shell am broadcast -a com.google.gservices.intent.action.GSERVICES_OVERRIDE -e finsky.webapk.do_webapk_install_package_check false`  
-`adb shell am broadcast -a com.google.gservices.intent.action.GSERVICES_OVERRIDE -e finsky.webapk.do_webapk_install_signing_check false`
+```
+adb root
+adb shell am broadcast -a com.google.gservices.intent.action.GSERVICES_OVERRIDE -e finsky.webapk.do_webapk_install_package_check false
+adb shell am broadcast -a com.google.gservices.intent.action.GSERVICES_OVERRIDE -e finsky.webapk.do_webapk_install_signing_check false
+```
 
 Building WebAPK shell locally
 --
 It is possible to build a test WebAPK and bypass the generation on the WebAPK
 server.
 
-On Android, build  
-//chrome/android/webapk/shell_apk:webapk  
-On ChromeOS, build  
-//chrome/android/webapk/shell_apk:webapk_chromeos  
+On Android, build
+`//chrome/android/webapk/shell_apk:webapk`
+On ChromeOS, build
+`//chrome/android/webapk/shell_apk:webapk_chromeos`
 
 Both can be customized via [shell_apk/manifest/bound_manifest_config.json](https://source.chromium.org/chromium/chromium/src/+/main:chrome/android/webapk/shell_apk/manifest/bound_manifest_config.json)
 
 To make a locally built WebAPK launch Chrome in 'WebAPK mode':
-1) Set the --skip-webapk-verification Chrome command line flag
-2) Ensure that the 'scope_url_\*' parameters in bound_manifest_config.json
-   match a directory which contains the 'start_url' parameter. In 99% of
+1) Set the `--skip-webapk-verification` Chrome command line flag
+2) Ensure that the `'scope_url_*'` parameters in bound_manifest_config.json
+   match a directory which contains the `'start_url'` parameter. In 99% of
    test cases the other parameters can use arbitrary URL origins.
 
    Example:
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 90afcdf..1e115ed 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -3492,6 +3492,7 @@
       "//components/payments/content/android",
       "//components/payments/content/android:jni_headers",
       "//components/payments/content/android/ui",
+      "//components/payments/content/browser_binding:browser_bound_keys_deleter_factory",
       "//components/permissions/android:native",
       "//components/plus_addresses:features",
       "//components/plus_addresses/resources/strings",
@@ -4367,6 +4368,15 @@
       "//chrome/browser/ui/media_router:impl",
       "//chrome/browser/ui/page_action:icon_type",
       "//chrome/browser/ui/privacy_sandbox",
+      "//chrome/browser/ui/search",
+
+      # TODO(crbug.com/418180294): Remove this circular dependency when:
+      # - bookmark_model_factory.h,
+      # - after_startup_task_utils.h,
+      # - history_service_factory.h,
+      # - identity_manager_factory.h
+      # ... get componentized.
+      "//chrome/browser/ui/search:impl",
       "//chrome/browser/ui/send_tab_to_self",
       "//chrome/browser/ui/views/zoom:impl",
       "//chrome/browser/ui/webui/access_code_cast",
@@ -4553,6 +4563,14 @@
       # gets modularized.
       "//chrome/browser/ui/privacy_sandbox:impl",
 
+      # TODO(crbug.com/418180294): Remove this circular dependency when:
+      # - bookmark_model_factory.h,
+      # - after_startup_task_utils.h,
+      # - history_service_factory.h,
+      # - identity_manager_factory.h
+      # ... get componentized.
+      "//chrome/browser/ui/search:impl",
+
       "//chrome/browser/ui/browser_window:impl",
       "//chrome/browser/ui/exclusive_access",
       "//chrome/browser/ui/views/toolbar",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 9e60652..a4c5852 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -4846,11 +4846,6 @@
      flag_descriptions::kEnterpriseBadgingForNtpFooterDescription,
      kOsMac | kOsWin | kOsLinux,
      FEATURE_VALUE_TYPE(features::kEnterpriseBadgingForNtpFooter)},
-    {"enable-enterprise-updated-profile-creation-screen",
-     flag_descriptions::kEnterpriseUpdatedProfileCreationScreenName,
-     flag_descriptions::kEnterpriseUpdatedProfileCreationScreenDescription,
-     kOsMac | kOsWin | kOsLinux,
-     FEATURE_VALUE_TYPE(features::kEnterpriseUpdatedProfileCreationScreen)},
     {"enable-enterprise-profile-required-interstitial",
      flag_descriptions::kManagedProfileRequiredInterstitialName,
      flag_descriptions::kManagedProfileRequiredInterstitialDescription,
@@ -11558,9 +11553,6 @@
          password_manager::features::kPasswordFormGroupedAffiliations)},
 
 #if !BUILDFLAG(IS_ANDROID)
-    {"ai-settings-page-refresh", flag_descriptions::kAiSettingsPageRefreshName,
-     flag_descriptions::kAiSettingsPageRefreshDescription, kOsDesktop,
-     FEATURE_VALUE_TYPE(optimization_guide::features::kAiSettingsPageRefresh)},
     {"privacy-guide-ai-settings",
      flag_descriptions::kPrivacyGuideAiSettingsName,
      flag_descriptions::kPrivacyGuideAiSettingsDescription, kOsDesktop,
@@ -12483,6 +12475,16 @@
      FEATURE_VALUE_TYPE(password_manager::features::kFillRecoveryPassword)},
 #endif  // BUILDFLAG(IS_ANDROID)
 
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || \
+    BUILDFLAG(IS_CHROMEOS)
+    {"enable-site-search-allow-user-override-policy",
+     flag_descriptions::kEnableSiteSearchAllowUserOverridePolicyName,
+     flag_descriptions::kEnableSiteSearchAllowUserOverridePolicyDescription,
+     static_cast<unsigned short>(kOsCrOS | kOsLinux | kOsMac | kOsWin),
+     FEATURE_VALUE_TYPE(omnibox::kEnableSiteSearchAllowUserOverridePolicy)},
+#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) ||
+        // BUILDFLAG(IS_CHROME_OS)
+
     // Add new entries above this line.
 
     // NOTE: Adding a new flag requires adding a corresponding entry to enum
diff --git a/chrome/browser/actor/actor_test_util.cc b/chrome/browser/actor/actor_test_util.cc
index ac8acdb..899152c83 100644
--- a/chrome/browser/actor/actor_test_util.cc
+++ b/chrome/browser/actor/actor_test_util.cc
@@ -13,6 +13,7 @@
 #include "chrome/common/actor/action_result.h"
 #include "components/optimization_guide/proto/features/actions_data.pb.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/point.h"
 
 namespace actor {
 
@@ -97,6 +98,22 @@
   return action;
 }
 
+BrowserAction MakeType(const gfx::Point& type_point,
+                       std::string_view text,
+                       bool follow_by_enter) {
+  BrowserAction action;
+  TypeAction* type_action = action.add_action_information()->mutable_type();
+  Coordinate* coordinate = type_action->mutable_target()->mutable_coordinate();
+  coordinate->set_x(type_point.x());
+  coordinate->set_y(type_point.y());
+  type_action->set_text(text);
+  // TODO(crbug.com/409570203): Tests should set a mode.
+  type_action->set_mode(
+      TypeAction_TypeMode::TypeAction_TypeMode_UNKNOWN_TYPE_MODE);
+  type_action->set_follow_by_enter(follow_by_enter);
+  return action;
+}
+
 BrowserAction MakeScroll(std::optional<int> content_node_id,
                          float scroll_offset_x,
                          float scroll_offset_y) {
diff --git a/chrome/browser/actor/actor_test_util.h b/chrome/browser/actor/actor_test_util.h
index 5ed93a61..48049f00e 100644
--- a/chrome/browser/actor/actor_test_util.h
+++ b/chrome/browser/actor/actor_test_util.h
@@ -26,6 +26,9 @@
 optimization_guide::proto::BrowserAction MakeType(int content_node_id,
                                                   std::string_view text,
                                                   bool follow_by_enter);
+optimization_guide::proto::BrowserAction MakeType(const gfx::Point& type_point,
+                                                  std::string_view text,
+                                                  bool follow_by_enter);
 optimization_guide::proto::BrowserAction MakeSelect(int content_node_id,
                                                     std::string_view value);
 
diff --git a/chrome/browser/actor/tools/tools_browsertest.cc b/chrome/browser/actor/tools/tools_browsertest.cc
index 209552bc..c23c6029 100644
--- a/chrome/browser/actor/tools/tools_browsertest.cc
+++ b/chrome/browser/actor/tools/tools_browsertest.cc
@@ -619,6 +619,169 @@
             EvalJs(web_contents(), "document.getElementById('input2').value"));
 }
 
+// Basic test of the TypeTool coordinate target - ensure typed string is entered
+// into a node at the coordinate.
+IN_PROC_BROWSER_TEST_F(ActorToolsTest, TypeTool_TextInputAtCoordinate) {
+  const GURL url =
+      embedded_test_server()->GetURL("/actor/type_input_coordinate.html");
+  ASSERT_TRUE(content::NavigateToURL(web_contents(), url));
+
+  std::string typed_string = "test";
+  // Type into coordinate of input box.
+  {
+    gfx::Point type_point = gfx::ToFlooredPoint(
+        GetCenterCoordinatesOfElementWithId(web_contents(), "input"));
+    BrowserAction action =
+        MakeType(type_point, typed_string, /*follow_by_enter=*/true);
+
+    TestFuture<mojom::ActionResultPtr> result;
+    actor_coordinator().Act(action, result.GetCallback());
+    ExpectOkResult(result);
+
+    EXPECT_EQ(typed_string,
+              EvalJs(web_contents(), "document.getElementById('input').value"));
+  }
+  // Type into coordinate of editable div.
+  {
+    gfx::Point type_point = gfx::ToFlooredPoint(
+        GetCenterCoordinatesOfElementWithId(web_contents(), "editableDiv"));
+    BrowserAction action =
+        MakeType(type_point, typed_string, /*follow_by_enter=*/true);
+
+    TestFuture<mojom::ActionResultPtr> result;
+    actor_coordinator().Act(action, result.GetCallback());
+    ExpectOkResult(result);
+
+    EXPECT_EQ(typed_string,
+              EvalJs(web_contents(),
+                     "document.getElementById('editableDiv').textContent"));
+  }
+}
+
+// Ensure the type tool correctly sends the events to element at the
+// coordinates.
+IN_PROC_BROWSER_TEST_F(ActorToolsTest, TypeTool_EventsSentToCoordinates) {
+  const GURL url =
+      embedded_test_server()->GetURL("/actor/type_input_coordinate.html");
+  ASSERT_TRUE(content::NavigateToURL(web_contents(), url));
+
+  // The log starts empty.
+  ASSERT_EQ("", EvalJs(web_contents(), "input_event_log.join(',')"));
+
+  // Send event to an editable div.
+  {
+    gfx::Point type_point = gfx::ToFlooredPoint(
+        GetCenterCoordinatesOfElementWithId(web_contents(), "editableDiv"));
+
+    // Send 'a'. Ensure a click event is observed first on element at the
+    // coordinate.
+    std::string typed_string = "a";
+    BrowserAction action =
+        MakeType(type_point, typed_string, /*follow_by_enter=*/false);
+
+    TestFuture<mojom::ActionResultPtr> result;
+    actor_coordinator().Act(action, result.GetCallback());
+    ExpectOkResult(result);
+
+    EXPECT_EQ(
+        // click
+        "mousedown(" + type_point.ToString() + "),mouseup(" +
+            type_point.ToString() + "),click(" + type_point.ToString() +
+            "),"
+            // a
+            "keydown,input,keyup",
+        EvalJs(web_contents(), "input_event_log.join(',')"));
+  }
+
+  ASSERT_TRUE(ExecJs(web_contents(), "input_event_log = []"));
+
+  // Send event to a focusable but not editable div.
+  {
+    gfx::Point type_point = gfx::ToFlooredPoint(
+        GetCenterCoordinatesOfElementWithId(web_contents(), "focusableDiv"));
+
+    // Send 'a'. Ensure a click event is observed first on element at the
+    // coordinate.
+    std::string typed_string = "a";
+    BrowserAction action =
+        MakeType(type_point, typed_string, /*follow_by_enter=*/false);
+
+    TestFuture<mojom::ActionResultPtr> result;
+    actor_coordinator().Act(action, result.GetCallback());
+    ExpectOkResult(result);
+
+    EXPECT_EQ(
+        // click
+        "mousedown(" + type_point.ToString() + "),mouseup(" +
+            type_point.ToString() + "),click(" + type_point.ToString() +
+            "),"
+            // a
+            "keydown,keyup",
+        EvalJs(web_contents(), "input_event_log.join(',')"));
+  }
+}
+
+// Ensure the type tool correctly sends the events to an unfocusable element at
+// the coordinates.
+IN_PROC_BROWSER_TEST_F(ActorToolsTest,
+                       TypeTool_EventsSentToUnfocusableCoordinate) {
+  const GURL url =
+      embedded_test_server()->GetURL("/actor/type_input_coordinate.html");
+  ASSERT_TRUE(content::NavigateToURL(web_contents(), url));
+
+  // The log starts empty.
+  ASSERT_EQ("", EvalJs(web_contents(), "input_event_log.join(',')"));
+
+  // Set coordinate to an unfocusable div.
+
+  gfx::Point type_point = gfx::ToFlooredPoint(
+      GetCenterCoordinatesOfElementWithId(web_contents(), "unfocusableDiv"));
+
+  // Send 'a'. Ensure a click event is observed first on element at the
+  // coordinate.
+  std::string typed_string = "a";
+  BrowserAction action =
+      MakeType(type_point, typed_string, /*follow_by_enter=*/false);
+
+  TestFuture<mojom::ActionResultPtr> result;
+  actor_coordinator().Act(action, result.GetCallback());
+  ExpectOkResult(result);
+
+  // Only the click is handled by the node at coordinate.
+  EXPECT_EQ(
+      // click
+      "mousedown(" + type_point.ToString() + "),mouseup(" +
+          type_point.ToString() + "),click(" + type_point.ToString() + ")",
+      EvalJs(web_contents(), "input_event_log.join(',')"));
+  // The keydown and keyup event will go to the body now that div is
+  // unfocusable.
+  EXPECT_EQ(
+      // a
+      "keydown,keyup",
+      EvalJs(web_contents(), "body_input_event_log.join(',')"));
+}
+
+// Ensure the type tool will fail if target coordinate is offscreen.
+IN_PROC_BROWSER_TEST_F(ActorToolsTest, TypeTool_SentToOffScreenCoordinates) {
+  const GURL url =
+      embedded_test_server()->GetURL("/actor/type_input_coordinate.html");
+  ASSERT_TRUE(content::NavigateToURL(web_contents(), url));
+
+  // The log starts empty.
+  ASSERT_EQ("", EvalJs(web_contents(), "input_event_log.join(',')"));
+
+  // Send 'a' to an offscreen coordinate and observe failure.
+  std::string typed_string = "a";
+  BrowserAction action =
+      MakeType({-1, 0}, typed_string, /*follow_by_enter=*/false);
+
+  TestFuture<mojom::ActionResultPtr> result;
+  actor_coordinator().Act(action, result.GetCallback());
+  ExpectErrorResult(result, mojom::ActionResultCode::kClickInvalidPoint);
+
+  EXPECT_EQ("", EvalJs(web_contents(), "input_event_log.join(',')"));
+}
+
 // ===============================================
 // Mouse Move Tool
 // ===============================================
diff --git a/chrome/browser/ai/ai_data_keyed_service.cc b/chrome/browser/ai/ai_data_keyed_service.cc
index 56610a83..753f649f 100644
--- a/chrome/browser/ai/ai_data_keyed_service.cc
+++ b/chrome/browser/ai/ai_data_keyed_service.cc
@@ -1004,7 +1004,7 @@
   // handles getting a new observation.
 
   glic::FocusedTabData focused_tab_data{tab_->GetContents()->GetWeakPtr()};
-  glic::GlicPageContextFetcher::Fetch(
+  glic::FetchPageContext(
       focused_tab_data, DefaultOptions(),
       base::BindOnce(&AiDataKeyedService::ConvertToBrowserActionResult,
                      weak_factory_.GetWeakPtr(), std::move(callback), task_id_,
diff --git a/chrome/browser/ai/ai_on_device_browsertest.cc b/chrome/browser/ai/ai_on_device_browsertest.cc
index c2fd037..b330827 100644
--- a/chrome/browser/ai/ai_on_device_browsertest.cc
+++ b/chrome/browser/ai/ai_on_device_browsertest.cc
@@ -2,24 +2,125 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <string>
+
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "components/embedder_support/switches.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/content_switches.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/default_handlers.h"
 #include "third_party/blink/public/common/features_generated.h"
 
-class AIOnDeviceBrowserTest : public InProcessBrowserTest {
+namespace {
+
+// This is the public key of tools/origin_trials/eftest.key, used to validate
+// origin trial tokens generated by tools/origin_trials/generate_token.py.
+// https://chromium.googlesource.com/chromium/src/+/main/docs/origin_trials_integration.md
+constexpr char kOriginTrialPublicKeyForTesting[] =
+    "dRCs+TocuKkocNKa0AtZ4awrt9XKH2SQCI6o4FY6BNA=";
+
+// Origin trial tokens (expire on 2033-08-06) generated by
+// tools/origin_trials/generate_token.py https://a.test:32123 AIFooAPI \
+//  --expire-days 3000
+constexpr char kAIRewriterAPIOTToken[] =
+    "A7gvtQAwPhmBOadB9rGCwqWwgmba7wU+zXqjfDR9cfTzR8Xi2Tkedxawd/"
+    "PMg4SLjABtNGJZf3Iel4zqG/"
+    "iqZQ8AAABUeyJvcmlnaW4iOiAiaHR0cHM6Ly9hLnRlc3Q6MzIxMjMiLCAiZmVhdHVyZSI6ICJB"
+    "SVJld3JpdGVyQVBJIiwgImV4cGlyeSI6IDIwMDY5NzA3NDF9";
+constexpr char kAISummarizationAPIOTToken[] =
+    "A2UgHK404yD+"
+    "lJUncd3At3e5DePfDRTjhM96os1JFzJuBhMJyV8bVZNp9MSpCr4efZ2mdrMqOkDMmBkNyrJ75g"
+    "sAAABZeyJvcmlnaW4iOiAiaHR0cHM6Ly9hLnRlc3Q6MzIxMjMiLCAiZmVhdHVyZSI6ICJBSVN1"
+    "bW1hcml6YXRpb25BUEkiLCAiZXhwaXJ5IjogMjAwNjk3MDcwMX0=";
+constexpr char kAIWriterAPIOTToken[] =
+    "A0jJGgLmqGgNaHNH7my4hKMTvp7oBOvGoLvZhH3tzAGKY3SNkmSQCSTxFtgXNGxloQ7rFqxaut"
+    "85MKQRKEug+"
+    "Q4AAABSeyJvcmlnaW4iOiAiaHR0cHM6Ly9hLnRlc3Q6MzIxMjMiLCAiZmVhdHVyZSI6ICJBSVd"
+    "yaXRlckFQSSIsICJleHBpcnkiOiAyMDA2OTcwNjU4fQ==";
+
+// The boolean tuple describing:
+// 1. if the `kAIFooAPI` chrome://flag entries are explicitly enabled;
+// 2. if the `kAIFooAPIForWorkers` are explicitly enabled;
+// 3. if the `kAIFooAPI` kill switches are triggered;
+// 4. if the `kAIFooAPI` OT tokens are supplied (for any APIs in OT).
+using Variant = std::tuple<bool, bool, bool, bool>;
+bool IsAPIFlagEnabled(Variant v) {
+  return std::get<0>(v);
+}
+bool IsAPIWorkerFlagEnabled(Variant v) {
+  return std::get<1>(v);
+}
+bool IsAPIKillSwitchTriggered(Variant v) {
+  return std::get<2>(v);
+}
+bool IsOTTokenSupplied(Variant v) {
+  return std::get<3>(v);
+}
+
+// Describes the test variants in a meaningful way in the parameterized tests.
+std::string DescribeTestVariant(const testing::TestParamInfo<Variant> info) {
+  std::string api_flag =
+      IsAPIFlagEnabled(info.param) ? "WithAPIFlag" : "NoAPIFlag";
+  std::string worker_flag =
+      IsAPIWorkerFlagEnabled(info.param) ? "WithWorkerFlag" : "NoWorkerFlag";
+  std::string kill_switch = IsAPIKillSwitchTriggered(info.param)
+                                ? "WithAPIKillswitch"
+                                : "NoAPIKillswitch";
+  std::string ot_token =
+      IsOTTokenSupplied(info.param) ? "WithOTToken" : "NoOTToken";
+  return base::JoinString({api_flag, worker_flag, kill_switch, ot_token}, "_");
+}
+
+// Returns whether the API name matches those currently in origin trial.
+bool IsAPIInOT(std::string_view name) {
+  return name == "Summarizer" || name == "Rewriter" || name == "Writer";
+}
+
+// Injects an Origin Trial `token` into the page.
+void InjectOTToken(content::WebContents* tab, std::string_view token) {
+  static constexpr char kScript[] =
+      R"JS(
+        const meta = document.createElement('meta');
+        meta.httpEquiv = 'origin-trial';
+        meta.content = '%s';
+        document.head.appendChild(meta);
+      )JS";
+  EXPECT_TRUE(ExecJs(tab, base::StringPrintf(kScript, token)));
+}
+
+class AIOnDeviceBrowserTest : public InProcessBrowserTest,
+                              public testing::WithParamInterface<Variant> {
  public:
-  AIOnDeviceBrowserTest() {
-    feature_list_.InitWithFeatures(
-        {blink::features::kAIPromptAPIMultimodalInput,
-         blink::features::kAIRewriterAPI, blink::features::kAISummarizationAPI,
-         blink::features::kAIWriterAPI},
-        {});
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    if (IsAPIFlagEnabled(GetParam())) {
+      command_line->AppendSwitchASCII(switches::kEnableBlinkFeatures,
+                                      "AIPromptAPI,AIRewriterAPI,"
+                                      "AISummarizationAPI,AIWriterAPI");
+    }
+    if (IsAPIWorkerFlagEnabled(GetParam())) {
+      command_line->AppendSwitchASCII(switches::kEnableBlinkFeatures,
+                                      "AIPromptAPIForWorkers,"
+                                      "AIRewriterAPIForWorkers,"
+                                      "AISummarizationAPIForWorkers,"
+                                      "AIWriterAPIForWorkers");
+    }
+    // Specify the OT test public key to make the test token effective.
+    command_line->AppendSwitchASCII(embedder_support::kOriginTrialPublicKey,
+                                    kOriginTrialPublicKeyForTesting);
+    if (IsAPIKillSwitchTriggered(GetParam())) {
+      base::flat_map<base::test::FeatureRef, bool> feature_states;
+      feature_states[blink::features::kAIPromptAPI] = false;
+      feature_states[blink::features::kAIRewriterAPI] = false;
+      feature_states[blink::features::kAISummarizationAPI] = false;
+      feature_states[blink::features::kAIWriterAPI] = false;
+      feature_list_.InitWithFeatureStates(feature_states);
+    }
   }
 
   void SetUpOnMainThread() override {
@@ -27,18 +128,34 @@
     embedded_https_test_server().SetSSLConfig(
         net::EmbeddedTestServer::CERT_TEST_NAMES);
     net::test_server::RegisterDefaultHandlers(&embedded_https_test_server());
-    ASSERT_TRUE(embedded_https_test_server().Start());
+    // Specify a port to match the generated test OT tokens.
+    ASSERT_TRUE(embedded_https_test_server().Start(/*port=*/32123));
 
     auto* tab = browser()->tab_strip_model()->GetActiveWebContents();
     GURL url(embedded_https_test_server().GetURL("a.test", "/empty.html"));
     ASSERT_TRUE(NavigateToURL(tab, url));
+
+    if (IsOTTokenSupplied(GetParam())) {
+      InjectOTToken(tab, kAIRewriterAPIOTToken);
+      InjectOTToken(tab, kAISummarizationAPIOTToken);
+      InjectOTToken(tab, kAIWriterAPIOTToken);
+    }
   }
 
  private:
   base::test::ScopedFeatureList feature_list_;
 };
 
-IN_PROC_BROWSER_TEST_F(AIOnDeviceBrowserTest, APIsExposedToWindowNotWorker) {
+INSTANTIATE_TEST_SUITE_P(
+    /* no prefix */,
+    AIOnDeviceBrowserTest,
+    testing::Combine(testing::Bool(),
+                     testing::Bool(),
+                     testing::Bool(),
+                     testing::Bool()),
+    &DescribeTestVariant);
+
+IN_PROC_BROWSER_TEST_P(AIOnDeviceBrowserTest, APIsExposedToWindowAndWorker) {
   static constexpr char kWindow[] = "try { %s; 'OK'; } catch (e) { e.name; }";
   static constexpr char kWorker[] =
       R"JS(
@@ -50,9 +167,19 @@
     )JS";
   auto* tab = browser()->tab_strip_model()->GetActiveWebContents();
   for (const auto& id : {"LanguageModel", "Rewriter", "Summarizer", "Writer"}) {
-    EXPECT_EQ("OK", content::EvalJs(tab, absl::StrFormat(kWindow, id))) << id;
-    EXPECT_EQ("ReferenceError",
-              content::EvalJs(tab, absl::StrFormat(kWorker, id)))
-        << id;
+    bool is_api_exposed = IsAPIFlagEnabled(GetParam()) ||
+                          (IsAPIInOT(id) && IsOTTokenSupplied(GetParam()) &&
+                           !IsAPIKillSwitchTriggered(GetParam()));
+    std::string expected = is_api_exposed ? "OK" : "ReferenceError";
+    EXPECT_EQ(expected, content::EvalJs(tab, absl::StrFormat(kWindow, id)))
+        << "Unexpected " << id << " result in window context.";
+
+    // Worker access requires an additional flag, even with a valid OT.
+    is_api_exposed &= IsAPIWorkerFlagEnabled(GetParam());
+    expected = is_api_exposed ? "OK" : "ReferenceError";
+    EXPECT_EQ(expected, content::EvalJs(tab, absl::StrFormat(kWorker, id)))
+        << "Unexpected " << id << " result in worker context.";
   }
 }
+
+}  // namespace
diff --git a/chrome/browser/apps/app_service/publishers/chrome_app_deprecation_browsertest.cc b/chrome/browser/apps/app_service/publishers/chrome_app_deprecation_browsertest.cc
index 9cc7e690..ca6f1c5 100644
--- a/chrome/browser/apps/app_service/publishers/chrome_app_deprecation_browsertest.cc
+++ b/chrome/browser/apps/app_service/publishers/chrome_app_deprecation_browsertest.cc
@@ -9,11 +9,6 @@
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/apps/platform_apps/app_browsertest_util.h"
-#include "chrome/browser/ash/app_mode/kiosk_app_launch_error.h"
-#include "chrome/browser/ash/app_mode/kiosk_controller.h"
-#include "chrome/browser/ash/app_mode/test/fake_cws_chrome_apps.h"
-#include "chrome/browser/ash/app_mode/test/kiosk_mixin.h"
-#include "chrome/browser/ash/app_mode/test/kiosk_test_utils.h"
 #include "chrome/browser/extensions/extension_browsertest.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/common/pref_names.h"
@@ -22,11 +17,6 @@
 #include "extensions/test/test_extension_dir.h"
 #include "ui/message_center/message_center.h"
 
-using ash::KioskMixin;
-using ash::kiosk::test::LaunchAppManually;
-using ash::kiosk::test::OfflineEnabledChromeAppV1;
-using ash::kiosk::test::WaitKioskLaunched;
-
 namespace apps {
 
 using extensions::Extension;
@@ -79,109 +69,4 @@
   ASSERT_TRUE(center->GetNotifications().size() == notifications_count);
 }
 
-class ChromeAppDeprecationKioskSessionBrowserTest
-    : public MixinBasedInProcessBrowserTest {
- public:
-  ChromeAppDeprecationKioskSessionBrowserTest() = default;
-  ChromeAppDeprecationKioskSessionBrowserTest(
-      const ChromeAppDeprecationKioskSessionBrowserTest&) = delete;
-  ChromeAppDeprecationKioskSessionBrowserTest& operator=(
-      const ChromeAppDeprecationKioskSessionBrowserTest&) = delete;
-  ~ChromeAppDeprecationKioskSessionBrowserTest() override = default;
-
-  KioskMixin kiosk_{
-      &mixin_host_,
-      /*cached_configuration=*/KioskMixin::Config{
-          /*name=*/"ChromeApp",
-          KioskMixin::AutoLaunchAccount{OfflineEnabledChromeAppV1().account_id},
-          {OfflineEnabledChromeAppV1()}}};
-
- protected:
-  base::test::ScopedFeatureList scoped_feature_list_;
-};
-
-class KioskDefaultFeatureFlag
-    : public ChromeAppDeprecationKioskSessionBrowserTest {
- public:
-  KioskDefaultFeatureFlag() {
-    scoped_feature_list_.InitWithEmptyFeatureAndFieldTrialLists();
-  }
-  KioskDefaultFeatureFlag(const KioskDefaultFeatureFlag&) = delete;
-  KioskDefaultFeatureFlag& operator=(const KioskDefaultFeatureFlag&) = delete;
-  ~KioskDefaultFeatureFlag() override = default;
-};
-
-IN_PROC_BROWSER_TEST_F(KioskDefaultFeatureFlag, FeatureFlag) {
-  ASSERT_FALSE(base::FeatureList::IsEnabled(
-      chrome_app_deprecation::kAllowChromeAppsInKioskSessions));
-
-  RunUntilBrowserProcessQuits();
-  EXPECT_EQ(ash::KioskAppLaunchError::Error::kChromeAppDeprecated,
-            ash::KioskAppLaunchError::Get());
-  EXPECT_FALSE(ash::KioskController::Get().IsSessionStarting());
-}
-
-class KioskEnabledFeatureFlag
-    : public ChromeAppDeprecationKioskSessionBrowserTest {
- public:
-  KioskEnabledFeatureFlag() {
-    scoped_feature_list_.InitAndEnableFeature(
-        chrome_app_deprecation::kAllowChromeAppsInKioskSessions);
-  }
-  KioskEnabledFeatureFlag(const KioskEnabledFeatureFlag&) = delete;
-  KioskEnabledFeatureFlag& operator=(const KioskEnabledFeatureFlag&) = delete;
-  ~KioskEnabledFeatureFlag() override = default;
-};
-
-IN_PROC_BROWSER_TEST_F(KioskEnabledFeatureFlag, FeatureFlag) {
-  ASSERT_TRUE(base::FeatureList::IsEnabled(
-      chrome_app_deprecation::kAllowChromeAppsInKioskSessions));
-  ASSERT_TRUE(WaitKioskLaunched());
-}
-
-class KioskDisabledFeatureFlag
-    : public ChromeAppDeprecationKioskSessionBrowserTest {
- public:
-  KioskDisabledFeatureFlag() {
-    scoped_feature_list_.InitAndDisableFeature(
-        chrome_app_deprecation::kAllowChromeAppsInKioskSessions);
-  }
-  KioskDisabledFeatureFlag(const KioskDisabledFeatureFlag&) = delete;
-  KioskDisabledFeatureFlag& operator=(const KioskDisabledFeatureFlag&) = delete;
-  ~KioskDisabledFeatureFlag() override = default;
-};
-
-IN_PROC_BROWSER_TEST_F(KioskDisabledFeatureFlag, FeatureFlag) {
-  ASSERT_FALSE(base::FeatureList::IsEnabled(
-      chrome_app_deprecation::kAllowChromeAppsInKioskSessions));
-
-  RunUntilBrowserProcessQuits();
-  EXPECT_EQ(ash::KioskAppLaunchError::Error::kChromeAppDeprecated,
-            ash::KioskAppLaunchError::Get());
-  EXPECT_FALSE(ash::KioskController::Get().IsSessionStarting());
-}
-
-class KioskDisabledFeatureFlagWithAllowlistedApp
-    : public ChromeAppDeprecationKioskSessionBrowserTest {
- public:
-  KioskDisabledFeatureFlagWithAllowlistedApp() {
-    scoped_feature_list_.InitAndDisableFeature(
-        chrome_app_deprecation::kAllowChromeAppsInKioskSessions);
-    chrome_app_deprecation::AddAppToAllowlistForTesting(
-        OfflineEnabledChromeAppV1().app_id);
-  }
-  KioskDisabledFeatureFlagWithAllowlistedApp(
-      const KioskDisabledFeatureFlagWithAllowlistedApp&) = delete;
-  KioskDisabledFeatureFlagWithAllowlistedApp& operator=(
-      const KioskDisabledFeatureFlagWithAllowlistedApp&) = delete;
-  ~KioskDisabledFeatureFlagWithAllowlistedApp() override = default;
-};
-
-IN_PROC_BROWSER_TEST_F(KioskDisabledFeatureFlagWithAllowlistedApp,
-                       AllowlistedApp) {
-  ASSERT_FALSE(base::FeatureList::IsEnabled(
-      chrome_app_deprecation::kAllowChromeAppsInKioskSessions));
-
-  ASSERT_TRUE(WaitKioskLaunched());
-}
 }  // namespace apps
diff --git a/chrome/browser/apps/app_service/publishers/chrome_app_deprecation_kiosk_browsertest.cc b/chrome/browser/apps/app_service/publishers/chrome_app_deprecation_kiosk_browsertest.cc
new file mode 100644
index 0000000..06c5353
--- /dev/null
+++ b/chrome/browser/apps/app_service/publishers/chrome_app_deprecation_kiosk_browsertest.cc
@@ -0,0 +1,183 @@
+// 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 "ash/public/cpp/login_accelerators.h"
+#include "base/task/current_thread.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "chrome/browser/apps/app_service/publishers/chrome_app_deprecation.h"
+#include "chrome/browser/ash/app_mode/kiosk_app_launch_error.h"
+#include "chrome/browser/ash/app_mode/kiosk_controller.h"
+#include "chrome/browser/ash/app_mode/test/fake_cws_chrome_apps.h"
+#include "chrome/browser/ash/app_mode/test/kiosk_mixin.h"
+#include "chrome/browser/ash/app_mode/test/kiosk_test_utils.h"
+#include "chrome/browser/ash/login/app_mode/kiosk_launch_controller.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
+#include "chrome/test/base/mixin_based_in_process_browser_test.h"
+#include "content/public/test/browser_test.h"
+
+namespace apps {
+
+using ash::KioskAppLaunchError;
+using ash::KioskMixin;
+using ash::kiosk::test::LaunchAppManually;
+using ash::kiosk::test::OfflineEnabledChromeAppV1;
+using ash::kiosk::test::WaitKioskLaunched;
+
+enum AllowChromeAppsFlag {
+  kDefault,  // by default the flag is disabled.
+  kEnabled,
+};
+
+template <AllowChromeAppsFlag flag>
+class ChromeAppDeprecationKioskSessionBrowserTest
+    : public MixinBasedInProcessBrowserTest {
+ public:
+  ChromeAppDeprecationKioskSessionBrowserTest() {
+    switch (flag) {
+      case kDefault:
+        scoped_feature_list_.InitWithEmptyFeatureAndFieldTrialLists();
+        break;
+      case kEnabled:
+        scoped_feature_list_.InitAndEnableFeature(
+            chrome_app_deprecation::kAllowChromeAppsInKioskSessions);
+        break;
+    }
+  }
+
+  ChromeAppDeprecationKioskSessionBrowserTest(
+      const ChromeAppDeprecationKioskSessionBrowserTest&) = delete;
+  ChromeAppDeprecationKioskSessionBrowserTest& operator=(
+      const ChromeAppDeprecationKioskSessionBrowserTest&) = delete;
+  ~ChromeAppDeprecationKioskSessionBrowserTest() override = default;
+
+  void WaitForChromeAppDeprecatedLaunchError() {
+    ASSERT_TRUE(base::test::RunUntil([&]() {
+      return KioskAppLaunchError::Get() ==
+             KioskAppLaunchError::Error::kChromeAppDeprecated;
+    }));
+  }
+
+  bool PressBailoutAccelerator() {
+    return ash::LoginDisplayHost::default_host()->HandleAccelerator(
+        ash::LoginAcceleratorAction::kAppLaunchBailout);
+  }
+
+  void VerifyUniqueKioskLaunchErrorMetric(KioskAppLaunchError::Error error) {
+    histogram_.ExpectUniqueSample(ash::kKioskLaunchErrorHistogram, error,
+                                  /*expected_bucket_count=*/1);
+  }
+
+ private:
+  base::HistogramTester histogram_;
+
+  KioskMixin kiosk_{
+      &mixin_host_,
+      /*cached_configuration=*/KioskMixin::Config{
+          /*name=*/{},
+          KioskMixin::AutoLaunchAccount{OfflineEnabledChromeAppV1().account_id},
+          {OfflineEnabledChromeAppV1()}}};
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+using ChromeAppDeprecationKioskDefaultFlagTest =
+    ChromeAppDeprecationKioskSessionBrowserTest<AllowChromeAppsFlag::kDefault>;
+
+IN_PROC_BROWSER_TEST_F(ChromeAppDeprecationKioskDefaultFlagTest,
+                       StuckOnSplashScreen) {
+  WaitForChromeAppDeprecatedLaunchError();
+
+  base::RunLoop().RunUntilIdle();
+
+  // Check kiosk is not started and user is stuck on the splash screen.
+  EXPECT_EQ(ash::KioskController::Get().GetKioskSystemSession(), nullptr);
+  EXPECT_TRUE(ash::KioskController::Get().IsSessionStarting());
+}
+
+// Bailout accelerator should work on the splash screen with the Chrome App
+// deprecated message.
+IN_PROC_BROWSER_TEST_F(ChromeAppDeprecationKioskDefaultFlagTest,
+                       UserCancelled) {
+  WaitForChromeAppDeprecatedLaunchError();
+  EXPECT_TRUE(ash::KioskController::Get().IsSessionStarting());
+
+  PressBailoutAccelerator();
+
+  RunUntilBrowserProcessQuits();
+  EXPECT_EQ(KioskAppLaunchError::Get(),
+            KioskAppLaunchError::Error::kUserCancel);
+  EXPECT_FALSE(ash::KioskController::Get().IsSessionStarting());
+}
+
+IN_PROC_BROWSER_TEST_F(ChromeAppDeprecationKioskDefaultFlagTest,
+                       AllowlistedAppCanBeLaunched) {
+  chrome_app_deprecation::AddAppToAllowlistForTesting(
+      OfflineEnabledChromeAppV1().app_id);
+
+  ASSERT_FALSE(base::FeatureList::IsEnabled(
+      chrome_app_deprecation::kAllowChromeAppsInKioskSessions));
+
+  ASSERT_TRUE(WaitKioskLaunched());
+}
+
+IN_PROC_BROWSER_TEST_F(ChromeAppDeprecationKioskDefaultFlagTest,
+                       PRE_CheckMetric) {
+  WaitForChromeAppDeprecatedLaunchError();
+}
+
+// Metrics are recorded on the next browser start.
+IN_PROC_BROWSER_TEST_F(ChromeAppDeprecationKioskDefaultFlagTest, CheckMetric) {
+  VerifyUniqueKioskLaunchErrorMetric(
+      KioskAppLaunchError::Error::kChromeAppDeprecated);
+}
+
+IN_PROC_BROWSER_TEST_F(ChromeAppDeprecationKioskDefaultFlagTest,
+                       PRE_CheckAutoLaunchWorks) {
+  WaitForChromeAppDeprecatedLaunchError();
+}
+
+// When Chrome App is deprecated, the auto-launch after the browser restart
+// should proceed and stuck on the same splash screen.
+IN_PROC_BROWSER_TEST_F(ChromeAppDeprecationKioskDefaultFlagTest,
+                       CheckAutoLaunchWorks) {
+  WaitForChromeAppDeprecatedLaunchError();
+
+  base::RunLoop().RunUntilIdle();
+
+  // Check kiosk is not started and user is stuck on the splash screen.
+  EXPECT_EQ(ash::KioskController::Get().GetKioskSystemSession(), nullptr);
+  EXPECT_TRUE(ash::KioskController::Get().IsSessionStarting());
+}
+
+IN_PROC_BROWSER_TEST_F(ChromeAppDeprecationKioskDefaultFlagTest,
+                       PRE_NoAutoLaunchAfterUserCancelled) {
+  WaitForChromeAppDeprecatedLaunchError();
+  EXPECT_TRUE(ash::KioskController::Get().IsSessionStarting());
+
+  PressBailoutAccelerator();
+
+  RunUntilBrowserProcessQuits();
+  EXPECT_EQ(KioskAppLaunchError::Get(),
+            KioskAppLaunchError::Error::kUserCancel);
+  EXPECT_FALSE(ash::KioskController::Get().IsSessionStarting());
+}
+
+// Keep the existing behavior -- if user cancelled kiosk launch, app should not
+// be auto-launched again.
+IN_PROC_BROWSER_TEST_F(ChromeAppDeprecationKioskDefaultFlagTest,
+                       NoAutoLaunchAfterUserCancelled) {
+  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(ash::KioskController::Get().IsSessionStarting());
+}
+
+using ChromeAppDeprecationKioskEnabledFeatureFlagTest =
+    ChromeAppDeprecationKioskSessionBrowserTest<AllowChromeAppsFlag::kEnabled>;
+
+IN_PROC_BROWSER_TEST_F(ChromeAppDeprecationKioskEnabledFeatureFlagTest,
+                       LaunchChromeApp) {
+  ASSERT_TRUE(base::FeatureList::IsEnabled(
+      chrome_app_deprecation::kAllowChromeAppsInKioskSessions));
+  ASSERT_TRUE(WaitKioskLaunched());
+}
+
+}  // namespace apps
diff --git a/chrome/browser/ash/app_mode/app_launch_utils.cc b/chrome/browser/ash/app_mode/app_launch_utils.cc
index 0b53ab2..43da4c8 100644
--- a/chrome/browser/ash/app_mode/app_launch_utils.cc
+++ b/chrome/browser/ash/app_mode/app_launch_utils.cc
@@ -47,6 +47,11 @@
 // their own list.
 std::vector<std::string>* test_prefs_to_reset = nullptr;
 
+bool ShouldAutoLaunchAfterAppLaunchError(KioskAppLaunchError::Error error) {
+  return error == KioskAppLaunchError::Error::kNone ||
+         error == KioskAppLaunchError::Error::kChromeAppDeprecated;
+}
+
 }  // namespace
 
 void ResetEphemeralKioskPreferences(PrefService* prefs) {
@@ -88,7 +93,7 @@
 
   return command_line.HasSwitch(switches::kLoginManager) &&
          KioskController::Get().GetAutoLaunchApp().has_value() &&
-         KioskAppLaunchError::Get() == KioskAppLaunchError::Error::kNone &&
+         ShouldAutoLaunchAfterAppLaunchError(KioskAppLaunchError::Get()) &&
          // IsOobeCompleted() is needed to prevent kiosk session start in case
          // of enterprise rollback, when keeping the enrollment, policy, not
          // clearing TPM, but wiping stateful partition.
diff --git a/chrome/browser/ash/app_mode/kiosk_app_launch_error.cc b/chrome/browser/ash/app_mode/kiosk_app_launch_error.cc
index 8b3e94d..c8ba4cd 100644
--- a/chrome/browser/ash/app_mode/kiosk_app_launch_error.cc
+++ b/chrome/browser/ash/app_mode/kiosk_app_launch_error.cc
@@ -33,6 +33,8 @@
 
 }  // namespace
 
+const char kKioskLaunchErrorHistogram[] = "Kiosk.Launch.Error";
+
 // static
 std::string KioskAppLaunchError::GetErrorMessage(Error error) {
   switch (error) {
@@ -132,7 +134,7 @@
 
   std::optional<int> error = dict_update->FindInt(kKeyLaunchError);
   if (error) {
-    base::UmaHistogramEnumeration("Kiosk.Launch.Error",
+    base::UmaHistogramEnumeration(kKioskLaunchErrorHistogram,
                                   static_cast<Error>(*error));
   }
 
diff --git a/chrome/browser/ash/app_mode/kiosk_app_launch_error.h b/chrome/browser/ash/app_mode/kiosk_app_launch_error.h
index 83f7886d..7c2d9fd 100644
--- a/chrome/browser/ash/app_mode/kiosk_app_launch_error.h
+++ b/chrome/browser/ash/app_mode/kiosk_app_launch_error.h
@@ -9,6 +9,8 @@
 
 namespace ash {
 
+extern const char kKioskLaunchErrorHistogram[];
+
 class AuthFailure;
 
 class KioskAppLaunchError {
diff --git a/chrome/browser/ash/login/app_mode/kiosk_launch_controller.cc b/chrome/browser/ash/login/app_mode/kiosk_launch_controller.cc
index 4c9a0a79..357b653 100644
--- a/chrome/browser/ash/login/app_mode/kiosk_launch_controller.cc
+++ b/chrome/browser/ash/login/app_mode/kiosk_launch_controller.cc
@@ -577,6 +577,13 @@
       // because that prevents re-launch on the next run.
       std::move(attempt_relaunch_).Run();
       break;
+    case Error::kChromeAppDeprecated:
+      // Keep the splash screen with the error message, do not relaunch or exit.
+      splash_screen_->UpdateAppLaunchState(
+          ash::AppLaunchSplashScreenView::AppLaunchState::kChromeAppDeprecated);
+      splash_screen_->HideThrobber();
+      KioskAppLaunchError::Save(error);
+      return;
     case Error::kHasPendingLaunch:
     case Error::kUnableToMount:
     case Error::kUnableToRemove:
@@ -590,7 +597,6 @@
     case Error::kExtensionsLoadTimeout:
     case Error::kExtensionsPolicyInvalid:
     case Error::kUserNotAllowlisted:
-    case Error::kChromeAppDeprecated:
       if (KioskLaunchController::TestOverrides::block_exit_on_failure) {
         // Don't exit on launch failure if a test checks for Kiosk splash screen
         // after launch fails, which happens to MSan browser_tests since this
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 3c78cf9c..f342f0a 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
@@ -311,8 +311,6 @@
               KioskLaunchStateToString(state));
   }
 
-  void CancelAppLaunch() { controller().HandleAccelerator(kAppLaunchBailout); }
-
   void CleanUpController() { controller().CleanUp(); }
 
   void LoginFakeUser() {
@@ -320,6 +318,12 @@
     fake_user_manager_->LoginUser(kiosk_app_id().account_id);
   }
 
+  void CheckLaunchError(KioskAppLaunchError::Error error) {
+    const base::Value::Dict& dict =
+        g_browser_process->local_state()->GetDict("kiosk");
+    EXPECT_THAT(dict.FindInt("launch_error"), Eq(static_cast<int>(error)));
+  }
+
   auto& app_launched_future() { return app_launched_future_; }
 
   auto& launch_done_future() { return launch_done_future_; }
@@ -444,6 +448,7 @@
       screen(),
       HasViewState(
           AppLaunchSplashScreenView::AppLaunchState::kInstallingApplication));
+  EXPECT_TRUE(screen().IsThrobberVisible());
 }
 
 TEST_F(KioskLaunchControllerTest, AppPreparedShouldUpdateInternalState) {
@@ -695,10 +700,7 @@
   FinishLoadingProfileWithError(KioskAppLaunchError::Error::kUnableToMount);
   VerifyLaunchStateCrashKey(KioskLaunchState::kLaunchFailed);
 
-  const base::Value::Dict& dict =
-      g_browser_process->local_state()->GetDict("kiosk");
-  EXPECT_THAT(dict.FindInt("launch_error"),
-              Eq(static_cast<int>(KioskAppLaunchError::Error::kUnableToMount)));
+  CheckLaunchError(KioskAppLaunchError::Error::kUnableToMount);
 }
 
 TEST_F(KioskLaunchControllerTest,
@@ -977,4 +979,24 @@
   EXPECT_EQ(num_launchers_created(), 1);
 }
 
+TEST_F(KioskLaunchControllerTest,
+       ChromeAppDeprecatedCheckKeepSplashScreenMessage) {
+  controller().Start(kiosk_app(), /*auto_launch=*/false);
+  FinishLoadingProfile();
+
+  launcher().observers().NotifyLaunchFailed(
+      KioskAppLaunchError::Error::kChromeAppDeprecated);
+
+  VerifyLaunchStateCrashKey(KioskLaunchState::kLaunchFailed);
+
+  EXPECT_THAT(
+      screen(),
+      HasViewState(
+          AppLaunchSplashScreenView::AppLaunchState::kChromeAppDeprecated));
+  EXPECT_FALSE(screen().IsThrobberVisible());
+
+  task_environment()->FastForwardBy(base::Minutes(2));
+  EXPECT_FALSE(launcher().HasAppLaunched());
+}
+
 }  // namespace ash
diff --git a/chrome/browser/ash/login/screens/app_launch_splash_screen.cc b/chrome/browser/ash/login/screens/app_launch_splash_screen.cc
index 94c8b8d..93a85eb2 100644
--- a/chrome/browser/ash/login/screens/app_launch_splash_screen.cc
+++ b/chrome/browser/ash/login/screens/app_launch_splash_screen.cc
@@ -160,6 +160,12 @@
   view_->ToggleNetworkConfig(visible);
 }
 
+void AppLaunchSplashScreen::HideThrobber() {
+  if (view_) {
+    view_->HideThrobber();
+  }
+}
+
 void AppLaunchSplashScreen::SetDelegate(Delegate* delegate) {
   delegate_ = delegate;
 }
diff --git a/chrome/browser/ash/login/screens/app_launch_splash_screen.h b/chrome/browser/ash/login/screens/app_launch_splash_screen.h
index d66f9d4..6432630 100644
--- a/chrome/browser/ash/login/screens/app_launch_splash_screen.h
+++ b/chrome/browser/ash/login/screens/app_launch_splash_screen.h
@@ -70,6 +70,8 @@
   // Sets whether configure network control is visible.
   void ToggleNetworkConfig(bool visible);
 
+  virtual void HideThrobber();
+
   // Continues app launch after error screen is shown.
   virtual void ContinueAppLaunch();
 
diff --git a/chrome/browser/ash/login/screens/fake_app_launch_splash_screen.cc b/chrome/browser/ash/login/screens/fake_app_launch_splash_screen.cc
index 47fd78e9..cf7ef6b 100644
--- a/chrome/browser/ash/login/screens/fake_app_launch_splash_screen.cc
+++ b/chrome/browser/ash/login/screens/fake_app_launch_splash_screen.cc
@@ -42,6 +42,14 @@
   launch_error_ = error;
 }
 
+void FakeAppLaunchSplashScreen::HideThrobber() {
+  show_throbber_ = false;
+}
+
+bool FakeAppLaunchSplashScreen::IsThrobberVisible() {
+  return show_throbber_;
+}
+
 KioskAppLaunchError::Error FakeAppLaunchSplashScreen::GetLaunchError() const {
   return launch_error_;
 }
diff --git a/chrome/browser/ash/login/screens/fake_app_launch_splash_screen.h b/chrome/browser/ash/login/screens/fake_app_launch_splash_screen.h
index fd711732..3e5255b 100644
--- a/chrome/browser/ash/login/screens/fake_app_launch_splash_screen.h
+++ b/chrome/browser/ash/login/screens/fake_app_launch_splash_screen.h
@@ -27,6 +27,9 @@
                               const std::string& network_name) override;
   void ContinueAppLaunch() override;
   void ShowErrorMessage(KioskAppLaunchError::Error error) override;
+  void HideThrobber() override;
+
+  bool IsThrobberVisible();
 
   // Returns the app launch error last passed to `ShowErrorMessage`.
   KioskAppLaunchError::Error GetLaunchError() const;
@@ -41,6 +44,7 @@
  private:
   // The launch error last passed to `ShowErrorMessage`.
   KioskAppLaunchError::Error launch_error_ = KioskAppLaunchError::Error::kNone;
+  bool show_throbber_ = true;
 };
 
 }  // namespace ash
diff --git a/chrome/browser/ash/printing/printer_configurer.cc b/chrome/browser/ash/printing/printer_configurer.cc
index b196c6e..077d92d 100644
--- a/chrome/browser/ash/printing/printer_configurer.cc
+++ b/chrome/browser/ash/printing/printer_configurer.cc
@@ -16,7 +16,6 @@
 #include "base/feature_list.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
-#include "base/hash/md5.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/raw_ptr.h"
@@ -36,11 +35,19 @@
 #include "chromeos/printing/printer_configuration.h"
 #include "components/device_event_log/device_event_log.h"
 #include "content/public/browser/browser_thread.h"
+#include "crypto/obsolete/md5.h"
 #include "printing/printing_features.h"
 #include "third_party/cros_system_api/dbus/debugd/dbus-constants.h"
 
 namespace ash {
 
+namespace printing {
+// Not placed in namespace {} so it can be friended from //crypto.
+crypto::obsolete::Md5 MakeMd5HasherForPrinterConfigurer() {
+  return {};
+}
+}  // namespace printing
+
 namespace {
 
 using ::chromeos::PpdProvider;
@@ -239,7 +246,7 @@
                        << printer.uri().GetNormalized(
                               /*always_print_port=*/true);
     if (base::FeatureList::IsEnabled(
-            printing::features::kAddPrinterViaPrintscanmgr)) {
+            ::printing::features::kAddPrinterViaPrintscanmgr)) {
       printscanmgr::CupsAddAutoConfiguredPrinterRequest request;
       request.set_name(printer.id());
       request.set_uri(printer.uri().GetNormalized(/*always_print_port=*/true));
@@ -305,7 +312,7 @@
                        << printer.uri().GetNormalized(
                               /*always_print_port=*/true);
     if (base::FeatureList::IsEnabled(
-            printing::features::kAddPrinterViaPrintscanmgr)) {
+            ::printing::features::kAddPrinterViaPrintscanmgr)) {
       printscanmgr::CupsAddManuallyConfiguredPrinterRequest request;
       request.set_name(printer.id());
       request.set_uri(printer.uri().GetNormalized(/*always_print_port=*/true));
@@ -447,17 +454,14 @@
 
 // static
 std::string PrinterConfigurer::SetupFingerprint(const Printer& printer) {
-  base::MD5Context ctx;
-  base::MD5Init(&ctx);
-  base::MD5Update(&ctx, printer.id());
-  base::MD5Update(&ctx, printer.uri().GetNormalized(false));
-  base::MD5Update(&ctx, printer.ppd_reference().user_supplied_ppd_url);
-  base::MD5Update(&ctx, printer.ppd_reference().effective_make_and_model);
+  auto md5 = ash::printing::MakeMd5HasherForPrinterConfigurer();
+  md5.Update(printer.id());
+  md5.Update(printer.uri().GetNormalized(false));
+  md5.Update(printer.ppd_reference().user_supplied_ppd_url);
+  md5.Update(printer.ppd_reference().effective_make_and_model);
   char autoconf = printer.ppd_reference().autoconf ? 1 : 0;
-  base::MD5Update(&ctx, std::string(&autoconf, sizeof(autoconf)));
-  base::MD5Digest digest;
-  base::MD5Final(&digest, &ctx);
-  return std::string(reinterpret_cast<char*>(&digest.a[0]), sizeof(digest.a));
+  md5.Update(base::byte_span_from_ref(autoconf));
+  return std::string(base::as_string_view(md5.Finish()));
 }
 
 // static
diff --git a/chrome/browser/ash/printing/server_printers_fetcher.cc b/chrome/browser/ash/printing/server_printers_fetcher.cc
index 8b57846..ee02dccf 100644
--- a/chrome/browser/ash/printing/server_printers_fetcher.cc
+++ b/chrome/browser/ash/printing/server_printers_fetcher.cc
@@ -8,7 +8,6 @@
 #include <string_view>
 #include <utility>
 
-#include "base/hash/md5.h"
 #include "base/logging.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/scoped_refptr.h"
@@ -20,6 +19,7 @@
 #include "chrome/browser/net/system_network_context_manager.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/device_event_log/device_event_log.h"
+#include "crypto/obsolete/md5.h"
 #include "net/base/load_flags.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "services/network/public/cpp/resource_request.h"
@@ -33,6 +33,16 @@
 
 namespace ash {
 
+namespace printing {
+
+// Not in namespace {} so it can be friended by crypto/obsolete/md5.
+std::string ServerPrinterId(const std::string& url) {
+  return "server-" +
+         base::ToLowerASCII(base::HexEncode(crypto::obsolete::Md5::Hash(url)));
+}
+
+}  // namespace printing
+
 namespace {
 
 constexpr net::NetworkTrafficAnnotationTag kServerPrintersFetcherNetworkTag =
@@ -61,15 +71,6 @@
       }
     })");
 
-std::string ServerPrinterId(const std::string& url) {
-  base::MD5Context ctx;
-  base::MD5Init(&ctx);
-  base::MD5Update(&ctx, url);
-  base::MD5Digest digest;
-  base::MD5Final(&digest, &ctx);
-  return "server-" + base::MD5DigestToBase16(digest);
-}
-
 }  // namespace
 
 class ServerPrintersFetcher::PrivateImplementation
@@ -256,7 +257,7 @@
     // Complete building the printer's URI.
     url.SetPath({"printers", name});
     printer->SetUri(url);
-    printer->set_id(ServerPrinterId(url.GetNormalized()));
+    printer->set_id(printing::ServerPrinterId(url.GetNormalized()));
   }
 
   raw_ptr<const ServerPrintersFetcher, DanglingUntriaged> owner_;
diff --git a/chrome/browser/ash/printing/usb_printer_util.cc b/chrome/browser/ash/printing/usb_printer_util.cc
index a8d302b..8b21fa5d 100644
--- a/chrome/browser/ash/printing/usb_printer_util.cc
+++ b/chrome/browser/ash/printing/usb_printer_util.cc
@@ -17,7 +17,6 @@
 #include "base/containers/fixed_flat_set.h"
 #include "base/containers/span.h"
 #include "base/functional/callback_helpers.h"
-#include "base/hash/md5.h"
 #include "base/numerics/byte_conversions.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/strings/strcat.h"
@@ -28,6 +27,7 @@
 #include "chromeos/printing/printer_configuration.h"
 #include "chromeos/printing/usb_printer_id.h"
 #include "components/device_event_log/device_event_log.h"
+#include "crypto/obsolete/md5.h"
 #include "services/device/public/cpp/usb/usb_utils.h"
 #include "services/device/public/mojom/usb_device.mojom.h"
 #include "services/device/public/mojom/usb_enumeration_options.mojom.h"
@@ -35,6 +35,13 @@
 #include "ui/base/l10n/l10n_util.h"
 
 namespace ash {
+
+namespace printing {
+crypto::obsolete::Md5 MakeMd5HasherForUsbPrinterUtil() {
+  return {};
+}
+}  // namespace printing
+
 namespace {
 
 using device::mojom::UsbDeviceInfo;
@@ -164,16 +171,16 @@
                                             target, std::move(cb)));
 }
 
-// Incorporate the bytes of |val| into the incremental hash carried in |ctx| in
-// big-endian order.  |val| must be a simple integer type
-void MD5UpdateU8BigEndian(base::MD5Context* ctx,
-                          base::StrictNumeric<uint8_t> val) {
+// Incorporate the byte |val| into the incremental hash carried in |md5|.
+void Md5UpdateU8(crypto::obsolete::Md5& md5, base::StrictNumeric<uint8_t> val) {
   uint8_t tmp = val;
-  base::MD5Update(ctx, base::span_from_ref(tmp));
+  md5.Update(base::span_from_ref(tmp));
 }
-void MD5UpdateU16BigEndian(base::MD5Context* ctx,
+
+// Incorporate |val| into |md5| as a big-endian value.
+void Md5UpdateU16BigEndian(crypto::obsolete::Md5& md5,
                            base::StrictNumeric<uint16_t> val) {
-  base::MD5Update(ctx, base::U16ToBigEndian(val));
+  md5.Update(base::U16ToBigEndian(val));
 }
 
 // Update the hash with the contents of |str|.
@@ -184,8 +191,8 @@
 //
 // This is a long way to say "UTF-16 is hard to hash, let's just convert
 // to UTF-8 and hash that", which avoids all of these issues.
-void MD5UpdateString16(base::MD5Context* ctx, const std::u16string& str) {
-  base::MD5Update(ctx, base::UTF16ToUTF8(str));
+void Md5UpdateString16(crypto::obsolete::Md5& md5, const std::u16string& str) {
+  md5.Update(base::UTF16ToUTF8(str));
 }
 
 // Get the usb printer id for |device|.  This is used both as the identifier for
@@ -209,20 +216,18 @@
   static_assert(sizeof(device::GetDeviceVersion(device_info)) == 2,
                 "Version size changed");
 
-  base::MD5Context ctx;
-  base::MD5Init(&ctx);
-  MD5UpdateU8BigEndian(&ctx, device_info.class_code);
-  MD5UpdateU8BigEndian(&ctx, device_info.subclass_code);
-  MD5UpdateU8BigEndian(&ctx, device_info.protocol_code);
-  MD5UpdateU16BigEndian(&ctx, device_info.vendor_id);
-  MD5UpdateU16BigEndian(&ctx, device_info.product_id);
-  MD5UpdateU16BigEndian(&ctx, device::GetDeviceVersion(device_info));
-  base::MD5Update(&ctx, GetManufacturerName(device_info));
-  base::MD5Update(&ctx, GetProductName(device_info));
-  MD5UpdateString16(&ctx, GetSerialNumber(device_info));
-  base::MD5Digest digest;
-  base::MD5Final(&digest, &ctx);
-  return base::StringPrintf("usb-%s", base::MD5DigestToBase16(digest).c_str());
+  auto md5 = ash::printing::MakeMd5HasherForUsbPrinterUtil();
+  Md5UpdateU8(md5, device_info.class_code);
+  Md5UpdateU8(md5, device_info.subclass_code);
+  Md5UpdateU8(md5, device_info.protocol_code);
+  Md5UpdateU16BigEndian(md5, device_info.vendor_id);
+  Md5UpdateU16BigEndian(md5, device_info.product_id);
+  Md5UpdateU16BigEndian(md5, device::GetDeviceVersion(device_info));
+  md5.Update(GetManufacturerName(device_info));
+  md5.Update(GetProductName(device_info));
+  Md5UpdateString16(md5, GetSerialNumber(device_info));
+  return base::StringPrintf("usb-%s",
+                            base::ToLowerASCII(base::HexEncode(md5.Finish())));
 }
 
 // Creates a mojom filter which can be used to identify a basic USB printer.
diff --git a/chrome/browser/ash/printing/zeroconf_printer_detector.cc b/chrome/browser/ash/printing/zeroconf_printer_detector.cc
index e833249..e79ade5 100644
--- a/chrome/browser/ash/printing/zeroconf_printer_detector.cc
+++ b/chrome/browser/ash/printing/zeroconf_printer_detector.cc
@@ -17,7 +17,6 @@
 
 #include "base/containers/contains.h"
 #include "base/containers/fixed_flat_set.h"
-#include "base/hash/md5.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
@@ -26,9 +25,16 @@
 #include "chrome/browser/local_discovery/service_discovery_shared_client.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/device_event_log/device_event_log.h"
+#include "crypto/obsolete/md5.h"
 
 namespace ash {
 
+namespace printing {
+crypto::obsolete::Md5 MakeMd5HasherForZeroconf() {
+  return {};
+}
+}  // namespace printing
+
 // Supported service names for printers.
 const char ZeroconfPrinterDetector::kIppServiceName[] = "_ipp._tcp.local";
 const char ZeroconfPrinterDetector::kIppsServiceName[] = "_ipps._tcp.local";
@@ -144,19 +150,16 @@
 // all to be considered the same printer.
 std::string ZeroconfPrinterId(const ServiceDescription& service,
                               const ParsedMetadata& metadata) {
-  base::MD5Context ctx;
-  base::MD5Init(&ctx);
-  base::MD5Update(&ctx, service.instance_name());
-  base::MD5Update(&ctx, metadata.product);
-  base::MD5Update(&ctx, metadata.UUID);
-  base::MD5Update(&ctx, metadata.usb_MFG);
-  base::MD5Update(&ctx, metadata.usb_MDL);
-  base::MD5Update(&ctx, metadata.ty);
-  base::MD5Update(&ctx, metadata.rp);
-  base::MD5Digest digest;
-  base::MD5Final(&digest, &ctx);
+  auto md5 = ash::printing::MakeMd5HasherForZeroconf();
+  md5.Update(service.instance_name());
+  md5.Update(metadata.product);
+  md5.Update(metadata.UUID);
+  md5.Update(metadata.usb_MFG);
+  md5.Update(metadata.usb_MDL);
+  md5.Update(metadata.ty);
+  md5.Update(metadata.rp);
   return base::StringPrintf("zeroconf-%s",
-                            base::MD5DigestToBase16(digest).c_str());
+                            base::ToLowerASCII(base::HexEncode(md5.Finish())));
 }
 
 // Attempt to fill |detected_printer| using the information in
diff --git a/chrome/browser/auxiliary_search/junit/src/org/chromium/chrome/browser/auxiliary_search/module/AuxiliarySearchModuleBuilderUnitTest.java b/chrome/browser/auxiliary_search/junit/src/org/chromium/chrome/browser/auxiliary_search/module/AuxiliarySearchModuleBuilderUnitTest.java
index 97809882..e32b7ac 100644
--- a/chrome/browser/auxiliary_search/junit/src/org/chromium/chrome/browser/auxiliary_search/module/AuxiliarySearchModuleBuilderUnitTest.java
+++ b/chrome/browser/auxiliary_search/junit/src/org/chromium/chrome/browser/auxiliary_search/module/AuxiliarySearchModuleBuilderUnitTest.java
@@ -142,8 +142,7 @@
     @DisableFeatures({ChromeFeatureList.ANDROID_APP_INTEGRATION_MODULE})
     public void testCreateInputContext() {
         InputContext inputContext = mBuilder.createInputContext();
-        assertEquals(
-                0f, inputContext.getEntryForTesting("auxiliary_search_available").floatValue, 0.01);
+        assertEquals(0f, inputContext.getEntryValue("auxiliary_search_available").floatValue, 0.01);
     }
 
     @Test
@@ -151,7 +150,6 @@
     public void testCreateInputContext_Enabled() {
         AuxiliarySearchModuleBuilder.resetShownInThisSessionForTesting();
         InputContext inputContext = mBuilder.createInputContext();
-        assertEquals(
-                1f, inputContext.getEntryForTesting("auxiliary_search_available").floatValue, 0.01);
+        assertEquals(1f, inputContext.getEntryValue("auxiliary_search_available").floatValue, 0.01);
     }
 }
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
index 7593ff5f..2b9e8c2 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
@@ -122,6 +122,8 @@
 #include "components/password_manager/core/browser/password_manager_metrics_util.h"
 #include "components/password_manager/core/browser/password_store/password_store_interface.h"
 #include "components/password_manager/core/browser/password_store/smart_bubble_stats_store.h"
+#include "components/payments/content/browser_binding/browser_bound_keys_deleter.h"
+#include "components/payments/content/browser_binding/browser_bound_keys_deleter_factory.h"
 #include "components/payments/content/payment_manifest_web_data_service.h"
 #include "components/performance_manager/public/user_tuning/prefs.h"
 #include "components/permissions/permission_actions_history.h"
@@ -753,6 +755,14 @@
           base::DoNothing());
     }
 #endif  // !BUILDFLAG(IS_ANDROID)
+
+#if BUILDFLAG(IS_ANDROID)
+    if (payments::BrowserBoundKeyDeleter* browser_bound_key_deleter =
+            payments::BrowserBoundKeyDeleterFactory::GetForBrowserContext(
+                profile_)) {
+      browser_bound_key_deleter->RemoveInvalidBBKs();
+    }
+#endif  // BUILDFLAG(IS_ANDROID)
   }
 
   //////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
index ff59a5f..9d869bc 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
@@ -217,6 +217,8 @@
 #include "chrome/browser/ui/android/tab_model/tab_model_list.h"
 #include "chrome/browser/ui/android/tab_model/tab_model_test_helper.h"
 #include "components/password_manager/core/browser/split_stores_and_local_upm.h"
+#include "components/payments/content/browser_binding/browser_bound_keys_deleter_factory.h"
+#include "components/payments/content/browser_binding/mock_browser_bound_keys_deleter.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #else
 #include "base/task/current_thread.h"
@@ -4460,6 +4462,28 @@
 }
 #endif  // !BUILDFLAG(IS_ANDROID)
 
+#if BUILDFLAG(IS_ANDROID)
+// Verify that clearing cookies will also trigger removing invalid browser bound
+// keys.
+TEST_F(ChromeBrowsingDataRemoverDelegateTest,
+       ClearInvalidBrowserBoundKeysForSecurePaymentConfirmation) {
+  auto* mock_browser_bound_keys_deleter = static_cast<
+      payments::MockBrowserBoundKeyDeleter*>(
+      payments::BrowserBoundKeyDeleterFactory::GetInstance()
+          ->SetTestingFactoryAndUse(
+              GetProfile(),
+              base::BindOnce([](content::BrowserContext*)
+                                 -> std::unique_ptr<KeyedService> {
+                return std::make_unique<payments::MockBrowserBoundKeyDeleter>();
+              })));
+
+  EXPECT_CALL(*mock_browser_bound_keys_deleter, RemoveInvalidBBKs());
+  BlockUntilBrowsingDataRemoved(base::Time(), base::Time::Max(),
+                                content::BrowsingDataRemover::DATA_TYPE_COOKIES,
+                                false);
+}
+#endif  // BUILDFLAG(IS_ANDROID)
+
 class ChromeBrowsingDataRemoverDelegateOriginTrialsTest
     : public ChromeBrowsingDataRemoverDelegateTest {
  public:
diff --git a/chrome/browser/btm/BUILD.gn b/chrome/browser/btm/BUILD.gn
index a18c059c..f6782abe 100644
--- a/chrome/browser/btm/BUILD.gn
+++ b/chrome/browser/btm/BUILD.gn
@@ -4,13 +4,18 @@
 
 source_set("unit_tests") {
   testonly = true
-  sources = [ "btm_browser_signin_detector_unittest.cc" ]
+  sources = [
+    "btm_browser_signin_detector_unittest.cc",
+    "btm_service_unittest.cc",
+  ]
 
   deps = [
     "//base",
     "//base/test:test_support",
     "//chrome/browser",
+    "//chrome/browser/content_settings:content_settings_factory",
     "//chrome/test:test_support",
+    "//components/privacy_sandbox:tracking_protection_prefs",
     "//content/test:test_support",
     "//testing/gtest",
     "//url",
diff --git a/chrome/browser/btm/btm_service_unittest.cc b/chrome/browser/btm/btm_service_unittest.cc
new file mode 100644
index 0000000..d819b9c3
--- /dev/null
+++ b/chrome/browser/btm/btm_service_unittest.cc
@@ -0,0 +1,102 @@
+// 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/public/browser/btm_service.h"
+
+#include "base/memory/raw_ptr.h"
+#include "chrome/browser/content_settings/cookie_settings_factory.h"
+#include "chrome/common/webui_url_constants.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/content_settings/core/browser/cookie_settings.h"
+#include "components/prefs/pref_service.h"
+#include "components/privacy_sandbox/tracking_protection_prefs.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/content_browser_client.h"
+#include "content/public/common/content_client.h"
+#include "content/public/test/browser_task_environment.h"
+#include "content/public/test/btm_service_test_utils.h"
+#include "net/http/http_status_code.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+bool Has3pcException(BrowserContext* browser_context,
+                     WebContents* web_contents,
+                     const GURL& url,
+                     const GURL& initial_url,
+                     const GURL& final_url) {
+  BtmRedirectInfoPtr redirect = BtmRedirectInfo::CreateForServer(
+      UrlAndSourceId(url, ukm::kInvalidSourceId), BtmDataAccessType::kWrite,
+      base::Time::Now(), false, net::HTTP_FOUND, base::TimeDelta());
+  Populate3PcExceptions(browser_context, web_contents, initial_url, final_url,
+                        base::span_from_ref(redirect));
+  return redirect->has_3pc_exception.value();
+}
+
+class BtmService3pcExceptionsTest : public testing::Test {
+ public:
+  BtmService3pcExceptionsTest()
+      : cookie_settings_(
+            CookieSettingsFactory::GetForProfile(&profile_).get()) {}
+
+  TestingProfile* GetProfile() { return &profile_; }
+
+ protected:
+  BrowserTaskEnvironment task_environment_;
+  TestingProfile profile_;
+  raw_ptr<content_settings::CookieSettings> cookie_settings_;
+
+  void SetUp() override {
+    GetProfile()->GetPrefs()->SetBoolean(prefs::kTrackingProtection3pcdEnabled,
+                                         true);
+    ASSERT_FALSE(Are3PcsGenerallyEnabled());
+  }
+
+  // Add an exception to the third-party cookie blocking rule for
+  // `third_party_url` embedded by `first_party_url`.
+  void Add3PCException(const GURL& first_party_url,
+                       const GURL& third_party_url) {
+    cookie_settings_->SetTemporaryCookieGrantForHeuristic(
+        third_party_url, first_party_url, base::Days(1),
+        /*use_schemeless_patterns=*/false);
+
+    auto* client = GetContentClientForTesting()->browser();
+    ASSERT_TRUE(client->IsFullCookieAccessAllowed(
+        &profile_, nullptr, third_party_url,
+        blink::StorageKey::CreateFirstParty(
+            url::Origin::Create(first_party_url)),
+        /*overrides=*/{}));
+    ASSERT_FALSE(client->IsFullCookieAccessAllowed(
+        &profile_, nullptr, first_party_url,
+        blink::StorageKey::CreateFirstParty(
+            url::Origin::Create(third_party_url)),
+        /*overrides=*/{}));
+  }
+
+  bool Are3PcsGenerallyEnabled() {
+    return ::content::Are3PcsGenerallyEnabled(&profile_, nullptr);
+  }
+};
+
+// Verifies that redirect chains that start or end on embedder URLs do not
+// have 3PC exceptions.
+TEST_F(BtmService3pcExceptionsTest, EmbedderUrl_DoesNotHaveException) {
+  GURL non_web_url(chrome::kChromeUINewTabURL);
+  GURL redirect_url("https://redirect.com");
+
+  EXPECT_FALSE(Has3pcException(GetProfile(), nullptr, redirect_url, non_web_url,
+                               GURL("https://final.com")));
+  EXPECT_FALSE(Has3pcException(GetProfile(), nullptr, redirect_url,
+                               GURL("https://initial.com"), non_web_url));
+
+  // Verify that exception logic is working as expected for HTTP(S) URLs.
+  EXPECT_FALSE(Has3pcException(GetProfile(), nullptr, redirect_url,
+                               GURL("https://initial.com"),
+                               GURL("https://final.com")));
+  Add3PCException(GURL("https://initial.com"), redirect_url);
+  EXPECT_TRUE(Has3pcException(GetProfile(), nullptr, redirect_url,
+                              GURL("https://initial.com"),
+                              GURL("https://final.com")));
+}
+
+}  // namespace content
diff --git a/chrome/browser/chrome_browser_main_win.cc b/chrome/browser/chrome_browser_main_win.cc
index 6593de6..228ad52a 100644
--- a/chrome/browser/chrome_browser_main_win.cc
+++ b/chrome/browser/chrome_browser_main_win.cc
@@ -952,34 +952,31 @@
       &ChromeBrowserMainPartsWin::OnModuleEvent, base::Unretained(this)));
 }
 
+// Check if the browser process is launching elevated, and attempt to
+// automatically de-elevate.
 std::optional<int> ChromeBrowserMainPartsWin::MaybeAutoDeElevate() {
-  const char* const kNoRestartSwitches[] = {
-      // Automation might want to launch Chrome elevated.
-      switches::kEnableAutomation,
-      // Never attempt to de-elevate a second time.
-      switches::kDoNotDeElevateOnLaunch,
-      // Chrome is launched elevated in system-level installs to prompt the
-      // user, see `DoUninstallTasks`.
-      // TODO(crbug.com/418159641): Investigate whether this stage of uninstall
-      // could be run de-elevated.
-      switches::kUninstall};
-
-  // Check if the browser process is launching elevated, and attempt to
-  // automatically de-elevate. Do not interfere with automation scenarios. Don't
-  // bother trying when UAC is disabled because it won't work anyway.
   if (!base::FeatureList::IsEnabled(features::kAutoDeElevate)) {
     return std::nullopt;
   }
 
+  // Don't bother trying when UAC is disabled because it won't work anyway.
   if (!base::win::UserAccountIsUnnecessarilyElevated()) {
     return std::nullopt;
   }
 
-  for (const auto* do_not_restart_switch : kNoRestartSwitches) {
-    if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-            do_not_restart_switch)) {
-      return std::nullopt;
-    }
+  const char* const kNoRestartSwitches[] = {
+      // Do not interfere with automation scenarios, which might want to launch
+      // Chrome elevated.
+      switches::kEnableAutomation,
+      // Never attempt to de-elevate a second time.
+      switches::kDoNotDeElevateOnLaunch};
+  if (std::ranges::any_of(
+          kNoRestartSwitches,
+          [command_line = base::CommandLine::ForCurrentProcess()](
+              const char* no_restart_switch) {
+            return command_line->HasSwitch(no_restart_switch);
+          })) {
+    return std::nullopt;
   }
 
   base::CommandLine new_command_line(*base::CommandLine::ForCurrentProcess());
diff --git a/chrome/browser/chromeos/app_mode/chrome_kiosk_app_launcher.cc b/chrome/browser/chromeos/app_mode/chrome_kiosk_app_launcher.cc
index 359a136..c8b5f03 100644
--- a/chrome/browser/chromeos/app_mode/chrome_kiosk_app_launcher.cc
+++ b/chrome/browser/chromeos/app_mode/chrome_kiosk_app_launcher.cc
@@ -63,6 +63,7 @@
   if (apps::chrome_app_deprecation::HandleDeprecation(primary_app->id(),
                                                       profile_) ==
       apps::chrome_app_deprecation::DeprecationStatus::kLaunchBlocked) {
+    SYSLOG(WARNING) << "Kiosk Chrome app is deprecated";
     ReportLaunchFailure(LaunchResult::kChromeAppDeprecated);
     return;
   }
diff --git a/chrome/browser/compose/chrome_compose_client.cc b/chrome/browser/compose/chrome_compose_client.cc
index 8a8d85f..75195fe 100644
--- a/chrome/browser/compose/chrome_compose_client.cc
+++ b/chrome/browser/compose/chrome_compose_client.cc
@@ -801,10 +801,7 @@
       break;
   }
 
-  chrome::ShowSettingsSubPage(
-      browser, optimization_guide::features::IsAiSettingsPageRefreshEnabled()
-                   ? chrome::kAiHelpMeWriteSubpage
-                   : chrome::kOfferWritingHelpSubpage);
+  chrome::ShowSettingsSubPage(browser, chrome::kAiHelpMeWriteSubpage);
 }
 
 void ChromeComposeClient::AddSiteToNeverPromptList(const url::Origin& origin) {
diff --git a/chrome/browser/compose/chrome_compose_client_unittest.cc b/chrome/browser/compose/chrome_compose_client_unittest.cc
index 196ecfb..1b802321 100644
--- a/chrome/browser/compose/chrome_compose_client_unittest.cc
+++ b/chrome/browser/compose/chrome_compose_client_unittest.cc
@@ -166,7 +166,7 @@
     scoped_feature_list_.InitWithFeatures(
         {compose::features::kEnableCompose,
          optimization_guide::features::kOptimizationGuideModelExecution},
-        {optimization_guide::features::kAiSettingsPageRefresh});
+        {});
     // Needed for feature params to reset.
     compose::ResetConfigForTesting();
     ukm_recorder_ = std::make_unique<ukm::TestAutoSetUkmRecorder>();
@@ -2384,9 +2384,13 @@
             new_tab_webcontents->GetController().GetPendingEntry()->GetURL());
 }
 
-// TODO(crbug.com/362225975): Remove after AiSettingsPageRefresh and
-// ComposeProactiveNudge are launched.
+// TODO(crbug.com/400504728): Remove after ComposeProactiveNudge is launched.
 TEST_F(ChromeComposeClientTest, LearnMoreLinkOpensCorrectURL) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitWithFeatures(
+      {compose::features::kEnableCompose},
+      {compose::features::kEnableComposeProactiveNudge});
+
   GURL learn_more_url("https://support.google.com/chrome?p=help_me_write");
 
   ShowDialogAndBindMojo();
diff --git a/chrome/browser/compose/compose_session.cc b/chrome/browser/compose/compose_session.cc
index 7e34cd3..a1a104f 100644
--- a/chrome/browser/compose/compose_session.cc
+++ b/chrome/browser/compose/compose_session.cc
@@ -936,8 +936,7 @@
 }
 
 void ComposeSession::OpenComposeLearnMorePage() {
-  if (optimization_guide::features::IsAiSettingsPageRefreshEnabled() &&
-      base::FeatureList::IsEnabled(
+  if (base::FeatureList::IsEnabled(
           compose::features::kEnableComposeProactiveNudge)) {
     Browser* browser = chrome::FindBrowserWithTab(web_contents_);
     CHECK(browser);
diff --git a/chrome/browser/devtools/devtools_ui_bindings.cc b/chrome/browser/devtools/devtools_ui_bindings.cc
index edc907a..127ea69 100644
--- a/chrome/browser/devtools/devtools_ui_bindings.cc
+++ b/chrome/browser/devtools/devtools_ui_bindings.cc
@@ -1873,6 +1873,12 @@
   response_dict.Set("devToolsAiGeneratedTimelineLabels",
                     std::move(ai_generated_timeline_labels_dict));
 
+  base::Value::Dict flexible_layout_dict;
+  flexible_layout_dict.Set(
+      "verticalalDrawerEnabled",
+      base::FeatureList::IsEnabled(::features::kDevToolsVerticalDrawer));
+  response_dict.Set("devToolsFlexibleLayout", std::move(flexible_layout_dict));
+
   base::Value response = base::Value(std::move(response_dict));
   std::move(callback).Run(&response);
 }
diff --git a/chrome/browser/devtools/features.cc b/chrome/browser/devtools/features.cc
index e1841dc..81c050a 100644
--- a/chrome/browser/devtools/features.cc
+++ b/chrome/browser/devtools/features.cc
@@ -177,4 +177,9 @@
 );
 #endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
 
+// Whether DevTools drawer can be toggled to vertical orientation.
+BASE_FEATURE(kDevToolsVerticalDrawer,
+             "DevToolsVerticalDrawer",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 }  // namespace features
diff --git a/chrome/browser/devtools/features.h b/chrome/browser/devtools/features.h
index ff42c3b..b1bd5e4 100644
--- a/chrome/browser/devtools/features.h
+++ b/chrome/browser/devtools/features.h
@@ -96,6 +96,8 @@
 BASE_DECLARE_FEATURE(kDevToolsDebuggingRestrictions);
 #endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
 
+BASE_DECLARE_FEATURE(kDevToolsVerticalDrawer);
+
 }  // namespace features
 
 #endif  // CHROME_BROWSER_DEVTOOLS_FEATURES_H_
diff --git a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/EducationalTipCardProviderSignalHandler.java b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/EducationalTipCardProviderSignalHandler.java
index 6f0bfe4f..c684aa2b 100644
--- a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/EducationalTipCardProviderSignalHandler.java
+++ b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/EducationalTipCardProviderSignalHandler.java
@@ -40,6 +40,8 @@
             Profile profile,
             Tracker tracker) {
         InputContext inputContext = new InputContext();
+        inputContext.addEntry(
+                "is_user_signed_in", ProcessedValue.fromFloat(isUserSignedIn(profile)));
         switch (moduleType) {
             case ModuleType.DEFAULT_BROWSER_PROMO:
                 inputContext.addEntry(
@@ -163,4 +165,14 @@
 
         return 0.0f;
     }
+
+    /** Returns a value of 1.0f if the user has signed in. Otherwise, it returns 0.0f. */
+    private static float isUserSignedIn(Profile profile) {
+        if (assumeNonNull(IdentityServicesProvider.get().getIdentityManager(profile))
+                .hasPrimaryAccount(ConsentLevel.SIGNIN)) {
+            return 1.0f;
+        }
+
+        return 0.0f;
+    }
 }
diff --git a/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/EducationalTipCardProviderSignalHandlerUnitTest.java b/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/EducationalTipCardProviderSignalHandlerUnitTest.java
index 77b5a12..5e0a671 100644
--- a/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/EducationalTipCardProviderSignalHandlerUnitTest.java
+++ b/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/EducationalTipCardProviderSignalHandlerUnitTest.java
@@ -6,6 +6,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
@@ -28,6 +29,7 @@
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.magic_stack.ModuleDelegate.ModuleType;
 import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
 import org.chromium.chrome.browser.tab_group_sync.TabGroupSyncFeatures;
 import org.chromium.chrome.browser.tab_group_sync.TabGroupSyncFeaturesJni;
 import org.chromium.chrome.browser.tab_group_sync.TabGroupSyncServiceFactory;
@@ -39,6 +41,8 @@
 import org.chromium.components.feature_engagement.FeatureConstants;
 import org.chromium.components.feature_engagement.Tracker;
 import org.chromium.components.segmentation_platform.InputContext;
+import org.chromium.components.signin.identitymanager.ConsentLevel;
+import org.chromium.components.signin.identitymanager.IdentityManager;
 import org.chromium.components.tab_group_sync.TabGroupSyncService;
 
 /** Unit tests for {@link EducationalTipCardProviderSignalHandler}. */
@@ -61,6 +65,8 @@
     @Mock private Profile mProfile;
     @Mock private TabGroupSyncService mMockTabGroupSyncService;
     @Mock private TabGroupSyncFeatures.Natives mTabGroupSyncFeaturesJniMock;
+    @Mock private IdentityServicesProvider mIdentityServicesProvider;
+    @Mock private IdentityManager mIdentityManagerMock;
 
     private Context mContext;
 
@@ -80,6 +86,10 @@
         TrackerFactory.setTrackerForTests(mTracker);
         TabGroupSyncFeaturesJni.setInstanceForTesting(mTabGroupSyncFeaturesJniMock);
         when(mTabGroupSyncFeaturesJniMock.isTabGroupSyncEnabled(mProfile)).thenReturn(true);
+        IdentityServicesProvider.setInstanceForTests(mIdentityServicesProvider);
+        when(IdentityServicesProvider.get().getIdentityManager(any()))
+                .thenReturn(mIdentityManagerMock);
+        when(mIdentityManagerMock.hasPrimaryAccount(ConsentLevel.SIGNIN)).thenReturn(false);
     }
 
     @Test
@@ -94,7 +104,7 @@
         InputContext inputContext =
                 EducationalTipCardProviderSignalHandler.createInputContext(
                         ModuleType.DEFAULT_BROWSER_PROMO, mActionDelegate, mProfile, mTracker);
-        assertEquals(2, inputContext.getSizeForTesting());
+        assertEquals(3, inputContext.getSizeForTesting());
 
         // Test signal "should_show_non_role_manager_default_browser_promo".
         when(mMockDefaultBrowserPromoUtils.shouldShowNonRoleManagerPromo(mContext))
@@ -104,8 +114,7 @@
                         ModuleType.DEFAULT_BROWSER_PROMO, mActionDelegate, mProfile, mTracker);
         assertEquals(
                 1,
-                inputContext.getEntryForTesting(
-                                "should_show_non_role_manager_default_browser_promo")
+                inputContext.getEntryValue("should_show_non_role_manager_default_browser_promo")
                         .floatValue,
                 0.01);
 
@@ -116,8 +125,7 @@
                         ModuleType.DEFAULT_BROWSER_PROMO, mActionDelegate, mProfile, mTracker);
         assertEquals(
                 0,
-                inputContext.getEntryForTesting(
-                                "should_show_non_role_manager_default_browser_promo")
+                inputContext.getEntryValue("should_show_non_role_manager_default_browser_promo")
                         .floatValue,
                 0.01);
 
@@ -129,7 +137,7 @@
                         ModuleType.DEFAULT_BROWSER_PROMO, mActionDelegate, mProfile, mTracker);
         assertEquals(
                 0,
-                inputContext.getEntryForTesting("has_default_browser_promo_shown_in_other_surface")
+                inputContext.getEntryValue("has_default_browser_promo_shown_in_other_surface")
                         .floatValue,
                 0.01);
 
@@ -140,9 +148,22 @@
                         ModuleType.DEFAULT_BROWSER_PROMO, mActionDelegate, mProfile, mTracker);
         assertEquals(
                 1,
-                inputContext.getEntryForTesting("has_default_browser_promo_shown_in_other_surface")
+                inputContext.getEntryValue("has_default_browser_promo_shown_in_other_surface")
                         .floatValue,
                 0.01);
+
+        // Test signal "is_user_signed_in".
+        when(mIdentityManagerMock.hasPrimaryAccount(ConsentLevel.SIGNIN)).thenReturn(false);
+        inputContext =
+                EducationalTipCardProviderSignalHandler.createInputContext(
+                        ModuleType.DEFAULT_BROWSER_PROMO, mActionDelegate, mProfile, mTracker);
+        assertEquals(0, inputContext.getEntryValue("is_user_signed_in").floatValue, 0.01);
+
+        when(mIdentityManagerMock.hasPrimaryAccount(ConsentLevel.SIGNIN)).thenReturn(true);
+        inputContext =
+                EducationalTipCardProviderSignalHandler.createInputContext(
+                        ModuleType.DEFAULT_BROWSER_PROMO, mActionDelegate, mProfile, mTracker);
+        assertEquals(1, inputContext.getEntryValue("is_user_signed_in").floatValue, 0.01);
     }
 
     @Test
@@ -154,21 +175,21 @@
         InputContext inputContext =
                 EducationalTipCardProviderSignalHandler.createInputContext(
                         ModuleType.TAB_GROUP_PROMO, mActionDelegate, mProfile, mTracker);
-        assertEquals(2, inputContext.getSizeForTesting());
+        assertEquals(3, inputContext.getSizeForTesting());
 
         when(mNormalFilter.getTabGroupCount()).thenReturn(0);
         when(mIncognitoFilter.getTabGroupCount()).thenReturn(0);
         inputContext =
                 EducationalTipCardProviderSignalHandler.createInputContext(
                         ModuleType.TAB_GROUP_PROMO, mActionDelegate, mProfile, mTracker);
-        assertEquals(0, inputContext.getEntryForTesting("tab_group_exists").floatValue, 0.01);
+        assertEquals(0, inputContext.getEntryValue("tab_group_exists").floatValue, 0.01);
 
         when(mNormalFilter.getTabGroupCount()).thenReturn(5);
         when(mIncognitoFilter.getTabGroupCount()).thenReturn(6);
         inputContext =
                 EducationalTipCardProviderSignalHandler.createInputContext(
                         ModuleType.TAB_GROUP_PROMO, mActionDelegate, mProfile, mTracker);
-        assertEquals(1, inputContext.getEntryForTesting("tab_group_exists").floatValue, 0.01);
+        assertEquals(1, inputContext.getEntryValue("tab_group_exists").floatValue, 0.01);
     }
 
     @Test
@@ -186,28 +207,28 @@
         inputContext =
                 EducationalTipCardProviderSignalHandler.createInputContext(
                         ModuleType.TAB_GROUP_PROMO, mActionDelegate, mProfile, mTracker);
-        assertEquals(0, inputContext.getEntryForTesting("number_of_tabs").floatValue, 0.01);
+        assertEquals(0, inputContext.getEntryValue("number_of_tabs").floatValue, 0.01);
 
         when(mNormalModel.getCount()).thenReturn(5);
         when(mIncognitoModel.getCount()).thenReturn(0);
         inputContext =
                 EducationalTipCardProviderSignalHandler.createInputContext(
                         ModuleType.TAB_GROUP_PROMO, mActionDelegate, mProfile, mTracker);
-        assertEquals(5, inputContext.getEntryForTesting("number_of_tabs").floatValue, 0.01);
+        assertEquals(5, inputContext.getEntryValue("number_of_tabs").floatValue, 0.01);
 
         when(mNormalModel.getCount()).thenReturn(0);
         when(mIncognitoModel.getCount()).thenReturn(10);
         inputContext =
                 EducationalTipCardProviderSignalHandler.createInputContext(
                         ModuleType.TAB_GROUP_PROMO, mActionDelegate, mProfile, mTracker);
-        assertEquals(10, inputContext.getEntryForTesting("number_of_tabs").floatValue, 0.01);
+        assertEquals(10, inputContext.getEntryValue("number_of_tabs").floatValue, 0.01);
 
         when(mNormalModel.getCount()).thenReturn(10);
         when(mIncognitoModel.getCount()).thenReturn(10);
         inputContext =
                 EducationalTipCardProviderSignalHandler.createInputContext(
                         ModuleType.TAB_GROUP_PROMO, mActionDelegate, mProfile, mTracker);
-        assertEquals(20, inputContext.getEntryForTesting("number_of_tabs").floatValue, 0.01);
+        assertEquals(20, inputContext.getEntryValue("number_of_tabs").floatValue, 0.01);
 
         // Test cases when tab state is not initialized.
         when(mTabModelSelector.isTabStateInitialized()).thenReturn(false);
@@ -216,13 +237,13 @@
         inputContext =
                 EducationalTipCardProviderSignalHandler.createInputContext(
                         ModuleType.TAB_GROUP_PROMO, mActionDelegate, mProfile, mTracker);
-        assertEquals(10, inputContext.getEntryForTesting("number_of_tabs").floatValue, 0.01);
+        assertEquals(10, inputContext.getEntryValue("number_of_tabs").floatValue, 0.01);
 
         when(mActionDelegate.getTabCountForRelaunchFromSharedPrefs()).thenReturn(15);
         inputContext =
                 EducationalTipCardProviderSignalHandler.createInputContext(
                         ModuleType.TAB_GROUP_PROMO, mActionDelegate, mProfile, mTracker);
-        assertEquals(15, inputContext.getEntryForTesting("number_of_tabs").floatValue, 0.01);
+        assertEquals(15, inputContext.getEntryValue("number_of_tabs").floatValue, 0.01);
     }
 
     @Test
@@ -235,7 +256,7 @@
         InputContext inputContext =
                 EducationalTipCardProviderSignalHandler.createInputContext(
                         ModuleType.TAB_GROUP_SYNC_PROMO, mActionDelegate, mProfile, mTracker);
-        assertEquals(1, inputContext.getSizeForTesting());
+        assertEquals(2, inputContext.getSizeForTesting());
 
         // Test signal "synced_tab_group_exists".
         when(mProfile.isOffTheRecord()).thenReturn(false);
@@ -243,22 +264,19 @@
         inputContext =
                 EducationalTipCardProviderSignalHandler.createInputContext(
                         ModuleType.TAB_GROUP_SYNC_PROMO, mActionDelegate, mProfile, mTracker);
-        assertEquals(
-                1, inputContext.getEntryForTesting("synced_tab_group_exists").floatValue, 0.01);
+        assertEquals(1, inputContext.getEntryValue("synced_tab_group_exists").floatValue, 0.01);
 
         when(mMockTabGroupSyncService.getAllGroupIds()).thenReturn(new String[] {});
         inputContext =
                 EducationalTipCardProviderSignalHandler.createInputContext(
                         ModuleType.TAB_GROUP_SYNC_PROMO, mActionDelegate, mProfile, mTracker);
-        assertEquals(
-                0, inputContext.getEntryForTesting("synced_tab_group_exists").floatValue, 0.01);
+        assertEquals(0, inputContext.getEntryValue("synced_tab_group_exists").floatValue, 0.01);
 
         when(mProfile.isOffTheRecord()).thenReturn(true);
         when(mMockTabGroupSyncService.getAllGroupIds()).thenReturn(new String[] {SYNC_ID});
         inputContext =
                 EducationalTipCardProviderSignalHandler.createInputContext(
                         ModuleType.TAB_GROUP_SYNC_PROMO, mActionDelegate, mProfile, mTracker);
-        assertEquals(
-                0, inputContext.getEntryForTesting("synced_tab_group_exists").floatValue, 0.01);
+        assertEquals(0, inputContext.getEntryValue("synced_tab_group_exists").floatValue, 0.01);
     }
 }
diff --git a/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/EducationalTipModuleBuilderUnitTest.java b/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/EducationalTipModuleBuilderUnitTest.java
index bc56bf9..ac0f179 100644
--- a/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/EducationalTipModuleBuilderUnitTest.java
+++ b/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/EducationalTipModuleBuilderUnitTest.java
@@ -35,6 +35,7 @@
 import org.chromium.chrome.browser.magic_stack.ModuleDelegate.ModuleType;
 import org.chromium.chrome.browser.magic_stack.ModuleProvider;
 import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
 import org.chromium.chrome.browser.tabmodel.TabGroupModelFilter;
 import org.chromium.chrome.browser.tabmodel.TabGroupModelFilterProvider;
 import org.chromium.chrome.browser.tabmodel.TabModel;
@@ -43,6 +44,8 @@
 import org.chromium.components.feature_engagement.FeatureConstants;
 import org.chromium.components.feature_engagement.Tracker;
 import org.chromium.components.segmentation_platform.InputContext;
+import org.chromium.components.signin.identitymanager.ConsentLevel;
+import org.chromium.components.signin.identitymanager.IdentityManager;
 import org.chromium.ui.shadows.ShadowAppCompatResources;
 
 /** Test relating to {@link EducationalTipModuleBuilder} */
@@ -66,6 +69,8 @@
     @Mock private TabGroupModelFilter mIncognitoFilter;
     @Mock private TabModel mNormalModel;
     @Mock private TabModel mIncognitoModel;
+    @Mock private IdentityServicesProvider mIdentityServicesProvider;
+    @Mock private IdentityManager mIdentityManagerMock;
 
     private EducationalTipModuleBuilder mModuleBuilder;
 
@@ -90,6 +95,10 @@
         when(mTabModelSelector.getModel(/* incognito= */ true)).thenReturn(mIncognitoModel);
         when(mNormalModel.getCount()).thenReturn(0);
         when(mIncognitoModel.getCount()).thenReturn(0);
+        IdentityServicesProvider.setInstanceForTests(mIdentityServicesProvider);
+        when(IdentityServicesProvider.get().getIdentityManager(any()))
+                .thenReturn(mIdentityManagerMock);
+        when(mIdentityManagerMock.hasPrimaryAccount(ConsentLevel.SIGNIN)).thenReturn(false);
 
         mModuleBuilder =
                 new EducationalTipModuleBuilder(ModuleType.QUICK_DELETE_PROMO, mActionDelegate);
@@ -158,24 +167,26 @@
                 new EducationalTipModuleBuilder(ModuleType.DEFAULT_BROWSER_PROMO, mActionDelegate);
         InputContext inputContextForTest = moduleBuilderForDefaultBrowserPromo.createInputContext();
         assertNotNull(
-                inputContextForTest.getEntryForTesting(
+                inputContextForTest.getEntryValue(
                         "should_show_non_role_manager_default_browser_promo"));
         assertNotNull(
-                inputContextForTest.getEntryForTesting(
+                inputContextForTest.getEntryValue(
                         "has_default_browser_promo_shown_in_other_surface"));
-        assertNull(inputContextForTest.getEntryForTesting("tab_group_exists"));
-        assertNull(inputContextForTest.getEntryForTesting("number_of_tabs"));
+        assertNotNull(inputContextForTest.getEntryValue("is_user_signed_in"));
+        assertNull(inputContextForTest.getEntryValue("tab_group_exists"));
+        assertNull(inputContextForTest.getEntryValue("number_of_tabs"));
 
         EducationalTipModuleBuilder moduleBuilderForTabGroupPromo =
                 new EducationalTipModuleBuilder(ModuleType.TAB_GROUP_PROMO, mActionDelegate);
         inputContextForTest = moduleBuilderForTabGroupPromo.createInputContext();
         assertNull(
-                inputContextForTest.getEntryForTesting(
+                inputContextForTest.getEntryValue(
                         "should_show_non_role_manager_default_browser_promo"));
         assertNull(
-                inputContextForTest.getEntryForTesting(
+                inputContextForTest.getEntryValue(
                         "has_default_browser_promo_shown_in_other_surface"));
-        assertNotNull(inputContextForTest.getEntryForTesting("tab_group_exists"));
-        assertNotNull(inputContextForTest.getEntryForTesting("number_of_tabs"));
+        assertNotNull(inputContextForTest.getEntryValue("tab_group_exists"));
+        assertNotNull(inputContextForTest.getEntryValue("number_of_tabs"));
+        assertNotNull(inputContextForTest.getEntryValue("is_user_signed_in"));
     }
 }
diff --git a/chrome/browser/enterprise/util/managed_browser_utils.cc b/chrome/browser/enterprise/util/managed_browser_utils.cc
index a1b35af8..a1c8a3853 100644
--- a/chrome/browser/enterprise/util/managed_browser_utils.cc
+++ b/chrome/browser/enterprise/util/managed_browser_utils.cc
@@ -212,11 +212,14 @@
     return;
   // The updated consent screen also ask the user for consent to share device
   // signals.
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \
+    BUILDFLAG(IS_CHROMEOS)
   if (accepted && base::FeatureList::IsEnabled(
                       features::kEnterpriseUpdatedProfileCreationScreen)) {
     profile->GetPrefs()->SetBoolean(
         device_signals::prefs::kDeviceSignalsPermanentConsentReceived, true);
   }
+#endif
   ProfileManager* profile_manager = g_browser_process->profile_manager();
   ProfileAttributesEntry* entry =
       profile_manager->GetProfileAttributesStorage()
diff --git a/chrome/browser/facilitated_payments/BUILD.gn b/chrome/browser/facilitated_payments/BUILD.gn
index 82eb60e8..9b5fce9 100644
--- a/chrome/browser/facilitated_payments/BUILD.gn
+++ b/chrome/browser/facilitated_payments/BUILD.gn
@@ -19,6 +19,7 @@
     "//chrome/browser/profiles:profile",
     "//chrome/browser/ui/autofill",
     "//components/autofill/core/browser",
+    "//components/facilitated_payments/android",
     "//components/facilitated_payments/content/browser",
     "//components/facilitated_payments/core/browser",
     "//components/facilitated_payments/core/utils",
diff --git a/chrome/browser/facilitated_payments/ui/chrome_facilitated_payments_client.cc b/chrome/browser/facilitated_payments/ui/chrome_facilitated_payments_client.cc
index 3f4bb21..71a8b911 100644
--- a/chrome/browser/facilitated_payments/ui/chrome_facilitated_payments_client.cc
+++ b/chrome/browser/facilitated_payments/ui/chrome_facilitated_payments_client.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/facilitated_payments/ui/chrome_facilitated_payments_client.h"
 
+#include <memory>
+
 #include "base/android/build_info.h"
 #include "base/check_deref.h"
 #include "base/functional/callback_helpers.h"
@@ -17,6 +19,7 @@
 #include "components/autofill/core/browser/data_manager/personal_data_manager.h"
 #include "components/autofill/core/browser/data_model/payments/bank_account.h"
 #include "components/autofill/core/browser/data_model/payments/ewallet.h"
+#include "components/facilitated_payments/android/device_delegate_android.h"
 #include "components/facilitated_payments/core/browser/network_api/facilitated_payments_network_interface.h"
 #include "components/facilitated_payments/core/browser/network_api/multiple_request_facilitated_payments_network_interface.h"
 #include "components/facilitated_payments/core/features/features.h"
@@ -32,7 +35,7 @@
     : content::WebContentsUserData<ChromeFacilitatedPaymentsClient>(
           *web_contents),
       driver_factory_(web_contents,
-                      /*client=*/this),
+                      /* client= */ this),
       facilitated_payments_controller_(
           std::make_unique<FacilitatedPaymentsController>(web_contents)),
       optimization_guide_decider_(optimization_guide_decider) {
@@ -170,6 +173,10 @@
   return autofill::StrikeDatabaseFactory::GetForProfile(profile);
 }
 
+bool ChromeFacilitatedPaymentsClient::IsPixAccountLinkingSupported() const {
+  return payments::facilitated::IsWalletEligibleForPixAccountLinking();
+}
+
 void ChromeFacilitatedPaymentsClient::RegisterAllowlists() {
   if (optimization_guide_decider_) {
     if (base::FeatureList::IsEnabled(payments::facilitated::kEwalletPayments)) {
diff --git a/chrome/browser/facilitated_payments/ui/chrome_facilitated_payments_client.h b/chrome/browser/facilitated_payments/ui/chrome_facilitated_payments_client.h
index c462c7a..b3267182 100644
--- a/chrome/browser/facilitated_payments/ui/chrome_facilitated_payments_client.h
+++ b/chrome/browser/facilitated_payments/ui/chrome_facilitated_payments_client.h
@@ -86,6 +86,7 @@
       base::RepeatingCallback<void(payments::facilitated::UiEvent)>
           ui_event_listener) final;
   autofill::StrikeDatabase* GetStrikeDatabase() final;
+  bool IsPixAccountLinkingSupported() const final;
 
   // Register any allowlists with the OptimizationGuide framework, so that
   // individual features can later request to check whether the current main
diff --git a/chrome/browser/facilitated_payments/ui/chrome_facilitated_payments_client_unittest.cc b/chrome/browser/facilitated_payments/ui/chrome_facilitated_payments_client_unittest.cc
index 58db397..f0f13f47 100644
--- a/chrome/browser/facilitated_payments/ui/chrome_facilitated_payments_client_unittest.cc
+++ b/chrome/browser/facilitated_payments/ui/chrome_facilitated_payments_client_unittest.cc
@@ -41,6 +41,9 @@
 class MockPixAccountLinkingManager
     : public payments::facilitated::PixAccountLinkingManager {
  public:
+  explicit MockPixAccountLinkingManager(
+      payments::facilitated::FacilitatedPaymentsClient* client)
+      : PixAccountLinkingManager(client) {}
   ~MockPixAccountLinkingManager() override = default;
 
   MOCK_METHOD(void, MaybeShowPixAccountLinkingPrompt, (), (override));
@@ -58,7 +61,7 @@
     controller_ = controller.get();
     client_->SetFacilitatedPaymentsControllerForTesting(std::move(controller));
     auto pix_account_linking_manager =
-        std::make_unique<MockPixAccountLinkingManager>();
+        std::make_unique<MockPixAccountLinkingManager>(client_.get());
     pix_account_linking_manager_ = pix_account_linking_manager.get();
     client_->SetPixAccountLinkingManagerForTesting(
         std::move(pix_account_linking_manager));
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 7aded29..8391a25 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -96,14 +96,6 @@
     "expiry_milestone": 139
   },
   {
-    "name": "ai-settings-page-refresh",
-    "owners": [
-      "zalmashni@google.com",
-      "rainhard@chromium.org",
-      "chrome-browser-privacy-team@google.com"],
-    "expiry_milestone": 139
-  },
-  {
     "name": "align-surface-layer-impl-to-pixel-grid",
     "owners": [
       "wjmaclean@google.com", "pdr@google.com", "jonross@google.com"
@@ -3196,11 +3188,6 @@
     "expiry_milestone": 135
   },
   {
-    "name": "enable-enterprise-updated-profile-creation-screen",
-    "owners": [ "ydago@chromium.org", "cbe-magic@google.com" ],
-    "expiry_milestone": 140
-  },
-  {
      "name": "enable-enterprise-url-filtering",
      "owners": [ "eic@google.com", "bling-enterprise@google.com" ],
      "expiry_milestone": 141
@@ -4152,6 +4139,15 @@
     "expiry_milestone": -1
   },
   {
+    "name": "enable-site-search-allow-user-override-policy",
+    "owners": [
+      "alexwchen@chromium.org",
+      "mahmadi@chromium.org",
+      "cbe-productivity@google.com"
+    ],
+    "expiry_milestone": 139
+  },
+  {
     "name": "enable-smart-card-web-api",
     "owners": ["zgroza@chromium.org", "iwa-team@google.com"],
     "expiry_milestone": 142
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 6bda905..59305e5 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -26,10 +26,6 @@
     "Changes CanvasRenderingContxt2D::CanCreateCanvas2DResourceProvider() "
     "to check for provider recreation rather than bridge recreation";
 
-const char kAiSettingsPageRefreshName[] = "AI settings page refresh";
-const char kAiSettingsPageRefreshDescription[] =
-    "Enables a revamp of the existing AI settings page.";
-
 const char kAiSettingsPageEnterpriseDisabledName[] =
     "AI settings page enterprise disabled UI";
 const char kAiSettingsPageEnterpriseDisabledDescription[] =
@@ -1326,12 +1322,6 @@
     "Enable enterprise profile badging in the footer on the New Tab Page. This "
     "includes showing the enterprise logo and the management disclaimer";
 
-const char kEnterpriseUpdatedProfileCreationScreenName[] =
-    "Enable enterprise updated profile creation screens";
-const char kEnterpriseUpdatedProfileCreationScreenDescription[] =
-    "Enable enterprise updated profile creation screens in the profile picker "
-    "and forced profile creation.";
-
 const char kManagedProfileRequiredInterstitialName[] =
     "Enable the managed profile required interstitial";
 const char kManagedProfileRequiredInterstitialDescription[] =
@@ -1805,6 +1795,12 @@
 const char kEnableLazyLoadImageForInvisiblePageDescription[] =
     "Respect the loading = lazy attribute for images even on invisible pages.";
 
+const char kEnableSiteSearchAllowUserOverridePolicyName[] =
+    "Enable allow_user_override field for SiteSearchSettings policy";
+const char kEnableSiteSearchAllowUserOverridePolicyDescription[] =
+    "Enable the field that allows organizations to set a Site Search engine "
+    "that can be overridden by the user.";
+
 const char kEnableLensStandaloneFlagId[] = "enable-lens-standalone";
 const char kEnableLensStandaloneName[] = "Enable Lens features in Chrome.";
 const char kEnableLensStandaloneDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index af6db2a..8bc99bf 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -59,9 +59,6 @@
 extern const char kAdjustCanCreateCanvas2DResourceProviderName[];
 extern const char kAdjustCanCreateCanvas2DResourceProviderDescription[];
 
-extern const char kAiSettingsPageRefreshName[];
-extern const char kAiSettingsPageRefreshDescription[];
-
 extern const char kAiSettingsPageEnterpriseDisabledName[];
 extern const char kAiSettingsPageEnterpriseDisabledDescription[];
 
@@ -341,6 +338,9 @@
 extern const char kEnableLazyLoadImageForInvisiblePageName[];
 extern const char kEnableLazyLoadImageForInvisiblePageDescription[];
 
+extern const char kEnableSiteSearchAllowUserOverridePolicyName[];
+extern const char kEnableSiteSearchAllowUserOverridePolicyDescription[];
+
 extern const char kFontationsFontBackendName[];
 extern const char kFontationsFontBackendDescription[];
 
@@ -765,9 +765,6 @@
 extern const char kEnterpriseBadgingForNtpFooterName[];
 extern const char kEnterpriseBadgingForNtpFooterDescription[];
 
-extern const char kEnterpriseUpdatedProfileCreationScreenName[];
-extern const char kEnterpriseUpdatedProfileCreationScreenDescription[];
-
 extern const char kManagedProfileRequiredInterstitialName[];
 extern const char kManagedProfileRequiredInterstitialDescription[];
 
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
index 44bbf7b..7da2014 100644
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -178,7 +178,6 @@
     &history::kOrganicRepeatableQueries,
     &history_clusters::internal::kJourneys,
     &history_clusters::internal::kOmniboxAction,
-    &kAccessibilityAndroidEventInvestigations,
     &kAdaptiveButtonInTopToolbarCustomizationV2,
     &kAdaptiveButtonInTopToolbarPageSummary,
     &kAllowTabClosingUponMinimization,
@@ -333,6 +332,7 @@
     &kPwaRestoreUiAtStartup,
     &kOmahaMinSdkVersionAndroid,
     &kShortCircuitUnfocusAnimation,
+    &kShowHomeButtonPolicyAndroid,
     &kShowNewTabAnimations,
     &kPartnerCustomizationsUma,
     &kQuickDeleteAndroidSurvey,
@@ -462,10 +462,6 @@
 
 // Alphabetical:
 
-BASE_FEATURE(kAccessibilityAndroidEventInvestigations,
-             "AccessibilityAndroidEventInvestigations",
-             base::FEATURE_DISABLED_BY_DEFAULT);
-
 BASE_FEATURE(kAdaptiveButtonInTopToolbarCustomizationV2,
              "AdaptiveButtonInTopToolbarCustomizationV2",
              base::FEATURE_ENABLED_BY_DEFAULT);
@@ -1101,6 +1097,10 @@
              "ShortCircuitUnfocusAnimation",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+BASE_FEATURE(kShowHomeButtonPolicyAndroid,
+             "ShowHomeButtonPolicyAndroid",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 BASE_FEATURE(kShowNewTabAnimations,
              "ShowNewTabAnimations",
              base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/chrome/browser/flags/android/chrome_feature_list.h b/chrome/browser/flags/android/chrome_feature_list.h
index 2b370e2f..d356ed3a 100644
--- a/chrome/browser/flags/android/chrome_feature_list.h
+++ b/chrome/browser/flags/android/chrome_feature_list.h
@@ -14,7 +14,6 @@
 namespace android {
 
 // Alphabetical:
-BASE_DECLARE_FEATURE(kAccessibilityAndroidEventInvestigations);
 BASE_DECLARE_FEATURE(kAdaptiveButtonInTopToolbarCustomizationV2);
 BASE_DECLARE_FEATURE(kAdaptiveButtonInTopToolbarPageSummary);
 BASE_DECLARE_FEATURE(kAllowTabClosingUponMinimization);
@@ -166,6 +165,7 @@
 BASE_DECLARE_FEATURE(kOmahaMinSdkVersionAndroid);
 BASE_DECLARE_FEATURE(kAvoidRelayoutDuringFocusAnimation);
 BASE_DECLARE_FEATURE(kShortCircuitUnfocusAnimation);
+BASE_DECLARE_FEATURE(kShowHomeButtonPolicyAndroid);
 BASE_DECLARE_FEATURE(kShowNewTabAnimations);
 BASE_DECLARE_FEATURE(kOptimizeGeolocationHeaderGeneration);
 BASE_DECLARE_FEATURE(kPageAnnotationsService);
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 b9a4502..0489dd8 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
@@ -153,8 +153,6 @@
 
     // Feature names.
     /* Alphabetical: */
-    public static final String ACCESSIBILITY_ANDROID_EVENT_INVESTIGATIONS =
-            "AccessibilityAndroidEventInvestigations";
     public static final String ACT_USER_BYPASS_UX = "ActUserBypassUx";
     public static final String ADAPTIVE_BUTTON_IN_TOP_TOOLBAR_CUSTOMIZATION_V2 =
             "AdaptiveButtonInTopToolbarCustomizationV2";
@@ -550,6 +548,7 @@
             "SensitiveContentWhileSwitchingTabs";
     public static final String SETTINGS_SINGLE_ACTIVITY = "SettingsSingleActivity";
     public static final String SHARE_CUSTOM_ACTIONS_IN_CCT = "ShareCustomActionsInCCT";
+    public static final String SHOW_HOME_BUTTON_POLICY_ANDROID = "ShowHomeButtonPolicyAndroid";
     public static final String SHOW_NEW_TAB_ANIMATIONS = "ShowNewTabAnimations";
     public static final String SHOW_WARNINGS_FOR_SUSPICIOUS_NOTIFICATIONS =
             "ShowWarningsForSuspiciousNotifications";
@@ -633,7 +632,7 @@
     public static final CachedFlag sAndroidAppIntegrationModule =
             newCachedFlag(ANDROID_APP_INTEGRATION_MODULE, true);
     public static final CachedFlag sAndroidAppIntegrationMultiDataSource =
-            newCachedFlag(ANDROID_APP_INTEGRATION_MULTI_DATA_SOURCE, false);
+            newCachedFlag(ANDROID_APP_INTEGRATION_MULTI_DATA_SOURCE, false, true);
     public static final CachedFlag sAndroidAppIntegrationV2 =
             newCachedFlag(ANDROID_APP_INTEGRATION_V2, true);
     public static final CachedFlag sAndroidAppIntegrationWithFavicon =
@@ -851,6 +850,8 @@
             newCachedFlag(SMALLER_TAB_STRIP_TITLE_LIMIT, true);
     public static final CachedFlag sStartSurfaceReturnTime =
             newCachedFlag(START_SURFACE_RETURN_TIME, true);
+    public static final CachedFlag sShowHomeButtonPolicyAndroid =
+            newCachedFlag(SHOW_HOME_BUTTON_POLICY_ANDROID, false);
     public static final CachedFlag sTabClosureMethodRefactor =
             newCachedFlag(TAB_CLOSURE_METHOD_REFACTOR, false);
     public static final CachedFlag sTabletTabStripAnimation =
@@ -993,6 +994,7 @@
                     sSkipIsolatedSplitPreload,
                     sSmallerTabStripTitleLimit,
                     sStartSurfaceReturnTime,
+                    sShowHomeButtonPolicyAndroid,
                     sTabClosureMethodRefactor,
                     sTabletTabStripAnimation,
                     sTabStateFlatBuffer,
@@ -1021,8 +1023,6 @@
 
     // MutableFlagWithSafeDefault instances.
     /* Alphabetical: */
-    public static final MutableFlagWithSafeDefault sAccessibilityAndroidEventInvestigations =
-            newMutableFlagWithSafeDefault(ACCESSIBILITY_ANDROID_EVENT_INVESTIGATIONS, false);
     public static final MutableFlagWithSafeDefault sAndroidAppearanceSettings =
             newMutableFlagWithSafeDefault(ANDROID_APPEARANCE_SETTINGS, false);
     public static final MutableFlagWithSafeDefault sAndroidBookmarkBar =
diff --git a/chrome/browser/glic/glic_keyed_service.cc b/chrome/browser/glic/glic_keyed_service.cc
index 6f94676..985dad8 100644
--- a/chrome/browser/glic/glic_keyed_service.cc
+++ b/chrome/browser/glic/glic_keyed_service.cc
@@ -353,8 +353,7 @@
 
   metrics_->DidRequestContextFromFocusedTab();
 
-  GlicPageContextFetcher::Fetch(GetFocusedTabData(), options,
-                                std::move(callback));
+  FetchPageContext(GetFocusedTabData(), options, std::move(callback));
 }
 
 void GlicKeyedService::ActInFocusedTab(
diff --git a/chrome/browser/glic/host/context/glic_page_context_fetcher.cc b/chrome/browser/glic/host/context/glic_page_context_fetcher.cc
index 6f283b01..3b0dabe 100644
--- a/chrome/browser/glic/host/context/glic_page_context_fetcher.cc
+++ b/chrome/browser/glic/host/context/glic_page_context_fetcher.cc
@@ -4,14 +4,18 @@
 
 #include "chrome/browser/glic/host/context/glic_page_context_fetcher.h"
 
+#include <vector>
+
 #include "base/feature_list.h"
 #include "base/functional/callback.h"
+#include "base/memory/weak_ptr.h"
 #include "base/metrics/field_trial_params.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/thread_pool.h"
+#include "base/time/time.h"
 #include "chrome/browser/content_extraction/inner_text.h"
 #include "chrome/browser/glic/host/context/glic_page_context_eligibility_observer.h"
 #include "chrome/browser/glic/host/context/glic_tab_data.h"
@@ -26,11 +30,14 @@
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_widget_host_view.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_observer.h"
 #include "mojo/public/cpp/base/proto_wrapper.h"
 #include "pdf/mojom/pdf.mojom.h"
 #include "third_party/blink/public/mojom/content_extraction/ai_page_content.mojom.h"
 #include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkSize.h"
 #include "ui/gfx/codec/jpeg_codec.h"
+#include "url/origin.h"
 
 namespace glic {
 
@@ -115,13 +122,344 @@
   UMA_HISTOGRAM_ENUMERATION("Glic.TabContext.PdfContentsRequested", state);
 }
 
+// Coordinates fetching multiple types of page context.
+class GlicPageContextFetcher : public content::WebContentsObserver {
+ public:
+  GlicPageContextFetcher() = default;
+  ~GlicPageContextFetcher() override = default;
+
+  void FetchStart(
+      FocusedTabData focused_tab_data,
+      const mojom::GetTabContextOptions& options,
+      glic::mojom::WebClientHandler::GetContextFromFocusedTabCallback
+          callback) {
+    base::expected<content::WebContents*, std::string_view> focus =
+        focused_tab_data.GetFocus();
+    if (!focus.has_value()) {
+      std::move(callback).Run(
+          mojom::GetContextResult::NewErrorReason(std::string(focus.error())));
+      return;
+    }
+    options_ = options;
+
+    content::WebContents* aweb_contents = focus.value();
+    DCHECK(aweb_contents->GetPrimaryMainFrame());
+    CHECK_EQ(web_contents(),
+             nullptr);  // Ensure Fetch is called only once per instance.
+    Observe(aweb_contents);
+    // TODO(crbug.com/391851902): implement kSensitiveContentAttribute error
+    // checking and signaling.
+    start_time_ = base::TimeTicks::Now();
+    callback_ = std::move(callback);
+
+    if (options.include_viewport_screenshot) {
+      GetTabScreenshot(*web_contents());
+    } else {
+      screenshot_done_ = true;
+    }
+
+    if (options.include_inner_text) {
+      content::RenderFrameHost* frame = web_contents()->GetPrimaryMainFrame();
+      // This could be more efficient if content_extraction::GetInnerText
+      // supported a max length. Instead, we truncate after generating the full
+      // text.
+      content_extraction::GetInnerText(
+          *frame,
+          /*node_id=*/std::nullopt,
+          base::BindOnce(&GlicPageContextFetcher::ReceivedInnerText,
+                         GetWeakPtr()));
+    } else {
+      inner_text_done_ = true;
+    }
+
+    pdf_done_ = true;  // Will not fetch PDF contents by default.
+    if (options.include_pdf) {
+      bool is_pdf_document =
+          web_contents()->GetContentsMimeType() == pdf::kPDFMimeType;
+      pdf::PDFDocumentHelper* pdf_helper =
+          pdf::PDFDocumentHelper::MaybeGetForWebContents(web_contents());
+      RecordPdfRequestState(is_pdf_document,
+                            /*pdf_found=*/pdf_helper != nullptr);
+      // GetPdfBytes() is not safe before IsDocumentLoadComplete() = true.
+      if (is_pdf_document && pdf_helper &&
+          pdf_helper->IsDocumentLoadComplete()) {
+        pdf_origin_ = pdf_helper->render_frame_host().GetLastCommittedOrigin();
+        pdf_helper->GetPdfBytes(
+            options_.pdf_size_limit,
+            base::BindOnce(&GlicPageContextFetcher::ReceivedPdfBytes,
+                           GetWeakPtr()));
+        pdf_done_ = false;  // Will fetch PDF contents.
+      }
+    }
+
+    if (options.include_annotated_page_content) {
+      blink::mojom::AIPageContentOptionsPtr ai_page_content_options;
+      ai_page_content_options =
+          optimization_guide::DefaultAIPageContentOptions();
+      ai_page_content_options->include_geometry = false;
+      ai_page_content_options->on_critical_path = true;
+      ai_page_content_options->include_hidden_searchable_content = true;
+      ai_page_content_options->max_meta_elements = options.max_meta_tags;
+      // TODO(crbug.com/409564704): Move actor page content extraction to the
+      // actor coordinator.
+      if (base::FeatureList::IsEnabled(features::kGlicActor)) {
+        ai_page_content_options->include_geometry = true;
+        ai_page_content_options->enable_experimental_actionable_data = true;
+      }
+      optimization_guide::GetAIPageContent(
+          web_contents(), std::move(ai_page_content_options),
+          base::BindOnce(&GlicPageContextFetcher::ReceivedAnnotatedPageContent,
+                         GetWeakPtr()));
+    } else {
+      annotated_page_content_done_ = true;
+    }
+
+    // Will only fetch context eligibility if we can observe it.
+    context_eligibility_check_done_ =
+        !(GlicPageContextEligibilityObserver::MaybeGetEligibilityForWebContents(
+            web_contents(),
+            base::BindOnce(&GlicPageContextFetcher::ReceivedContextEligibility,
+                           GetWeakPtr())));
+
+    // Note: initialization_done_ guards against processing
+    // `RunCallbackIfComplete()` until we reach this point.
+    initialization_done_ = true;
+    RunCallbackIfComplete();
+  }
+
+  void ReceivedPdfBytes(pdf::mojom::PdfListener::GetPdfBytesStatus status,
+                        const std::vector<uint8_t>& pdf_bytes,
+                        uint32_t page_count) {
+    pdf_done_ = true;
+    pdf_status_ = status;
+    pdf_bytes_ = pdf_bytes;
+    RunCallbackIfComplete();
+  }
+
+  void GetTabScreenshot(content::WebContents& web_contents) {
+    // TODO(crbug.com/378937313): Finish this provisional implementation.
+    auto* view = web_contents.GetRenderWidgetHostView();
+    auto callback = base::BindOnce(
+        &GlicPageContextFetcher::RecievedJpegScreenshot, GetWeakPtr());
+
+    if (!view || !view->IsSurfaceAvailableForCopy()) {
+      std::move(callback).Run({});
+      DLOG(WARNING) << "Could not retrieve RenderWidgetHostView.";
+      return;
+    }
+
+    view->CopyFromSurface(
+        gfx::Rect(),  // Copy entire surface area.
+        GetScreenshotSize(view),
+        base::BindOnce(&GlicPageContextFetcher::ReceivedViewportBitmap,
+                       GetWeakPtr()));
+  }
+
+  void ReceivedViewportBitmap(const SkBitmap& bitmap) {
+    screenshot_dimensions_ = bitmap.dimensions();
+    base::UmaHistogramTimes("Glic.PageContextFetcher.GetScreenshot",
+                            base::TimeTicks::Now() - start_time_);
+    base::ThreadPool::PostTaskAndReplyWithResult(
+        FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
+        base::BindOnce(
+            [](const SkBitmap& bitmap) {
+              return gfx::JPEGCodec::Encode(bitmap, GetScreenshotJpegQuality());
+            },
+            std::move(bitmap)),
+        base::BindOnce(&GlicPageContextFetcher::RecievedJpegScreenshot,
+                       GetWeakPtr()));
+  }
+
+  // content::WebContentsObserver impl.
+  void PrimaryPageChanged(content::Page& page) override {
+    primary_page_changed_ = true;
+    RunCallbackIfComplete();
+  }
+
+  void RecievedJpegScreenshot(
+      std::optional<std::vector<uint8_t>> screenshot_jpeg_data) {
+    if (screenshot_jpeg_data) {
+      screenshot_ = glic::mojom::Screenshot::New(
+          screenshot_dimensions_.width(), screenshot_dimensions_.height(),
+          std::move(*screenshot_jpeg_data), "image/jpeg",
+          // TODO(crbug.com/380495633): Finalize and implement image
+          // annotations.
+          glic::mojom::ImageOriginAnnotations::New());
+    }
+    screenshot_done_ = true;
+    base::UmaHistogramTimes("Glic.PageContextFetcher.GetEncodedScreenshot",
+                            base::TimeTicks::Now() - start_time_);
+    RunCallbackIfComplete();
+  }
+
+  void ReceivedInnerText(
+      std::unique_ptr<content_extraction::InnerTextResult> result) {
+    inner_text_result_ = std::move(result);
+    inner_text_done_ = true;
+    base::UmaHistogramTimes("Glic.PageContextFetcher.GetInnerText",
+                            base::TimeTicks::Now() - start_time_);
+    RunCallbackIfComplete();
+  }
+
+  void ReceivedAnnotatedPageContent(
+      std::optional<optimization_guide::AIPageContentResult> content) {
+    annotated_page_content_result_ = std::move(content);
+    annotated_page_content_done_ = true;
+    base::UmaHistogramTimes("Glic.PageContextFetcher.GetAnnotatedPageContent",
+                            base::TimeTicks::Now() - start_time_);
+    RunCallbackIfComplete();
+  }
+
+  void ReceivedContextEligibility(bool is_eligible) {
+    context_eligible_ = is_eligible;
+    context_eligibility_check_done_ = true;
+    base::UmaHistogramTimes("Glic.PageContextFetcher.GetContextEligibility",
+                            base::TimeTicks::Now() - start_time_);
+    base::UmaHistogramBoolean("Glic.PageContextFetcher.PageContextEligible",
+                              *context_eligible_);
+    RunCallbackIfComplete();
+  }
+
+  void RunCallbackIfComplete() {
+    if (!initialization_done_) {
+      return;
+    }
+
+    // Continue only if the primary page changed or work is complete.
+    bool work_complete =
+        (screenshot_done_ && inner_text_done_ && annotated_page_content_done_ &&
+         pdf_done_ && context_eligibility_check_done_) ||
+        primary_page_changed_;
+    if (!work_complete) {
+      return;
+    }
+    base::UmaHistogramTimes("Glic.PageContextFetcher.Total",
+                            base::TimeTicks::Now() - start_time_);
+
+    mojom::GetContextResultPtr result;
+    if (primary_page_changed_) {
+      result = mojom::GetContextResult::NewErrorReason("web contents changed");
+      std::move(callback_).Run(std::move(result));
+      return;
+    }
+
+    if (!web_contents() || !web_contents()->GetPrimaryMainFrame()) {
+      result = mojom::GetContextResult::NewErrorReason("web contents changed");
+      std::move(callback_).Run(std::move(result));
+      return;
+    }
+
+    if (!context_eligible_.value_or(true)) {
+      result =
+          mojom::GetContextResult::NewErrorReason("page context ineligible");
+      std::move(callback_).Run(std::move(result));
+      return;
+    }
+
+    auto tab_context = mojom::TabContext::New();
+    tab_context->tab_data = CreateTabData(web_contents());
+    if (inner_text_result_) {
+      // Get trimmed text without copying.
+      std::string trimmed_text = std::move(inner_text_result_->inner_text);
+      size_t truncated_size = base::TruncateUTF8ToByteSize(
+                                  trimmed_text, options_.inner_text_bytes_limit)
+                                  .length();
+      bool truncated = false;
+      if (truncated_size < trimmed_text.length()) {
+        truncated = true;
+        trimmed_text.resize(truncated_size);
+      }
+
+      tab_context->web_page_data =
+          mojom::WebPageData::New(mojom::DocumentData::New(
+              web_contents()->GetPrimaryMainFrame()->GetLastCommittedOrigin(),
+              std::move(trimmed_text), truncated));
+    }
+    if (screenshot_) {
+      tab_context->viewport_screenshot = std::move(screenshot_);
+    }
+
+    if (pdf_status_) {
+      auto pdf_document_data = mojom::PdfDocumentData::New();
+      pdf_document_data->origin = pdf_origin_;
+
+      // Warning!: `pdf_bytes_` can be larger than pdf_size_limit.
+      // `pdf_size_limit` applies to the original PDF size, but the PDF is
+      // re-serialized and returned, so it is not identical to the original.
+      pdf_document_data->size_limit_exceeded =
+          *pdf_status_ ==
+              pdf::mojom::PdfListener_GetPdfBytesStatus::kSizeLimitExceeded ||
+          pdf_bytes_.size() > options_.pdf_size_limit;
+      if (!pdf_document_data->size_limit_exceeded) {
+        pdf_document_data->pdf_data = std::move(pdf_bytes_);
+      }
+
+      tab_context->pdf_document_data = std::move(pdf_document_data);
+    }
+
+    if (annotated_page_content_result_) {
+      auto annotated_page_data = mojom::AnnotatedPageData::New();
+
+      if (auto* media_integration =
+              GlicMediaIntegration::GetFor(web_contents())) {
+        optimization_guide::proto::ContentNode* media_node =
+            annotated_page_content_result_->proto.mutable_root_node()
+                ->add_children_nodes();
+
+        media_integration->AppendContext(web_contents(), media_node);
+      }
+
+      annotated_page_data->annotated_page_content =
+          mojo_base::ProtoWrapper(annotated_page_content_result_->proto);
+
+      annotated_page_data->metadata =
+          std::move(annotated_page_content_result_->metadata);
+
+      tab_context->annotated_page_data = std::move(annotated_page_data);
+    }
+
+    result = mojom::GetContextResult::NewTabContext(std::move(tab_context));
+    std::move(callback_).Run(std::move(result));
+  }
+
+ private:
+  base::WeakPtr<GlicPageContextFetcher> GetWeakPtr() {
+    return weak_ptr_factory_.GetWeakPtr();
+  }
+
+  glic::mojom::WebClientHandler::GetContextFromFocusedTabCallback callback_;
+
+  mojom::GetTabContextOptions options_;
+
+  // Intermediate results:
+
+  // Whether work is complete for each task, does not imply success.
+  bool initialization_done_ = false;
+  bool screenshot_done_ = false;
+  bool inner_text_done_ = false;
+  bool pdf_done_ = false;
+  bool annotated_page_content_done_ = false;
+  bool context_eligibility_check_done_ = false;
+  // Whether the primary page has changed since context fetching began.
+  bool primary_page_changed_ = false;
+  url::Origin pdf_origin_;
+  std::optional<std::vector<uint8_t>> screenshot_jpeg_data_;
+  SkISize screenshot_dimensions_;
+  glic::mojom::ScreenshotPtr screenshot_;
+  std::unique_ptr<content_extraction::InnerTextResult> inner_text_result_;
+  std::vector<uint8_t> pdf_bytes_;
+  std::optional<pdf::mojom::PdfListener_GetPdfBytesStatus> pdf_status_;
+  std::optional<optimization_guide::AIPageContentResult>
+      annotated_page_content_result_;
+  std::optional<bool> context_eligible_;
+  base::TimeTicks start_time_;
+
+  base::WeakPtrFactory<GlicPageContextFetcher> weak_ptr_factory_{this};
+};
+
 }  // namespace
 
-GlicPageContextFetcher::GlicPageContextFetcher() = default;
-
-GlicPageContextFetcher::~GlicPageContextFetcher() = default;
-
-void GlicPageContextFetcher::Fetch(
+void FetchPageContext(
     FocusedTabData focused_tab_data,
     const mojom::GetTabContextOptions& options,
     glic::mojom::WebClientHandler::GetContextFromFocusedTabCallback callback) {
@@ -141,293 +479,4 @@
           std::move(self), std::move(callback)));
 }
 
-void GlicPageContextFetcher::FetchStart(
-    FocusedTabData focused_tab_data,
-    const mojom::GetTabContextOptions& options,
-    glic::mojom::WebClientHandler::GetContextFromFocusedTabCallback callback) {
-  base::expected<content::WebContents*, std::string_view> focus =
-      focused_tab_data.GetFocus();
-  if (!focus.has_value()) {
-    std::move(callback).Run(
-        mojom::GetContextResult::NewErrorReason(std::string(focus.error())));
-    return;
-  }
-  options_ = options;
-
-  content::WebContents* aweb_contents = focus.value();
-  DCHECK(aweb_contents->GetPrimaryMainFrame());
-  CHECK_EQ(web_contents(),
-           nullptr);  // Ensure Fetch is called only once per instance.
-  Observe(aweb_contents);
-  // TODO(crbug.com/391851902): implement kSensitiveContentAttribute error
-  // checking and signaling.
-  start_time_ = base::TimeTicks::Now();
-  callback_ = std::move(callback);
-
-  if (options.include_viewport_screenshot) {
-    GetTabScreenshot(*web_contents());
-  } else {
-    screenshot_done_ = true;
-  }
-
-  if (options.include_inner_text) {
-    content::RenderFrameHost* frame = web_contents()->GetPrimaryMainFrame();
-    // This could be more efficient if content_extraction::GetInnerText
-    // supported a max length. Instead, we truncate after generating the full
-    // text.
-    content_extraction::GetInnerText(
-        *frame,
-        /*node_id=*/std::nullopt,
-        base::BindOnce(&GlicPageContextFetcher::ReceivedInnerText,
-                       GetWeakPtr()));
-  } else {
-    inner_text_done_ = true;
-  }
-
-  pdf_done_ = true;  // Will not fetch PDF contents by default.
-  if (options.include_pdf) {
-    bool is_pdf_document =
-        web_contents()->GetContentsMimeType() == pdf::kPDFMimeType;
-    pdf::PDFDocumentHelper* pdf_helper =
-        pdf::PDFDocumentHelper::MaybeGetForWebContents(web_contents());
-    RecordPdfRequestState(is_pdf_document, /*pdf_found=*/pdf_helper != nullptr);
-    // GetPdfBytes() is not safe before IsDocumentLoadComplete() = true.
-    if (is_pdf_document && pdf_helper && pdf_helper->IsDocumentLoadComplete()) {
-      pdf_origin_ = pdf_helper->render_frame_host().GetLastCommittedOrigin();
-      pdf_helper->GetPdfBytes(
-          options_.pdf_size_limit,
-          base::BindOnce(&GlicPageContextFetcher::ReceivedPdfBytes,
-                         GetWeakPtr()));
-      pdf_done_ = false;  // Will fetch PDF contents.
-    }
-  }
-
-  if (options.include_annotated_page_content) {
-    blink::mojom::AIPageContentOptionsPtr ai_page_content_options;
-    ai_page_content_options = optimization_guide::DefaultAIPageContentOptions();
-    ai_page_content_options->include_geometry = false;
-    ai_page_content_options->on_critical_path = true;
-    ai_page_content_options->include_hidden_searchable_content = true;
-    ai_page_content_options->max_meta_elements = options.max_meta_tags;
-    // TODO(crbug.com/409564704): Move actor page content extraction to the
-    // actor coordinator.
-    if (base::FeatureList::IsEnabled(features::kGlicActor)) {
-      ai_page_content_options->include_geometry = true;
-      ai_page_content_options->enable_experimental_actionable_data = true;
-    }
-    optimization_guide::GetAIPageContent(
-        web_contents(), std::move(ai_page_content_options),
-        base::BindOnce(&GlicPageContextFetcher::ReceivedAnnotatedPageContent,
-                       GetWeakPtr()));
-  } else {
-    annotated_page_content_done_ = true;
-  }
-
-  // Will only fetch context eligibility if we can observe it.
-  context_eligibility_check_done_ =
-      !(GlicPageContextEligibilityObserver::MaybeGetEligibilityForWebContents(
-          web_contents(),
-          base::BindOnce(&GlicPageContextFetcher::ReceivedContextEligibility,
-                         GetWeakPtr())));
-
-  // Note: initialization_done_ guards against processing
-  // `RunCallbackIfComplete()` until we reach this point.
-  initialization_done_ = true;
-  RunCallbackIfComplete();
-}
-
-void GlicPageContextFetcher::ReceivedPdfBytes(
-    pdf::mojom::PdfListener::GetPdfBytesStatus status,
-    const std::vector<uint8_t>& pdf_bytes,
-    uint32_t page_count) {
-  pdf_done_ = true;
-  pdf_status_ = status;
-  pdf_bytes_ = pdf_bytes;
-  RunCallbackIfComplete();
-}
-
-void GlicPageContextFetcher::GetTabScreenshot(
-    content::WebContents& web_contents) {
-  // TODO(crbug.com/378937313): Finish this provisional implementation.
-  auto* view = web_contents.GetRenderWidgetHostView();
-  auto callback = base::BindOnce(
-      &GlicPageContextFetcher::RecievedJpegScreenshot, GetWeakPtr());
-
-  if (!view || !view->IsSurfaceAvailableForCopy()) {
-    std::move(callback).Run({});
-    DLOG(WARNING) << "Could not retrieve RenderWidgetHostView.";
-    return;
-  }
-
-  view->CopyFromSurface(
-      gfx::Rect(),  // Copy entire surface area.
-      GetScreenshotSize(view),
-      base::BindOnce(&GlicPageContextFetcher::ReceivedViewportBitmap,
-                     GetWeakPtr()));
-}
-
-void GlicPageContextFetcher::ReceivedViewportBitmap(const SkBitmap& bitmap) {
-  screenshot_dimensions_ = bitmap.dimensions();
-  base::UmaHistogramTimes("Glic.PageContextFetcher.GetScreenshot",
-                          base::TimeTicks::Now() - start_time_);
-  base::ThreadPool::PostTaskAndReplyWithResult(
-      FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
-      base::BindOnce(
-          [](const SkBitmap& bitmap) {
-            return gfx::JPEGCodec::Encode(bitmap, GetScreenshotJpegQuality());
-          },
-          std::move(bitmap)),
-      base::BindOnce(&GlicPageContextFetcher::RecievedJpegScreenshot,
-                     GetWeakPtr()));
-}
-
-void GlicPageContextFetcher::PrimaryPageChanged(content::Page& page) {
-  primary_page_changed_ = true;
-  RunCallbackIfComplete();
-}
-
-void GlicPageContextFetcher::RecievedJpegScreenshot(
-    std::optional<std::vector<uint8_t>> screenshot_jpeg_data) {
-  if (screenshot_jpeg_data) {
-    screenshot_ = glic::mojom::Screenshot::New(
-        screenshot_dimensions_.width(), screenshot_dimensions_.height(),
-        std::move(*screenshot_jpeg_data), "image/jpeg",
-        // TODO(crbug.com/380495633): Finalize and implement image annotations.
-        glic::mojom::ImageOriginAnnotations::New());
-  }
-  screenshot_done_ = true;
-  base::UmaHistogramTimes("Glic.PageContextFetcher.GetEncodedScreenshot",
-                          base::TimeTicks::Now() - start_time_);
-  RunCallbackIfComplete();
-}
-
-void GlicPageContextFetcher::ReceivedInnerText(
-    std::unique_ptr<content_extraction::InnerTextResult> result) {
-  inner_text_result_ = std::move(result);
-  inner_text_done_ = true;
-  base::UmaHistogramTimes("Glic.PageContextFetcher.GetInnerText",
-                          base::TimeTicks::Now() - start_time_);
-  RunCallbackIfComplete();
-}
-
-void GlicPageContextFetcher::ReceivedAnnotatedPageContent(
-    std::optional<optimization_guide::AIPageContentResult> content) {
-  annotated_page_content_result_ = std::move(content);
-  annotated_page_content_done_ = true;
-  base::UmaHistogramTimes("Glic.PageContextFetcher.GetAnnotatedPageContent",
-                          base::TimeTicks::Now() - start_time_);
-  RunCallbackIfComplete();
-}
-
-void GlicPageContextFetcher::ReceivedContextEligibility(bool is_eligible) {
-  context_eligible_ = is_eligible;
-  context_eligibility_check_done_ = true;
-  base::UmaHistogramTimes("Glic.PageContextFetcher.GetContextEligibility",
-                          base::TimeTicks::Now() - start_time_);
-  base::UmaHistogramBoolean("Glic.PageContextFetcher.PageContextEligible",
-                            *context_eligible_);
-  RunCallbackIfComplete();
-}
-
-void GlicPageContextFetcher::RunCallbackIfComplete() {
-  if (!initialization_done_) {
-    return;
-  }
-
-  // Continue only if the primary page changed or work is complete.
-  bool work_complete =
-      (screenshot_done_ && inner_text_done_ && annotated_page_content_done_ &&
-       pdf_done_ && context_eligibility_check_done_) ||
-      primary_page_changed_;
-  if (!work_complete) {
-    return;
-  }
-  base::UmaHistogramTimes("Glic.PageContextFetcher.Total",
-                          base::TimeTicks::Now() - start_time_);
-
-  mojom::GetContextResultPtr result;
-  if (primary_page_changed_) {
-    result = mojom::GetContextResult::NewErrorReason("web contents changed");
-    std::move(callback_).Run(std::move(result));
-    return;
-  }
-
-  if (!web_contents() || !web_contents()->GetPrimaryMainFrame()) {
-    result = mojom::GetContextResult::NewErrorReason("web contents changed");
-    std::move(callback_).Run(std::move(result));
-    return;
-  }
-
-  if (!context_eligible_.value_or(true)) {
-    result = mojom::GetContextResult::NewErrorReason("page context ineligible");
-    std::move(callback_).Run(std::move(result));
-    return;
-  }
-
-  auto tab_context = mojom::TabContext::New();
-  tab_context->tab_data = CreateTabData(web_contents());
-  if (inner_text_result_) {
-    // Get trimmed text without copying.
-    std::string trimmed_text = std::move(inner_text_result_->inner_text);
-    size_t truncated_size = base::TruncateUTF8ToByteSize(
-                                trimmed_text, options_.inner_text_bytes_limit)
-                                .length();
-    bool truncated = false;
-    if (truncated_size < trimmed_text.length()) {
-      truncated = true;
-      trimmed_text.resize(truncated_size);
-    }
-
-    tab_context->web_page_data =
-        mojom::WebPageData::New(mojom::DocumentData::New(
-            web_contents()->GetPrimaryMainFrame()->GetLastCommittedOrigin(),
-            std::move(trimmed_text), truncated));
-  }
-  if (screenshot_) {
-    tab_context->viewport_screenshot = std::move(screenshot_);
-  }
-
-  if (pdf_status_) {
-    auto pdf_document_data = mojom::PdfDocumentData::New();
-    pdf_document_data->origin = pdf_origin_;
-
-    // Warning!: `pdf_bytes_` can be larger than pdf_size_limit.
-    // `pdf_size_limit` applies to the original PDF size, but the PDF is
-    // re-serialized and returned, so it is not identical to the original.
-    pdf_document_data->size_limit_exceeded =
-        *pdf_status_ ==
-            pdf::mojom::PdfListener_GetPdfBytesStatus::kSizeLimitExceeded ||
-        pdf_bytes_.size() > options_.pdf_size_limit;
-    if (!pdf_document_data->size_limit_exceeded) {
-      pdf_document_data->pdf_data = std::move(pdf_bytes_);
-    }
-
-    tab_context->pdf_document_data = std::move(pdf_document_data);
-  }
-
-  if (annotated_page_content_result_) {
-    auto annotated_page_data = mojom::AnnotatedPageData::New();
-
-    if (auto* media_integration =
-            GlicMediaIntegration::GetFor(web_contents())) {
-      optimization_guide::proto::ContentNode* media_node =
-          annotated_page_content_result_->proto.mutable_root_node()
-              ->add_children_nodes();
-
-      media_integration->AppendContext(web_contents(), media_node);
-    }
-
-    annotated_page_data->annotated_page_content =
-        mojo_base::ProtoWrapper(annotated_page_content_result_->proto);
-
-    annotated_page_data->metadata =
-        std::move(annotated_page_content_result_->metadata);
-
-    tab_context->annotated_page_data = std::move(annotated_page_data);
-  }
-
-  result = mojom::GetContextResult::NewTabContext(std::move(tab_context));
-  std::move(callback_).Run(std::move(result));
-}
-
 }  // namespace glic
diff --git a/chrome/browser/glic/host/context/glic_page_context_fetcher.h b/chrome/browser/glic/host/context/glic_page_context_fetcher.h
index 3937b13..84de8db 100644
--- a/chrome/browser/glic/host/context/glic_page_context_fetcher.h
+++ b/chrome/browser/glic/host/context/glic_page_context_fetcher.h
@@ -5,95 +5,15 @@
 #ifndef CHROME_BROWSER_GLIC_HOST_CONTEXT_GLIC_PAGE_CONTEXT_FETCHER_H_
 #define CHROME_BROWSER_GLIC_HOST_CONTEXT_GLIC_PAGE_CONTEXT_FETCHER_H_
 
-#include <cstddef>
-#include <vector>
-
-#include "base/functional/callback_forward.h"
-#include "base/memory/weak_ptr.h"
-#include "base/time/time.h"
-#include "chrome/browser/content_extraction/inner_text.h"
 #include "chrome/browser/glic/host/context/glic_tab_data.h"
 #include "chrome/browser/glic/host/glic.mojom.h"
-#include "components/optimization_guide/content/browser/page_content_proto_provider.h"
-#include "content/public/browser/web_contents_observer.h"
-#include "pdf/mojom/pdf.mojom-forward.h"
-#include "third_party/skia/include/core/SkSize.h"
-#include "url/origin.h"
-
-namespace content {
-class WebContents;
-}
 
 namespace glic {
 
-// Coordinates fetching multiple types of page context.
-class GlicPageContextFetcher : public content::WebContentsObserver {
- public:
-  GlicPageContextFetcher();
-  ~GlicPageContextFetcher() override;
-
-  // Fetches the page context.
-  static void Fetch(
-      FocusedTabData focused_tab_data,
-      const mojom::GetTabContextOptions& options,
-      glic::mojom::WebClientHandler::GetContextFromFocusedTabCallback callback);
-
-  // content::WebContentsObserver.
-  void PrimaryPageChanged(content::Page& page) override;
-
- private:
-  void FetchStart(
-      FocusedTabData focused_tab_data,
-      const mojom::GetTabContextOptions& options,
-      glic::mojom::WebClientHandler::GetContextFromFocusedTabCallback callback);
-
-  void GetTabScreenshot(content::WebContents& web_contents);
-  void ReceivedViewportBitmap(const SkBitmap& bitmap);
-  void RecievedJpegScreenshot(
-      std::optional<std::vector<uint8_t>> screenshot_jpeg_data);
-  void ReceivedInnerText(
-      std::unique_ptr<content_extraction::InnerTextResult> result);
-  void ReceivedAnnotatedPageContent(
-      std::optional<optimization_guide::AIPageContentResult>);
-  void RunCallbackIfComplete();
-  void ReceivedPdfBytes(pdf::mojom::PdfListener_GetPdfBytesStatus status,
-                        const std::vector<uint8_t>& pdf_bytes,
-                        uint32_t page_count);
-  void ReceivedContextEligibility(bool is_eligible);
-
-  base::WeakPtr<GlicPageContextFetcher> GetWeakPtr() {
-    return weak_ptr_factory_.GetWeakPtr();
-  }
-
-  glic::mojom::WebClientHandler::GetContextFromFocusedTabCallback callback_;
-
-  mojom::GetTabContextOptions options_;
-
-  // Intermediate results:
-
-  // Whether work is complete for each task, does not imply success.
-  bool initialization_done_ = false;
-  bool screenshot_done_ = false;
-  bool inner_text_done_ = false;
-  bool pdf_done_ = false;
-  bool annotated_page_content_done_ = false;
-  bool context_eligibility_check_done_ = false;
-  // Whether the primary page has changed since context fetching began.
-  bool primary_page_changed_ = false;
-  url::Origin pdf_origin_;
-  std::optional<std::vector<uint8_t>> screenshot_jpeg_data_;
-  SkISize screenshot_dimensions_;
-  glic::mojom::ScreenshotPtr screenshot_;
-  std::unique_ptr<content_extraction::InnerTextResult> inner_text_result_;
-  std::vector<uint8_t> pdf_bytes_;
-  std::optional<pdf::mojom::PdfListener_GetPdfBytesStatus> pdf_status_;
-  std::optional<optimization_guide::AIPageContentResult>
-      annotated_page_content_result_;
-  std::optional<bool> context_eligible_;
-  base::TimeTicks start_time_;
-
-  base::WeakPtrFactory<GlicPageContextFetcher> weak_ptr_factory_{this};
-};
+void FetchPageContext(
+    FocusedTabData focused_tab_data,
+    const mojom::GetTabContextOptions& options,
+    mojom::WebClientHandler::GetContextFromFocusedTabCallback callback);
 
 }  // namespace glic
 
diff --git a/chrome/browser/glic/host/glic_actor_controller.cc b/chrome/browser/glic/host/glic_actor_controller.cc
index ae62d22..9cc1a43 100644
--- a/chrome/browser/glic/host/glic_actor_controller.cc
+++ b/chrome/browser/glic/host/glic_actor_controller.cc
@@ -169,7 +169,7 @@
   // with GlicKeyedService::GetContextFromFocusedTab(). It's not clear yet if
   // the same permission checks, etc. should apply here.
 
-  GlicPageContextFetcher::Fetch(focused_tab_data, options, std::move(callback));
+  FetchPageContext(focused_tab_data, options, std::move(callback));
 }
 
 base::WeakPtr<const GlicActorController> GlicActorController::GetWeakPtr()
diff --git a/chrome/browser/glic/host/glic_actor_controller_interactive_uitest.cc b/chrome/browser/glic/host/glic_actor_controller_interactive_uitest.cc
index b42fc60..0fe78245 100644
--- a/chrome/browser/glic/host/glic_actor_controller_interactive_uitest.cc
+++ b/chrome/browser/glic/host/glic_actor_controller_interactive_uitest.cc
@@ -240,12 +240,11 @@
       ASSERT_TRUE(glic_service);
 
       base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
-      auto fetcher = std::make_unique<GlicPageContextFetcher>();
 
       auto options = mojom::GetTabContextOptions::New();
       options->include_annotated_page_content = true;
 
-      fetcher->Fetch(
+      FetchPageContext(
           glic_service->GetFocusedTabData(), *options,
           base::BindLambdaForTesting([&](mojom::GetContextResultPtr result) {
             mojo_base::ProtoWrapper& serialized_apc =
diff --git a/chrome/browser/glic/host/glic_annotation_manager_interactive_uitest.cc b/chrome/browser/glic/host/glic_annotation_manager_interactive_uitest.cc
index 8c996fd..1c3d213 100644
--- a/chrome/browser/glic/host/glic_annotation_manager_interactive_uitest.cc
+++ b/chrome/browser/glic/host/glic_annotation_manager_interactive_uitest.cc
@@ -19,6 +19,7 @@
 #include "chrome/common/chrome_features.h"
 #include "chrome/test/interaction/interactive_browser_test.h"
 #include "chrome/test/interaction/tracked_element_webcontents.h"
+#include "components/optimization_guide/proto/features/common_quality_data.pb.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
@@ -149,7 +150,7 @@
       auto options = mojom::GetTabContextOptions::New();
       options->include_annotated_page_content = true;
 
-      GlicPageContextFetcher::Fetch(
+      FetchPageContext(
           glic_service->GetFocusedTabData(), *options,
           base::BindLambdaForTesting([&](mojom::GetContextResultPtr result) {
             mojo_base::ProtoWrapper& serialized_apc =
diff --git a/chrome/browser/glic/host/glic_api_browsertest.cc b/chrome/browser/glic/host/glic_api_browsertest.cc
index 2e31656..d42d62d2 100644
--- a/chrome/browser/glic/host/glic_api_browsertest.cc
+++ b/chrome/browser/glic/host/glic_api_browsertest.cc
@@ -34,10 +34,12 @@
 #include "chrome/browser/glic/glic_keyed_service.h"
 #include "chrome/browser/glic/glic_keyed_service_factory.h"
 #include "chrome/browser/glic/glic_metrics.h"
+#include "chrome/browser/glic/glic_profile_manager.h"
 #include "chrome/browser/glic/host/glic.mojom.h"
 #include "chrome/browser/glic/host/glic_page_handler.h"
 #include "chrome/browser/glic/host/host.h"
 #include "chrome/browser/glic/test_support/glic_test_util.h"
+#include "chrome/browser/glic/test_support/interactive_test_util.h"
 #include "chrome/browser/glic/test_support/non_interactive_glic_test.h"
 #include "chrome/browser/glic/widget/glic_window_controller.h"
 #include "chrome/browser/media/audio_ducker.h"
@@ -90,6 +92,7 @@
       "GlicApiTestWithFastTimeout",
       "GlicApiTestSystemSettingsTest",
       "GlicApiTestWithOneTabAndContextualCueing",
+      "GlicApiTestWithOneTabAndPreloading",
       "GlicApiTestPageContextEligibilityTest",
   };
 }
@@ -324,6 +327,72 @@
   }
 };
 
+class GlicApiTestWithOneTabAndPreloading : public GlicApiTestWithOneTab {
+ public:
+  GlicApiTestWithOneTabAndPreloading() {
+    features_.InitWithFeaturesAndParameters(
+        /*enabled_features=*/
+        {{features::kGlic,
+          {
+              {"glic-default-hotkey", "Ctrl+G"},
+              // Shorten load timeouts.
+              {features::kGlicPreLoadingTimeMs.name, "20"},
+              {features::kGlicMinLoadingTimeMs.name, "40"},
+          }},
+         {features::kGlicApiActivationGating, {}},
+         {features::kGlicWarming,
+          {{features::kGlicWarmingDelayMs.name, "0"},
+           {features::kGlicWarmingJitterMs.name, "0"}}}},
+        /*disabled_features=*/
+        {});
+    // This will temporarily disable preloading to ensure that we don't load the
+    // web client before we've initialized the embedded test server and can set
+    // the correct URL.
+    GlicProfileManager::ForceMemoryPressureForTesting(
+        base::MemoryPressureMonitor::MemoryPressureLevel::
+            MEMORY_PRESSURE_LEVEL_CRITICAL);
+    GlicProfileManager::ForceConnectionTypeForTesting(
+        network::mojom::ConnectionType::CONNECTION_ETHERNET);
+  }
+
+  void SetUpOnMainThread() override {
+    GlicApiTest::SetUpOnMainThread();
+    RunTestSequence(InstrumentTab(kFirstTab),
+                    NavigateWebContents(kFirstTab, page_url()));
+  }
+
+  void TearDown() override {
+    GlicApiTestWithOneTab::TearDown();
+    GlicProfileManager::ForceMemoryPressureForTesting(std::nullopt);
+    GlicProfileManager::ForceConnectionTypeForTesting(std::nullopt);
+  }
+
+  GlicKeyedService* GetService() {
+    Profile* profile = browser()->profile();
+    return GlicKeyedServiceFactory::GetGlicKeyedService(profile);
+  }
+
+  Host* GetHost() {
+    Profile* profile = browser()->profile();
+    return &GlicKeyedServiceFactory::GetGlicKeyedService(profile)->host();
+  }
+
+  auto CreateAndWarmGlic() {
+    return Do([this] { GetService()->TryPreload(); });
+  }
+
+  auto ResetMemoryPressure() {
+    return Do([]() {
+      GlicProfileManager::ForceMemoryPressureForTesting(
+          base::MemoryPressureMonitor::MemoryPressureLevel::
+              MEMORY_PRESSURE_LEVEL_NONE);
+    });
+  }
+
+ private:
+  base::test::ScopedFeatureList features_;
+};
+
 class GlicApiTestWithOneTabAndContextualCueing : public GlicApiTestWithOneTab {
  public:
   GlicApiTestWithOneTabAndContextualCueing() {
@@ -509,6 +578,25 @@
   });
 }
 
+IN_PROC_BROWSER_TEST_F(GlicApiTest, testReloadWebUi) {
+  WebUIStateListener listener(&host());
+  RunTestSequence(
+      OpenGlicWindow(GlicWindowMode::kDetached, GlicInstrumentMode::kNone));
+  ExecuteJsTest();
+
+  listener.WaitForWebUiState(mojom::WebUiState::kReady);
+  window_controller().Reload();
+  listener.WaitForWebUiState(mojom::WebUiState::kUninitialized);
+  ExecuteJsTest();
+
+  ASSERT_TRUE(base::test::RunUntil(
+      [&]() { return host().GetPageHandlersForTesting().size() == 1; }));
+  // Reloading the WebUI should trigger loading a second page handler.
+  // That page handler should become the primary page handler.
+  // This assertion is a regression test for b/418258791.
+  ASSERT_TRUE(host().GetPrimaryPageHandlerForTesting());
+}
+
 // The client navigates to the 'sorry' page before it finishes initialize().
 // Chrome should show this page.
 IN_PROC_BROWSER_TEST_F(GlicApiTest, testSorryPageBeforeInitialize) {
@@ -829,6 +917,25 @@
   ExecuteJsTest();
 }
 
+IN_PROC_BROWSER_TEST_F(GlicApiTestWithOneTabAndPreloading,
+                       testDeferredFocusedTabStateAtCreation) {
+  // Preload a web contents and then navigate.
+  RunTestSequence(
+      WaitForShow(kGlicButtonElementId), ResetMemoryPressure(),
+      ObserveState(glic::test::internal::kWebUiState, &host()),
+      CreateAndWarmGlic(),
+      WaitForState(glic::test::internal::kWebUiState,
+                   mojom::WebUiState::kReady),
+      CheckControllerShowing(false),
+      NavigateWebContents(kFirstTab,
+                          InProcessBrowserTest::embedded_test_server()->GetURL(
+                              "/scrollable_page_with_content.html")));
+  ExecuteJsTest();
+  RunTestSequence(ToggleGlicWindow(GlicWindowMode::kDetached),
+                  CheckControllerShowing(true));
+  ContinueJsTest();
+}
+
 IN_PROC_BROWSER_TEST_F(GlicApiTestWithOneTab, testGetFocusedTabStateV2) {
   ExecuteJsTest();
 }
diff --git a/chrome/browser/glic/host/host.cc b/chrome/browser/glic/host/host.cc
index 9ac552d..d678c04 100644
--- a/chrome/browser/glic/host/host.cc
+++ b/chrome/browser/glic/host/host.cc
@@ -92,6 +92,26 @@
   info.page_handler = page_handler;
   page_handlers_.push_back(std::move(info));
 
+  // We effectively just want to pick the first page handler as the primary one.
+  // There are two reasons why there can be more than one glic page handler:
+  // 1. The chrome://glic URL can be loaded in a tab, which is used for
+  //    development sometimes.
+  // 2. The glic window supports right-click->Reload. When this happens, there
+  //    is momentarily two page handlers for the same web contents. Since this
+  //    can affect real users, it needs to be handled specially here.
+  if (contents_ &&
+      contents_->web_contents() == page_handler->webui_contents()) {
+    if (primary_page_handler_) {
+      // Allow replacing the primary page handler if the new handler is running
+      // in the primary web contents. Note that the old handler may also have
+      // the same web contents, but in the case of Reload, it will be removed
+      // soon.
+      WebUiStateChanged(primary_page_handler_,
+                        mojom::WebUiState::kUninitialized);
+      primary_page_handler_ = nullptr;
+    }
+    primary_page_handler_ = page_handler;
+  }
   if (!primary_page_handler_) {
     primary_page_handler_ = page_handler;
   }
@@ -236,6 +256,10 @@
       [](PageHandlerInfo& e) -> GlicPageHandler* { return e.page_handler; });
 }
 
+GlicPageHandler* Host::GetPrimaryPageHandlerForTesting() {
+  return primary_page_handler_;
+}
+
 void Host::PanelWillOpenComplete(GlicWebClientAccess* client,
                                  mojom::OpenPanelInfoPtr open_info) {
   CHECK(client);
diff --git a/chrome/browser/glic/host/host.h b/chrome/browser/glic/host/host.h
index fc3c0ae..1d7b9169 100644
--- a/chrome/browser/glic/host/host.h
+++ b/chrome/browser/glic/host/host.h
@@ -108,6 +108,7 @@
 
   // Returns the list of page handlers for glic WebUI pages.
   std::vector<GlicPageHandler*> GetPageHandlersForTesting();
+  GlicPageHandler* GetPrimaryPageHandlerForTesting();
 
   // TODO(b/409332639): Hide direct access to the web client.
   GlicWebClientAccess* GetPrimaryWebClient();
diff --git a/chrome/browser/glic/test_support/interactive_test_util.cc b/chrome/browser/glic/test_support/interactive_test_util.cc
index e597692..a87f748 100644
--- a/chrome/browser/glic/test_support/interactive_test_util.cc
+++ b/chrome/browser/glic/test_support/interactive_test_util.cc
@@ -35,6 +35,22 @@
 
 DEFINE_STATE_IDENTIFIER_VALUE(GlicAppStateObserver, kGlicAppState);
 
+WebUiStateObserver::WebUiStateObserver(Host* host) : host_(host) {
+  observation_.Observe(host);
+}
+
+WebUiStateObserver::~WebUiStateObserver() {
+  observation_.Reset();
+}
+
+mojom::WebUiState WebUiStateObserver::GetStateObserverInitialState() const {
+  return host_->GetPrimaryWebUiState();
+}
+
+void WebUiStateObserver::WebUiStateChanged(mojom::WebUiState state) {
+  OnStateObserverStateChanged(state);
+}
+
 }  // namespace internal
 
 DEFINE_ELEMENT_IDENTIFIER_VALUE(kGlicHostElementId);
diff --git a/chrome/browser/glic/test_support/interactive_test_util.h b/chrome/browser/glic/test_support/interactive_test_util.h
index 86f8ce42..9bc8add3 100644
--- a/chrome/browser/glic/test_support/interactive_test_util.h
+++ b/chrome/browser/glic/test_support/interactive_test_util.h
@@ -76,6 +76,25 @@
 };
 
 DEFINE_LOCAL_STATE_IDENTIFIER_VALUE(WaitingStateObserver, kDelayState);
+
+class WebUiStateObserver : public ui::test::StateObserver<mojom::WebUiState>,
+                           public Host::Observer {
+ public:
+  explicit WebUiStateObserver(Host* host);
+
+  ~WebUiStateObserver() override;
+
+  mojom::WebUiState GetStateObserverInitialState() const override;
+
+  void WebUiStateChanged(mojom::WebUiState state) override;
+
+ private:
+  base::ScopedObservation<Host, Host::Observer> observation_{this};
+  raw_ptr<Host> host_;
+};
+
+DEFINE_LOCAL_STATE_IDENTIFIER_VALUE(WebUiStateObserver, kWebUiState);
+
 }  // namespace internal
 
 // The glic WebUI web contents.
diff --git a/chrome/browser/history_embeddings/history_embeddings_utils.cc b/chrome/browser/history_embeddings/history_embeddings_utils.cc
index 1b66097a..f6eab19 100644
--- a/chrome/browser/history_embeddings/history_embeddings_utils.cc
+++ b/chrome/browser/history_embeddings/history_embeddings_utils.cc
@@ -157,11 +157,8 @@
   source->AddInteger("historyEmbeddingsSearchMinimumWordCount",
                      history_embeddings::GetFeatureParameters()
                          .search_query_minimum_word_count);
-  source->AddString(
-      "historyEmbeddingsSettingsUrl",
-      optimization_guide::features::IsAiSettingsPageRefreshEnabled()
-          ? chrome::kHistorySearchV2SettingURL
-          : chrome::kHistorySearchSettingURL);
+  source->AddString("historyEmbeddingsSettingsUrl",
+                    chrome::kHistorySearchSettingURL);
 
   bool logging_disabled_by_enterprise =
       profile->GetPrefs()->GetInteger(
diff --git a/chrome/browser/magic_stack/android/junit/src/org/chromium/chrome/browser/magic_stack/HomeModulesUtilsUnitTest.java b/chrome/browser/magic_stack/android/junit/src/org/chromium/chrome/browser/magic_stack/HomeModulesUtilsUnitTest.java
index 92a18f8e..7cb1c42 100644
--- a/chrome/browser/magic_stack/android/junit/src/org/chromium/chrome/browser/magic_stack/HomeModulesUtilsUnitTest.java
+++ b/chrome/browser/magic_stack/android/junit/src/org/chromium/chrome/browser/magic_stack/HomeModulesUtilsUnitTest.java
@@ -132,7 +132,7 @@
         // Verifies the freshness score matches.
         assertEquals(
                 expectedScore,
-                inputContext.getEntryForTesting(
+                inputContext.getEntryValue(
                                 HomeModulesUtils.getFreshnessInputContextString(moduleType))
                         .floatValue,
                 0.01);
diff --git a/chrome/browser/magic_stack/android/junit/src/org/chromium/chrome/browser/magic_stack/ModuleRegistryUnitTest.java b/chrome/browser/magic_stack/android/junit/src/org/chromium/chrome/browser/magic_stack/ModuleRegistryUnitTest.java
index 00af5bff..3fd106a 100644
--- a/chrome/browser/magic_stack/android/junit/src/org/chromium/chrome/browser/magic_stack/ModuleRegistryUnitTest.java
+++ b/chrome/browser/magic_stack/android/junit/src/org/chromium/chrome/browser/magic_stack/ModuleRegistryUnitTest.java
@@ -148,10 +148,10 @@
 
         assertEquals(
                 INVALID_FRESHNESS_SCORE,
-                inputContext.getEntryForTesting(
+                inputContext.getEntryValue(
                                 HomeModulesUtils.getFreshnessInputContextString(moduleType1))
                         .floatValue,
                 0.01);
-        assertEquals(1.0f, inputContext.getEntryForTesting("key").floatValue, 0.01);
+        assertEquals(1.0f, inputContext.getEntryValue("key").floatValue, 0.01);
     }
 }
diff --git a/chrome/browser/navigation_predictor/anchor_element_preloader_browsertest.cc b/chrome/browser/navigation_predictor/anchor_element_preloader_browsertest.cc
index d0d67d1..29b5c6a 100644
--- a/chrome/browser/navigation_predictor/anchor_element_preloader_browsertest.cc
+++ b/chrome/browser/navigation_predictor/anchor_element_preloader_browsertest.cc
@@ -118,7 +118,8 @@
   void OnPreresolveFinished(
       const GURL& url,
       const net::NetworkAnonymizationKey& network_anonymization_key,
-      mojo::PendingRemote<network::mojom::ReconnectEventObserver>& observer,
+      mojo::PendingRemote<network::mojom::ConnectionChangeObserverClient>&
+          observer,
       bool success) override {
     if (url != GURL(kOrigin1) && url != GURL(kOrigin2)) {
       return;
diff --git a/chrome/browser/navigation_predictor/navigation_predictor_preconnect_client_browsertest.cc b/chrome/browser/navigation_predictor/navigation_predictor_preconnect_client_browsertest.cc
index f84c791d..11a707e 100644
--- a/chrome/browser/navigation_predictor/navigation_predictor_preconnect_client_browsertest.cc
+++ b/chrome/browser/navigation_predictor/navigation_predictor_preconnect_client_browsertest.cc
@@ -104,7 +104,8 @@
   void OnPreresolveFinished(
       const GURL& url,
       const net::NetworkAnonymizationKey& network_anonymization_key,
-      mojo::PendingRemote<network::mojom::ReconnectEventObserver>& observer,
+      mojo::PendingRemote<network::mojom::ConnectionChangeObserverClient>&
+          observer,
       bool success) override {
     // The tests do not care about preresolves to non-test server (e.g., hard
     // coded preconnects to google.com).
@@ -132,7 +133,7 @@
  protected:
   int preresolve_done_count_ = 0;
   std::unique_ptr<net::EmbeddedTestServer> https_server_;
-  mojo::Remote<network::mojom::ReconnectEventObserver> observer_;
+  mojo::Remote<network::mojom::ConnectionChangeObserverClient> observer_;
 
  private:
   base::test::ScopedFeatureList feature_list_;
diff --git a/chrome/browser/navigation_predictor/search_engine_preconnector.cc b/chrome/browser/navigation_predictor/search_engine_preconnector.cc
index c8d0fe5..ea565f9 100644
--- a/chrome/browser/navigation_predictor/search_engine_preconnector.cc
+++ b/chrome/browser/navigation_predictor/search_engine_preconnector.cc
@@ -29,7 +29,6 @@
 #include "mojo/public/cpp/bindings/remote.h"
 #include "net/base/features.h"
 #include "net/base/reconnect_notifier.h"
-#include "services/network/public/mojom/reconnect_event_observer.mojom-forward.h"
 
 namespace {
 
@@ -181,7 +180,7 @@
       is_browser_app_likely_in_foreground);
 
   std::optional<net::ConnectionKeepAliveConfig> keepalive_config;
-  mojo::PendingRemote<network::mojom::ReconnectEventObserver> observer;
+  mojo::PendingRemote<network::mojom::ConnectionChangeObserverClient> observer;
   if (SearchEnginePreconnect2Enabled()) {
     keepalive_config = net::ConnectionKeepAliveConfig();
     keepalive_config->idle_timeout_in_seconds =
diff --git a/chrome/browser/navigation_predictor/search_engine_preconnector.h b/chrome/browser/navigation_predictor/search_engine_preconnector.h
index 7ec6c61..1a2415d 100644
--- a/chrome/browser/navigation_predictor/search_engine_preconnector.h
+++ b/chrome/browser/navigation_predictor/search_engine_preconnector.h
@@ -12,7 +12,7 @@
 #include "base/timer/timer.h"
 #include "chrome/browser/predictors/preconnect_manager.h"
 #include "components/keyed_service/core/keyed_service.h"
-#include "services/network/public/mojom/reconnect_event_observer.mojom.h"
+#include "services/network/public/mojom/connection_change_observer_client.mojom.h"
 #include "url/origin.h"
 
 namespace content {
@@ -62,10 +62,11 @@
 
 // Class to preconnect to the user's default search engine at regular intervals.
 // Preconnects are made by |this| if the browser app is likely in foreground.
-class SearchEnginePreconnector : public predictors::PreconnectManager::Delegate,
-                                 public WebContentVisibilityManager,
-                                 public KeyedService,
-                                 public network::mojom::ReconnectEventObserver {
+class SearchEnginePreconnector
+    : public predictors::PreconnectManager::Delegate,
+      public WebContentVisibilityManager,
+      public KeyedService,
+      public network::mojom::ConnectionChangeObserverClient {
  public:
   static bool ShouldBeEnabledAsKeyedService();
   static bool ShouldBeEnabledForOffTheRecord();
@@ -91,7 +92,7 @@
   void PreconnectFinished(
       std::unique_ptr<predictors::PreconnectStats> stats) override {}
 
-  // network::mojom::ReconnectEventObserver
+  // network::mojom::ConnectionChangeObserverClient
   void OnSessionClosed() override;
   void OnNetworkEvent(net::NetworkChangeEvent event) override;
   void OnConnectionFailed() override;
@@ -193,8 +194,9 @@
   std::optional<base::TimeTicks> last_preconnect_attempt_time_;
 
   // Receives and dispatches method calls to this implementation of the
-  // `network::mojom::ReconnectEventObserver` interface.
-  mojo::Receiver<network::mojom::ReconnectEventObserver> receiver_{this};
+  // `network::mojom::ConnectionChangeObserverClient` interface.
+  mojo::Receiver<network::mojom::ConnectionChangeObserverClient> receiver_{
+      this};
 
   // How many times the connection has consecutively failed. This is used for
   // exponential backoff the preconnect retries.
diff --git a/chrome/browser/navigation_predictor/search_engine_preconnector_browsertest.cc b/chrome/browser/navigation_predictor/search_engine_preconnector_browsertest.cc
index abee4af..6ad805a 100644
--- a/chrome/browser/navigation_predictor/search_engine_preconnector_browsertest.cc
+++ b/chrome/browser/navigation_predictor/search_engine_preconnector_browsertest.cc
@@ -97,7 +97,8 @@
   void OnPreresolveFinished(
       const GURL& url,
       const net::NetworkAnonymizationKey& network_anonymization_key,
-      mojo::PendingRemote<network::mojom::ReconnectEventObserver>& observer,
+      mojo::PendingRemote<network::mojom::ConnectionChangeObserverClient>&
+          observer,
       bool success) override {
     // Take the observer so that we can manually send mojo message.
     if (observer.is_valid() && !remote_.is_bound()) {
@@ -140,7 +141,7 @@
   std::map<GURL, int> preresolve_counts_;
   base::test::ScopedFeatureList feature_list_;
 
-  mojo::Remote<network::mojom::ReconnectEventObserver> remote_;
+  mojo::Remote<network::mojom::ConnectionChangeObserverClient> remote_;
 
  private:
   std::unique_ptr<net::EmbeddedTestServer> https_server_;
@@ -654,7 +655,8 @@
   void OnPreresolveFinished(
       const GURL& url,
       const net::NetworkAnonymizationKey& network_anonymization_key,
-      mojo::PendingRemote<network::mojom::ReconnectEventObserver>& observer,
+      mojo::PendingRemote<network::mojom::ConnectionChangeObserverClient>&
+          observer,
       bool success) override {
     // Take the observer so that we can manually send mojo message.
     if (observer.is_valid() && !remote_.is_bound()) {
diff --git a/chrome/browser/ntp_customization/BUILD.gn b/chrome/browser/ntp_customization/BUILD.gn
index 60f33e8..b423b1a 100644
--- a/chrome/browser/ntp_customization/BUILD.gn
+++ b/chrome/browser/ntp_customization/BUILD.gn
@@ -68,6 +68,7 @@
     "java/res/drawable/ntp_customization_bottom_sheet_list_item_background_top.xml",
     "java/res/layout/bottom_sheet_list_item_view.xml",
     "java/res/layout/ntp_customization_bottom_sheet.xml",
+    "java/res/layout/ntp_customization_drag_handle_bar.xml",
     "java/res/layout/ntp_customization_feed_bottom_sheet.xml",
     "java/res/layout/ntp_customization_main_bottom_sheet.xml",
     "java/res/layout/ntp_customization_ntp_cards_bottom_sheet.xml",
diff --git a/chrome/browser/ntp_customization/java/res/layout/ntp_customization_drag_handle_bar.xml b/chrome/browser/ntp_customization/java/res/layout/ntp_customization_drag_handle_bar.xml
new file mode 100644
index 0000000..9f770d7
--- /dev/null
+++ b/chrome/browser/ntp_customization/java/res/layout/ntp_customization_drag_handle_bar.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+<merge
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+    <ImageView
+        android:id="@+id/drag_handle_bar"
+        android:layout_height="@dimen/default_browser_bottom_sheet_drag_handel_bar_height"
+        android:layout_width="@dimen/default_browser_bottom_sheet_drag_handel_bar_width"
+        android:layout_marginVertical="@dimen/default_browser_bottom_sheet_drag_handel_bar_margin_vertical"
+        android:importantForAccessibility="no"
+        android:src="@drawable/drag_handlebar"
+        app:tint="@macro/drag_handlebar_color"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent" />
+</merge>
\ No newline at end of file
diff --git a/chrome/browser/ntp_customization/java/res/layout/ntp_customization_feed_bottom_sheet.xml b/chrome/browser/ntp_customization/java/res/layout/ntp_customization_feed_bottom_sheet.xml
index 77c1112..d0d452a 100644
--- a/chrome/browser/ntp_customization/java/res/layout/ntp_customization_feed_bottom_sheet.xml
+++ b/chrome/browser/ntp_customization/java/res/layout/ntp_customization_feed_bottom_sheet.xml
@@ -14,6 +14,8 @@
     android:layout_height="wrap_content"
     android:paddingBottom="@dimen/ntp_customization_bottom_sheet_layout_padding_bottom">
 
+    <include layout="@layout/ntp_customization_drag_handle_bar" />
+
     <ImageView
         android:id="@+id/back_button"
         android:layout_height="@dimen/ntp_customization_back_button_clickable_size"
@@ -35,10 +37,10 @@
         android:textAppearance="@style/TextAppearance.Headline.Primary"
         android:text="@string/ntp_customization_feed_settings_title"
         android:layout_marginStart="@dimen/ntp_customization_ntp_cards_title_margin"
-        android:layout_marginTop="@dimen/ntp_customization_back_button_margin"
+        android:layout_marginTop="@dimen/ntp_customization_bottom_sheet_layout_padding_bottom"
         android:layout_marginEnd="@dimen/ntp_customization_bottom_sheet_layout_padding_bottom"
         app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/drag_handle_bar"
         app:layout_constraintEnd_toStartOf="@id/learn_more_button" />
 
     <ImageView
diff --git a/chrome/browser/ntp_customization/java/res/layout/ntp_customization_main_bottom_sheet.xml b/chrome/browser/ntp_customization/java/res/layout/ntp_customization_main_bottom_sheet.xml
index 4048bed1..6d34ab11 100644
--- a/chrome/browser/ntp_customization/java/res/layout/ntp_customization_main_bottom_sheet.xml
+++ b/chrome/browser/ntp_customization/java/res/layout/ntp_customization_main_bottom_sheet.xml
@@ -12,17 +12,7 @@
     android:layout_height="wrap_content"
     android:paddingBottom="@dimen/ntp_customization_bottom_sheet_layout_padding_bottom">
 
-    <ImageView
-        android:id="@+id/drag_handle_bar"
-        android:layout_height="@dimen/default_browser_bottom_sheet_drag_handel_bar_height"
-        android:layout_width="@dimen/default_browser_bottom_sheet_drag_handel_bar_width"
-        android:layout_marginVertical="@dimen/default_browser_bottom_sheet_drag_handel_bar_margin_vertical"
-        android:importantForAccessibility="no"
-        android:src="@drawable/drag_handlebar"
-        app:tint="@macro/drag_handlebar_color"
-        app:layout_constraintTop_toTopOf="parent"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintEnd_toEndOf="parent" />
+    <include layout="@layout/ntp_customization_drag_handle_bar" />
 
     <ImageView
         android:id="@+id/edit_icon"
diff --git a/chrome/browser/ntp_customization/java/res/layout/ntp_customization_ntp_cards_bottom_sheet.xml b/chrome/browser/ntp_customization/java/res/layout/ntp_customization_ntp_cards_bottom_sheet.xml
index 13ab471..26cc1a5 100644
--- a/chrome/browser/ntp_customization/java/res/layout/ntp_customization_ntp_cards_bottom_sheet.xml
+++ b/chrome/browser/ntp_customization/java/res/layout/ntp_customization_ntp_cards_bottom_sheet.xml
@@ -13,6 +13,8 @@
     android:layout_height="wrap_content"
     android:paddingBottom="@dimen/ntp_customization_bottom_sheet_layout_padding_bottom">
 
+    <include layout="@layout/ntp_customization_drag_handle_bar" />
+
     <ImageView
         android:id="@+id/back_button"
         android:layout_height="@dimen/ntp_customization_back_button_clickable_size"
@@ -34,10 +36,10 @@
         android:textAppearance="@style/TextAppearance.Headline.Primary"
         android:text="@string/home_modules_configuration"
         android:layout_marginStart="@dimen/ntp_customization_ntp_cards_title_margin"
-        android:layout_marginTop="@dimen/ntp_customization_back_button_margin"
+        android:layout_marginTop="@dimen/ntp_customization_bottom_sheet_layout_padding_bottom"
         android:layout_marginEnd="@dimen/ntp_customization_bottom_sheet_layout_padding_bottom"
         app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/drag_handle_bar"
         app:layout_constraintEnd_toEndOf="parent" />
 
     <org.chromium.chrome.browser.ntp_customization.ntp_cards.NtpCardsListContainerView
diff --git a/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc b/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
index 3cd6f2e2..a8995e78 100644
--- a/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
@@ -766,7 +766,8 @@
   }
 
 #if !BUILDFLAG(IS_ANDROID)
-  if (optimization_guide::features::kShowAiSettingsForTesting.Get()) {
+  if (base::FeatureList::IsEnabled(
+          optimization_guide::features::kAiSettingsPageForceAvailable)) {
     return true;
   }
 #endif
diff --git a/chrome/browser/optimization_guide/optimization_guide_keyed_service_browsertest.cc b/chrome/browser/optimization_guide/optimization_guide_keyed_service_browsertest.cc
index acdbb12..a9f5abf 100644
--- a/chrome/browser/optimization_guide/optimization_guide_keyed_service_browsertest.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_keyed_service_browsertest.cc
@@ -1254,9 +1254,8 @@
     SettingsNotVisible) {
   EnableSignIn();
 
-  EXPECT_EQ(
-      ShouldFeatureBeEnabled() && features::IsAiSettingsPageRefreshEnabled(),
-      IsSettingVisible(UserVisibleFeatureKey::kWallpaperSearch));
+  EXPECT_EQ(ShouldFeatureBeEnabled(),
+            IsSettingVisible(UserVisibleFeatureKey::kWallpaperSearch));
 
   EXPECT_EQ(ShouldFeatureBeEnabled(),
             IsSettingVisible(UserVisibleFeatureKey::kTabOrganization));
diff --git a/chrome/browser/page_load_metrics/observers/from_gws_abandoned_page_load_metrics_observer_browsertest.cc b/chrome/browser/page_load_metrics/observers/from_gws_abandoned_page_load_metrics_observer_browsertest.cc
index 2a7bfae..d053fa7 100644
--- a/chrome/browser/page_load_metrics/observers/from_gws_abandoned_page_load_metrics_observer_browsertest.cc
+++ b/chrome/browser/page_load_metrics/observers/from_gws_abandoned_page_load_metrics_observer_browsertest.cc
@@ -744,12 +744,12 @@
       for (NavigationMilestone milestone : all_throttleable_milestones()) {
         content::TestNavigationThrottleInserter throttle_inserter(
             web_contents(),
-            base::BindLambdaForTesting([&](content::NavigationHandle* handle)
-                                           -> std::unique_ptr<
-                                               content::NavigationThrottle> {
-              if (handle->GetURL() != url_non_srp_2() &&
-                  handle->GetURL() != url_non_srp_redirect()) {
-                return nullptr;
+            base::BindLambdaForTesting([&](content::NavigationThrottleRegistry&
+                                               registry) -> void {
+              auto& handle = registry.GetNavigationHandle();
+              if (handle.GetURL() != url_non_srp_2() &&
+                  handle.GetURL() != url_non_srp_redirect()) {
+                return;
               }
               content::TestNavigationThrottle::ThrottleMethod method =
                   content::TestNavigationThrottle::WILL_START_REQUEST;
@@ -761,9 +761,9 @@
                 method = content::TestNavigationThrottle::WILL_PROCESS_RESPONSE;
               }
               auto throttle =
-                  std::make_unique<content::TestNavigationThrottle>(handle);
+                  std::make_unique<content::TestNavigationThrottle>(registry);
               throttle->SetResponse(method, synchrony, action);
-              return throttle;
+              registry.AddThrottle(std::move(throttle));
             }));
         TestNavigationAbandonment(
             AbandonReason::kInternalCancellation, milestone,
@@ -790,12 +790,12 @@
   for (NavigationMilestone milestone : all_throttleable_milestones()) {
     content::TestNavigationThrottleInserter throttle_inserter(
         web_contents(),
-        base::BindLambdaForTesting([&](content::NavigationHandle* handle)
-                                       -> std::unique_ptr<
-                                           content::NavigationThrottle> {
-          if (handle->GetURL() != url_non_srp_2() &&
-              handle->GetURL() != url_non_srp_redirect()) {
-            return nullptr;
+        base::BindLambdaForTesting([&](content::NavigationThrottleRegistry&
+                                           registry) -> void {
+          auto& handle = registry.GetNavigationHandle();
+          if (handle.GetURL() != url_non_srp_2() &&
+              handle.GetURL() != url_non_srp_redirect()) {
+            return;
           }
           content::TestNavigationThrottle::ThrottleMethod method =
               content::TestNavigationThrottle::WILL_START_REQUEST;
@@ -807,14 +807,14 @@
             method = content::TestNavigationThrottle::WILL_PROCESS_RESPONSE;
           }
           auto throttle =
-              std::make_unique<content::TestNavigationThrottle>(handle);
+              std::make_unique<content::TestNavigationThrottle>(registry);
           throttle->SetResponse(
               method, content::TestNavigationThrottle::SYNCHRONOUS,
               milestone ==
                       NavigationMilestone::kNonRedirectResponseLoaderCallback
                   ? content::NavigationThrottle::BLOCK_RESPONSE
                   : content::NavigationThrottle::BLOCK_REQUEST);
-          return throttle;
+          registry.AddThrottle(std::move(throttle));
         }));
     TestNavigationAbandonment(
         AbandonReason::kErrorPage, milestone,
diff --git a/chrome/browser/page_load_metrics/observers/gws_abandoned_page_load_metrics_observer_browsertest.cc b/chrome/browser/page_load_metrics/observers/gws_abandoned_page_load_metrics_observer_browsertest.cc
index 1b230f2..e979805 100644
--- a/chrome/browser/page_load_metrics/observers/gws_abandoned_page_load_metrics_observer_browsertest.cc
+++ b/chrome/browser/page_load_metrics/observers/gws_abandoned_page_load_metrics_observer_browsertest.cc
@@ -1186,30 +1186,27 @@
       for (NavigationMilestone milestone : all_throttleable_milestones()) {
         content::TestNavigationThrottleInserter throttle_inserter(
             web_contents(),
-            base::BindLambdaForTesting(
-                [&](content::NavigationHandle* handle)
-                    -> std::unique_ptr<content::NavigationThrottle> {
-                  if (handle->GetURL() != url_srp() &&
-                      handle->GetURL() != url_srp_redirect()) {
-                    return nullptr;
-                  }
-                  content::TestNavigationThrottle::ThrottleMethod method =
-                      content::TestNavigationThrottle::WILL_START_REQUEST;
-                  if (milestone == NavigationMilestone::
-                                       kFirstRedirectResponseLoaderCallback) {
-                    method =
-                        content::TestNavigationThrottle::WILL_REDIRECT_REQUEST;
-                  } else if (milestone ==
-                             NavigationMilestone::
-                                 kNonRedirectResponseLoaderCallback) {
-                    method =
-                        content::TestNavigationThrottle::WILL_PROCESS_RESPONSE;
-                  }
-                  auto throttle =
-                      std::make_unique<content::TestNavigationThrottle>(handle);
-                  throttle->SetResponse(method, synchrony, action);
-                  return throttle;
-                }));
+            base::BindLambdaForTesting([&](content::NavigationThrottleRegistry&
+                                               registry) -> void {
+              auto& handle = registry.GetNavigationHandle();
+              if (handle.GetURL() != url_srp() &&
+                  handle.GetURL() != url_srp_redirect()) {
+                return;
+              }
+              content::TestNavigationThrottle::ThrottleMethod method =
+                  content::TestNavigationThrottle::WILL_START_REQUEST;
+              if (milestone ==
+                  NavigationMilestone::kFirstRedirectResponseLoaderCallback) {
+                method = content::TestNavigationThrottle::WILL_REDIRECT_REQUEST;
+              } else if (milestone == NavigationMilestone::
+                                          kNonRedirectResponseLoaderCallback) {
+                method = content::TestNavigationThrottle::WILL_PROCESS_RESPONSE;
+              }
+              auto throttle =
+                  std::make_unique<content::TestNavigationThrottle>(registry);
+              throttle->SetResponse(method, synchrony, action);
+              registry.AddThrottle(std::move(throttle));
+            }));
         TestNavigationAbandonment(
             AbandonReason::kInternalCancellation, milestone,
             GetTargetURLForMilestone(milestone),
@@ -1235,32 +1232,32 @@
   for (NavigationMilestone milestone : all_throttleable_milestones()) {
     content::TestNavigationThrottleInserter throttle_inserter(
         web_contents(),
-        base::BindLambdaForTesting(
-            [&](content::NavigationHandle* handle)
-                -> std::unique_ptr<content::NavigationThrottle> {
-              if (handle->GetURL() != url_srp() &&
-                  handle->GetURL() != url_srp_redirect()) {
-                return nullptr;
-              }
-              content::TestNavigationThrottle::ThrottleMethod method =
-                  content::TestNavigationThrottle::WILL_START_REQUEST;
-              if (milestone ==
-                  NavigationMilestone::kFirstRedirectResponseLoaderCallback) {
-                method = content::TestNavigationThrottle::WILL_REDIRECT_REQUEST;
-              } else if (milestone == NavigationMilestone::
-                                          kNonRedirectResponseLoaderCallback) {
-                method = content::TestNavigationThrottle::WILL_PROCESS_RESPONSE;
-              }
-              auto throttle =
-                  std::make_unique<content::TestNavigationThrottle>(handle);
-              throttle->SetResponse(
-                  method, content::TestNavigationThrottle::SYNCHRONOUS,
-                  milestone == NavigationMilestone::
-                                   kNonRedirectResponseLoaderCallback
-                      ? content::NavigationThrottle::BLOCK_RESPONSE
-                      : content::NavigationThrottle::BLOCK_REQUEST);
-              return throttle;
-            }));
+        base::BindLambdaForTesting([&](content::NavigationThrottleRegistry&
+                                           registry) -> void {
+          auto& handle = registry.GetNavigationHandle();
+          if (handle.GetURL() != url_srp() &&
+              handle.GetURL() != url_srp_redirect()) {
+            return;
+          }
+          content::TestNavigationThrottle::ThrottleMethod method =
+              content::TestNavigationThrottle::WILL_START_REQUEST;
+          if (milestone ==
+              NavigationMilestone::kFirstRedirectResponseLoaderCallback) {
+            method = content::TestNavigationThrottle::WILL_REDIRECT_REQUEST;
+          } else if (milestone ==
+                     NavigationMilestone::kNonRedirectResponseLoaderCallback) {
+            method = content::TestNavigationThrottle::WILL_PROCESS_RESPONSE;
+          }
+          auto throttle =
+              std::make_unique<content::TestNavigationThrottle>(registry);
+          throttle->SetResponse(
+              method, content::TestNavigationThrottle::SYNCHRONOUS,
+              milestone ==
+                      NavigationMilestone::kNonRedirectResponseLoaderCallback
+                  ? content::NavigationThrottle::BLOCK_RESPONSE
+                  : content::NavigationThrottle::BLOCK_REQUEST);
+          registry.AddThrottle(std::move(throttle));
+        }));
     TestNavigationAbandonment(
         AbandonReason::kErrorPage, milestone,
         GetTargetURLForMilestone(milestone),
diff --git a/chrome/browser/pdf/pdf_extension_util.cc b/chrome/browser/pdf/pdf_extension_util.cc
index d55b118..8967bad 100644
--- a/chrome/browser/pdf/pdf_extension_util.cc
+++ b/chrome/browser/pdf/pdf_extension_util.cc
@@ -33,11 +33,14 @@
 #include "url/gurl.h"
 
 #if BUILDFLAG(IS_CHROMEOS)
+#include "chromeos/ash/components/browser_context_helper/browser_context_types.h"
+#endif  // BUILDFLAG(IS_CHROMEOS)
+
+#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(ENABLE_PDF_INK2)
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/pref_names.h"
-#include "chromeos/ash/components/browser_context_helper/browser_context_types.h"
 #include "components/prefs/pref_service.h"
-#endif  // BUILDFLAG(IS_CHROMEOS)
+#endif  // BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(ENABLE_PDF_INK2)
 
 namespace pdf_extension_util {
 
@@ -240,19 +243,25 @@
 #endif  // BUILDFLAG(IS_CHROMEOS)
 }
 
-#if BUILDFLAG(IS_CHROMEOS)
-bool IsPdfInk1AnnotationsEnabled(content::BrowserContext* context) {
+#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(ENABLE_PDF_INK2)
+bool IsPdfAnnotationsEnabledByPolicy(content::BrowserContext* context) {
   PrefService* prefs =
       context ? Profile::FromBrowserContext(context)->GetPrefs() : nullptr;
   return !prefs || !prefs->IsManagedPreference(prefs::kPdfAnnotationsEnabled) ||
          prefs->GetBoolean(prefs::kPdfAnnotationsEnabled);
 }
+#endif  // BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(ENABLE_PDF_INK2)
+
+#if BUILDFLAG(IS_CHROMEOS)
+bool IsPdfInk1AnnotationsEnabled(content::BrowserContext* context) {
+  return IsPdfAnnotationsEnabledByPolicy(context);
+}
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
 #if BUILDFLAG(ENABLE_PDF_INK2)
-bool IsPdfInk2AnnotationsEnabled() {
-  // TODO(crbug.com/414844437): Support `kPdfAnnotationsEnabled` policy.
-  return base::FeatureList::IsEnabled(chrome_pdf::features::kPdfInk2);
+bool IsPdfInk2AnnotationsEnabled(content::BrowserContext* context) {
+  return base::FeatureList::IsEnabled(chrome_pdf::features::kPdfInk2) &&
+         IsPdfAnnotationsEnabledByPolicy(context);
 }
 #endif  // BUILDFLAG(ENABLE_PDF_INK2)
 
@@ -299,7 +308,7 @@
 #endif
 
 #if BUILDFLAG(ENABLE_PDF_INK2)
-  const bool use_ink2 = IsPdfInk2AnnotationsEnabled();
+  const bool use_ink2 = IsPdfInk2AnnotationsEnabled(context);
   dict->Set("pdfInk2Enabled", use_ink2);
   dict->Set("pdfTextAnnotationsEnabled",
             use_ink2 && chrome_pdf::features::kPdfInk2TextAnnotations.Get());
diff --git a/chrome/browser/pdf/pdf_searchify_browsertest.cc b/chrome/browser/pdf/pdf_searchify_browsertest.cc
index ec04684..4ca6207d 100644
--- a/chrome/browser/pdf/pdf_searchify_browsertest.cc
+++ b/chrome/browser/pdf/pdf_searchify_browsertest.cc
@@ -2,19 +2,32 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/command_line.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/run_until.h"
 #include "base/test/test_future.h"
 #include "base/time/time.h"
 #include "chrome/browser/pdf/pdf_extension_test_base.h"
 #include "chrome/browser/screen_ai/screen_ai_install_state.h"
+#include "components/metrics/content/subprocess_metrics_provider.h"
 #include "components/pdf/browser/pdf_document_helper.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "pdf/pdf_features.h"
 #include "services/screen_ai/public/cpp/utilities.h"
 #include "ui/accessibility/accessibility_features.h"
+#include "ui/accessibility/accessibility_switches.h"
 #include "ui/accessibility/ax_features.mojom-features.h"
 
+namespace {
+
+bool IsScreenReaderEnabled() {
+  return base::CommandLine::ForCurrentProcess()->HasSwitch(
+      switches::kForceRendererAccessibility);
+}
+
+}  // namespace
+
 // Parameter: Searchify (ScreenAI OCR) availability.
 class PDFSearchifyTest : public PDFExtensionTestBase,
                          public screen_ai::ScreenAIInstallState::Observer,
@@ -80,7 +93,7 @@
 
   // Searchify may be slow, so if the test expects text, `repeat_until_has_text`
   // should be set to true to repeat getting page text until it's not empty.
-  std::u16string GetPageText(bool repeat_until_has_text) {
+  std::u16string GetPageText(int32_t page_index, bool repeat_until_has_text) {
     auto* helper =
         pdf::PDFDocumentHelper::MaybeGetForWebContents(GetActiveWebContents());
     if (!helper) {
@@ -89,10 +102,10 @@
     }
 
     std::u16string result;
-    EXPECT_TRUE(
-        base::test::RunUntil([&helper, &repeat_until_has_text, &result]() {
+    EXPECT_TRUE(base::test::RunUntil(
+        [&helper, &page_index, &repeat_until_has_text, &result]() {
           base::test::TestFuture<const std::u16string&> future;
-          helper->GetPageText(0, future.GetCallback());
+          helper->GetPageText(page_index, future.GetCallback());
           EXPECT_TRUE(future.Wait());
           result = future.Take();
           return !result.empty() || !repeat_until_has_text;
@@ -116,24 +129,66 @@
 #endif
 );
 
-IN_PROC_BROWSER_TEST_P(PDFSearchifyTest, HelloWorld) {
 // TODO(crbug.com/406839385): Re-enable this test on Mac.
 #if BUILDFLAG(IS_MAC)
-  GTEST_SKIP() << "Disable on macOS";
+#define MAYBE_HelloWorld DISABLED_HelloWorld
+#else
+#define MAYBE_HelloWorld HelloWorld
 #endif
+IN_PROC_BROWSER_TEST_P(PDFSearchifyTest, MAYBE_HelloWorld) {
+  base::HistogramTester histograms;
   ASSERT_TRUE(LoadPdf(embedded_test_server()->GetURL(
       "/pdf/accessibility/hello-world-in-image.pdf")));
 
-  std::u16string page_text = GetPageText(/*repeat_until_has_text=*/
-                                         IsSearchifyActive());
+  std::u16string page_text =
+      GetPageText(/*page_idex=*/0, /*repeat_until_has_text=*/
+                  IsSearchifyActive());
 
   EXPECT_EQ(page_text, IsSearchifyActive() ? u"Hello, world!" : u"");
+
+  metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
+
+  histograms.ExpectUniqueSample("PDF.PageHasText", false, 1);
+
+  // `ScreenReaderModeEnabled` is recorded only when searchify is active.
+  histograms.ExpectUniqueSample(
+      "Accessibility.ScreenAI.Searchify.ScreenReaderModeEnabled",
+      IsScreenReaderEnabled(), IsSearchifyActive());
 }
 
-// TODO(crbug.com/360803943): Add metrics tests similar to
-// `PdfOcrIntegrationTest`.
+// TODO(crbug.com/406839385): Re-enable this test on Mac.
+#if BUILDFLAG(IS_MAC)
+#define MAYBE_MultiPage DISABLED_MultiPage
+#else
+#define MAYBE_MultiPage MultiPage
+#endif
+IN_PROC_BROWSER_TEST_P(PDFSearchifyTest, MAYBE_MultiPage) {
+  base::HistogramTester histograms;
+  ASSERT_TRUE(LoadPdf(embedded_test_server()->GetURL(
+      "/pdf/accessibility/inaccessible-text-in-three-page.pdf")));
 
-// TODO(crbug.com/360803943): Consider adding save tests like
+  static constexpr std::u16string_view kExpectedTexts[3] = {
+      u"Hello, world!", u"Paragraph 1 on Page 2Paragraph 2 on Page 2",
+      u"Paragraph 1 on Page 3Paragraph 2 on Page 3"};
+
+  int page_index = 0;
+  for (const auto& expected_text : kExpectedTexts) {
+    std::u16string page_text =
+        GetPageText(page_index++, /*repeat_until_has_text=*/
+                    IsSearchifyActive());
+    EXPECT_EQ(page_text, IsSearchifyActive() ? expected_text : u"");
+  }
+
+  metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
+
+  histograms.ExpectUniqueSample("PDF.PageHasText", false, 3);
+
+  // `ScreenReaderModeEnabled` is recorded only when searchify is active and is
+  // recorded only once for each PDF.
+  histograms.ExpectUniqueSample(
+      "Accessibility.ScreenAI.Searchify.ScreenReaderModeEnabled",
+      IsScreenReaderEnabled(), IsSearchifyActive() ? 1 : 0);
+}
+
+// TODO(crbug.com/382610226): Consider adding save tests like
 // pdf_extension_download_test.cc
-
-// TODO(crbug.com/360803943): Add multi-page tests.
diff --git a/chrome/browser/picture_in_picture/document_picture_in_picture_window_controller_browsertest.cc b/chrome/browser/picture_in_picture/document_picture_in_picture_window_controller_browsertest.cc
index dfc07bf9..0f835365 100644
--- a/chrome/browser/picture_in_picture/document_picture_in_picture_window_controller_browsertest.cc
+++ b/chrome/browser/picture_in_picture/document_picture_in_picture_window_controller_browsertest.cc
@@ -13,6 +13,7 @@
 #include "base/scoped_observation.h"
 #include "base/strings/strcat.h"
 #include "base/strings/to_string.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/test_timeouts.h"
 #include "build/build_config.h"
@@ -260,6 +261,22 @@
     EXPECT_NE(browser_view->GetBounds().origin(), gfx::Point(0, 0));
   }
 
+  const std::string GetPipWindowPageTitle(content::WebContents* web_contents) {
+    return EvalJs(web_contents, "getPipWindowPageTitle();").ExtractString();
+  }
+
+  void SetPipWindowPageTitle(content::WebContents* web_contents,
+                             std::string title) {
+    std::string script =
+        base::StrCat({"setPipWindowPageTitle(\"", title, "\");"});
+
+    ASSERT_EQ(true, EvalJs(web_contents, script));
+  }
+
+  const std::string GetWindowPageTitle(content::WebContents* web_contents) {
+    return EvalJs(web_contents, "getWindowPageTitle();").ExtractString();
+  }
+
   // Watch for destruction of a WebContents. `is_destroyed()` will report if the
   // WebContents has been destroyed yet.
   class DestructionObserver : public content::WebContentsObserver {
@@ -920,3 +937,42 @@
   // The picture-in-picture window should no longer have system focus.
   EXPECT_TRUE(widget_activation_waiter.WaitForActivationState(false));
 }
+
+IN_PROC_BROWSER_TEST_F(DocumentPictureInPictureWindowControllerBrowserTest,
+                       AccessibleTabLabelReturnsCorrectTitle) {
+  LoadTabAndEnterPictureInPicture(browser());
+  auto* opener_web_contents = window_controller()->GetWebContents();
+  auto* pip_web_contents = window_controller()->GetChildWebContents();
+  ASSERT_NE(nullptr, pip_web_contents);
+  WaitForPageLoad(pip_web_contents);
+  auto* browser_view = static_cast<BrowserView*>(
+      BrowserWindow::FindBrowserWindowWithWebContents(pip_web_contents));
+
+  // Verify that the pip window page title is empty and, the opener window page
+  // title is not.
+  const std::string pip_window_page_title =
+      GetPipWindowPageTitle(opener_web_contents);
+  EXPECT_TRUE(pip_window_page_title.empty());
+  const std::string window_page_title = GetWindowPageTitle(opener_web_contents);
+  EXPECT_FALSE(window_page_title.empty());
+
+  // Verify that the accessible label returns the opener window page title, when
+  // the pip window page title is not set.
+  EXPECT_EQ(base::UTF8ToUTF16(window_page_title),
+            browser_view->GetAccessibleTabLabel(
+                browser()->tab_strip_model()->active_index()));
+
+  // Set the pip window page title and ensure that the pip and opener window
+  // page titles are different.
+  const std::string new_pip_window_page_title = "Test PiP Page Title";
+  SetPipWindowPageTitle(opener_web_contents, new_pip_window_page_title);
+  EXPECT_EQ(new_pip_window_page_title,
+            GetPipWindowPageTitle(opener_web_contents));
+  EXPECT_NE(new_pip_window_page_title, window_page_title);
+
+  // Verify that, although the pip window page title is set, the accessible
+  // label returns the opener window page title.
+  EXPECT_EQ(base::UTF8ToUTF16(window_page_title),
+            browser_view->GetAccessibleTabLabel(
+                browser()->tab_strip_model()->active_index()));
+}
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index 79f1bc2e..82373bd 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -2176,10 +2176,13 @@
     prefs::kSuppressDifferentOriginSubframeJSDialogs,
     base::Value::Type::BOOLEAN },
 
-#if BUILDFLAG(IS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(ENABLE_PDF_INK2)
   { key::kPdfAnnotationsEnabled,
     prefs::kPdfAnnotationsEnabled,
     base::Value::Type::BOOLEAN },
+#endif  // BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(ENABLE_PDF_INK2)
+
+#if BUILDFLAG(IS_CHROMEOS)
   { key::kLensOnGalleryEnabled,
     prefs::kMediaAppLensEnabled,
     base::Value::Type::BOOLEAN },
diff --git a/chrome/browser/predictors/loading_predictor_browsertest.cc b/chrome/browser/predictors/loading_predictor_browsertest.cc
index 83d46c936..e655e253 100644
--- a/chrome/browser/predictors/loading_predictor_browsertest.cc
+++ b/chrome/browser/predictors/loading_predictor_browsertest.cc
@@ -224,7 +224,8 @@
   void OnPreresolveFinished(
       const GURL& url,
       const net::NetworkAnonymizationKey& network_anonymization_key,
-      mojo::PendingRemote<network::mojom::ReconnectEventObserver>& observer,
+      mojo::PendingRemote<network::mojom::ConnectionChangeObserverClient>&
+          observer,
       bool success) override {
     ResolveHostRequestInfo preconnect_info{url.host(),
                                            network_anonymization_key};
diff --git a/chrome/browser/predictors/loading_predictor_unittest.cc b/chrome/browser/predictors/loading_predictor_unittest.cc
index 2421b52..8eb9abe 100644
--- a/chrome/browser/predictors/loading_predictor_unittest.cc
+++ b/chrome/browser/predictors/loading_predictor_unittest.cc
@@ -69,8 +69,8 @@
            net::NetworkTrafficAnnotationTag traffic_annotation,
            const content::StoragePartitionConfig*,
            std::optional<net::ConnectionKeepAliveConfig> keepalive_config,
-           mojo::PendingRemote<network::mojom::ReconnectEventObserver>
-               reconnect_event_observer));
+           mojo::PendingRemote<network::mojom::ConnectionChangeObserverClient>
+               observer_client));
   MOCK_METHOD1(Stop, void(const GURL& url));
 
   void Start(const GURL& url,
diff --git a/chrome/browser/predictors/preconnect_manager.cc b/chrome/browser/predictors/preconnect_manager.cc
index 037b6248..6069dd81 100644
--- a/chrome/browser/predictors/preconnect_manager.cc
+++ b/chrome/browser/predictors/preconnect_manager.cc
@@ -23,8 +23,8 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/storage_partition.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
+#include "services/network/public/mojom/connection_change_observer_client.mojom.h"
 #include "services/network/public/mojom/network_context.mojom.h"
-#include "services/network/public/mojom/reconnect_event_observer.mojom.h"
 
 namespace predictors {
 
@@ -59,8 +59,8 @@
     net::NetworkTrafficAnnotationTag traffic_annotation_tag,
     std::optional<content::StoragePartitionConfig> storage_partition_config,
     std::optional<net::ConnectionKeepAliveConfig> keepalive_config,
-    mojo::PendingRemote<network::mojom::ReconnectEventObserver>
-        reconnect_event_observer,
+    mojo::PendingRemote<network::mojom::ConnectionChangeObserverClient>
+        connection_change_observer_client,
     PreresolveInfo* info)
     : url(url),
       num_sockets(num_sockets),
@@ -69,7 +69,8 @@
       traffic_annotation_tag(std::move(traffic_annotation_tag)),
       storage_partition_config(std::move(storage_partition_config)),
       keepalive_config(std::move(keepalive_config)),
-      reconnect_event_observer(std::move(reconnect_event_observer)),
+      connection_change_observer_client(
+          std::move(connection_change_observer_client)),
       info(info),
       creation_time(base::TimeTicks::Now()) {
   DCHECK_GE(num_sockets, 0);
@@ -190,8 +191,8 @@
     net::NetworkTrafficAnnotationTag traffic_annotation,
     const content::StoragePartitionConfig* storage_partition_config,
     std::optional<net::ConnectionKeepAliveConfig> keepalive_config,
-    mojo::PendingRemote<network::mojom::ReconnectEventObserver>
-        reconnect_event_observer) {
+    mojo::PendingRemote<network::mojom::ConnectionChangeObserverClient>
+        connection_change_observer_client) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   if (!IsEnabled())
     return;
@@ -201,7 +202,7 @@
       url.DeprecatedGetOriginAsURL(), 1, allow_credentials,
       std::move(network_anonymization_key), traffic_annotation,
       base::OptionalFromPtr(storage_partition_config),
-      std::move(keepalive_config), std::move(reconnect_event_observer),
+      std::move(keepalive_config), std::move(connection_change_observer_client),
       nullptr));
   queued_jobs_.push_front(job_id);
 
@@ -226,8 +227,8 @@
     const net::NetworkTrafficAnnotationTag& traffic_annotation,
     const content::StoragePartitionConfig* storage_partition_config,
     std::optional<net::ConnectionKeepAliveConfig> keepalive_config,
-    mojo::PendingRemote<network::mojom::ReconnectEventObserver>
-        reconnect_event_observer) const {
+    mojo::PendingRemote<network::mojom::ConnectionChangeObserverClient>
+        connection_change_observer_client) const {
   DCHECK(url.DeprecatedGetOriginAsURL() == url);
   DCHECK(url.SchemeIsHTTPOrHTTPS());
   if (observer_)
@@ -251,7 +252,8 @@
                         : network::mojom::CredentialsMode::kOmit,
       network_anonymization_key,
       net::MutableNetworkTrafficAnnotationTag(traffic_annotation),
-      std::move(keepalive_config), std::move(reconnect_event_observer));
+      std::move(keepalive_config),
+      std::move(connection_change_observer_client));
 }
 
 std::unique_ptr<ResolveHostClientImpl> PreconnectManager::PreresolveUrl(
@@ -341,7 +343,8 @@
 
   if (observer_)
     observer_->OnPreresolveFinished(job->url, job->network_anonymization_key,
-                                    job->reconnect_event_observer, success);
+                                    job->connection_change_observer_client,
+                                    success);
 
   job->resolve_host_client = nullptr;
   FinishPreresolveJob(job_id, success);
@@ -382,7 +385,7 @@
                   job->network_anonymization_key, job->traffic_annotation_tag,
                   base::OptionalToPtr(job->storage_partition_config),
                   std::move(job->keepalive_config),
-                  std::move(job->reconnect_event_observer));
+                  std::move(job->connection_change_observer_client));
   }
 
   PreresolveInfo* info = job->info;
diff --git a/chrome/browser/predictors/preconnect_manager.h b/chrome/browser/predictors/preconnect_manager.h
index 7ffcb52..666d9fb 100644
--- a/chrome/browser/predictors/preconnect_manager.h
+++ b/chrome/browser/predictors/preconnect_manager.h
@@ -20,7 +20,7 @@
 #include "chrome/browser/predictors/resource_prefetch_predictor.h"
 #include "content/public/browser/storage_partition_config.h"
 #include "net/base/network_anonymization_key.h"
-#include "services/network/public/mojom/reconnect_event_observer.mojom.h"
+#include "services/network/public/mojom/connection_change_observer_client.mojom.h"
 #include "url/gurl.h"
 
 namespace content {
@@ -89,8 +89,8 @@
       net::NetworkTrafficAnnotationTag traffic_annotation_tag,
       std::optional<content::StoragePartitionConfig> storage_partition_config,
       std::optional<net::ConnectionKeepAliveConfig> keepalive_config,
-      mojo::PendingRemote<network::mojom::ReconnectEventObserver>
-          reconnect_event_observer,
+      mojo::PendingRemote<network::mojom::ConnectionChangeObserverClient>
+          connection_change_observer_client,
       PreresolveInfo* info);
 
   PreresolveJob(const PreresolveJob&) = delete;
@@ -114,8 +114,8 @@
   std::optional<content::StoragePartitionConfig> storage_partition_config;
 
   std::optional<net::ConnectionKeepAliveConfig> keepalive_config;
-  mojo::PendingRemote<network::mojom::ReconnectEventObserver>
-      reconnect_event_observer;
+  mojo::PendingRemote<network::mojom::ConnectionChangeObserverClient>
+      connection_change_observer_client;
   // Raw pointer usage is fine here because even though PreresolveJob can
   // outlive PreresolveInfo. It's only accessed on PreconnectManager class
   // context and PreresolveInfo lifetime is tied to PreconnectManager.
@@ -165,7 +165,8 @@
     virtual void OnPreresolveFinished(
         const GURL& url,
         const net::NetworkAnonymizationKey& network_anonymization_key,
-        mojo::PendingRemote<network::mojom::ReconnectEventObserver>& observer,
+        mojo::PendingRemote<network::mojom::ConnectionChangeObserverClient>&
+            observer,
         bool success) {}
     virtual void OnProxyLookupFinished(
         const GURL& url,
@@ -211,8 +212,8 @@
       net::NetworkTrafficAnnotationTag traffic_annotation,
       const content::StoragePartitionConfig* storage_partition_config,
       std::optional<net::ConnectionKeepAliveConfig> keepalive_config,
-      mojo::PendingRemote<network::mojom::ReconnectEventObserver>
-          reconnect_event_observer);
+      mojo::PendingRemote<network::mojom::ConnectionChangeObserverClient>
+          connection_change_observer_client);
 
   // No additional jobs associated with the |url| will be queued after this.
   virtual void Stop(const GURL& url);
@@ -241,8 +242,8 @@
       const net::NetworkTrafficAnnotationTag& traffic_annotation,
       const content::StoragePartitionConfig* storage_partition_config,
       std::optional<net::ConnectionKeepAliveConfig> keepalive_config,
-      mojo::PendingRemote<network::mojom::ReconnectEventObserver>
-          reconnect_event_observer) const;
+      mojo::PendingRemote<network::mojom::ConnectionChangeObserverClient>
+          connection_change_observer_client) const;
   std::unique_ptr<ResolveHostClientImpl> PreresolveUrl(
       const GURL& url,
       const net::NetworkAnonymizationKey& network_anonymization_key,
diff --git a/chrome/browser/predictors/preconnect_manager_unittest.cc b/chrome/browser/predictors/preconnect_manager_unittest.cc
index 69f7d3f3..7ce1c35 100644
--- a/chrome/browser/predictors/preconnect_manager_unittest.cc
+++ b/chrome/browser/predictors/preconnect_manager_unittest.cc
@@ -29,7 +29,7 @@
 #include "net/base/network_anonymization_key.h"
 #include "net/dns/public/resolve_error_info.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
-#include "services/network/public/mojom/reconnect_event_observer.mojom.h"
+#include "services/network/public/mojom/connection_change_observer_client.mojom.h"
 #include "services/network/test/test_network_context.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -174,8 +174,8 @@
           const net::NetworkAnonymizationKey& network_anonymization_key,
           const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
           const std::optional<net::ConnectionKeepAliveConfig>& keepalive_config,
-          mojo::PendingRemote<network::mojom::ReconnectEventObserver>
-              reconnect_event_observer));
+          mojo::PendingRemote<network::mojom::ConnectionChangeObserverClient>
+              observer_client));
 
  private:
   bool IsHangingHost(const GURL& url) const {
diff --git a/chrome/browser/privacy_sandbox/notice/BUILD.gn b/chrome/browser/privacy_sandbox/notice/BUILD.gn
index 52fa03bb..37c81cd 100644
--- a/chrome/browser/privacy_sandbox/notice/BUILD.gn
+++ b/chrome/browser/privacy_sandbox/notice/BUILD.gn
@@ -94,7 +94,6 @@
       "//chrome/browser/ui/browser_window:browser_window",
       "//chrome/common",
       "//components/tabs:public",
-      "//components/web_modal:web_modal",
       "//content/public/browser",
     ]
   }
diff --git a/chrome/browser/privacy_sandbox/notice/desktop_entrypoint_handlers.cc b/chrome/browser/privacy_sandbox/notice/desktop_entrypoint_handlers.cc
index bd94f8e54..6b349244 100644
--- a/chrome/browser/privacy_sandbox/notice/desktop_entrypoint_handlers.cc
+++ b/chrome/browser/privacy_sandbox/notice/desktop_entrypoint_handlers.cc
@@ -9,13 +9,9 @@
 #include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
 #include "chrome/common/webui_url_constants.h"
 #include "components/tabs/public/tab_interface.h"
-#include "components/web_modal/web_contents_modal_dialog_host.h"
 #include "content/public/browser/navigation_handle.h"
 
 namespace privacy_sandbox {
-
-constexpr int kMinRequiredDialogHeight = 100;
-
 //-----------------------------------------------------------------------------
 // EntryPointHandler
 //-----------------------------------------------------------------------------
@@ -87,25 +83,8 @@
     return;
   }
 
-  // Avoid showing the prompt on popups, pip, anything that isn't a normal
-  // browser.
-  if (browser_window_interface->GetType() !=
-      BrowserWindowInterface::TYPE_NORMAL) {
-    return;
-  }
-
-  // If the windows height is too small, it is difficult to read or interact
-  // with the dialog. The dialog is blocking modal, that is why we want to
-  // prevent it from showing if there isn't enough space. The PrivacySandbox
-  // prompt can always fit inside a normal tabbed window due to its minimum
-  // width, so checking the height is enough here.
-  auto* web_contents =
-      browser_window_interface->GetWebContentsModalDialogHostForWindow();
-  if (!web_contents || web_contents->GetMaximumDialogSize().height() <
-                           kMinRequiredDialogHeight) {
-    return;
-  }
-
+  // TODO(crbug.com/408016824):  Finish implementing checks needed before
+  // showing notice.
   // TODO(crbug.com/408016824):  Add error-event histograms.
 
   HandleEntryPoint(browser_window_interface);
diff --git a/chrome/browser/privacy_sandbox/notice/desktop_entrypoint_handlers_browsertest.cc b/chrome/browser/privacy_sandbox/notice/desktop_entrypoint_handlers_browsertest.cc
index 774814c..a2a20a9c 100644
--- a/chrome/browser/privacy_sandbox/notice/desktop_entrypoint_handlers_browsertest.cc
+++ b/chrome/browser/privacy_sandbox/notice/desktop_entrypoint_handlers_browsertest.cc
@@ -55,18 +55,7 @@
   std::unique_ptr<MockDesktopViewManager> mock_view_manager_;
 };
 
-// Test that navigation alerts view manager.
-IN_PROC_BROWSER_TEST_F(PrivacySandboxNoticeEntryPointHandlersTest,
-                       TestNavigationCallsEntryPointCallback) {
-  EXPECT_CALL(*mock_view_manager_.get(), HandleChromeOwnedPageNavigation)
-      .Times(1);
-  // Navigate
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(),
-                                           GURL(chrome::kChromeUINewTabURL)));
-  Mock::VerifyAndClearExpectations(mock_view_manager_.get());
-}
-
-// Test that navigation alerts view manager.
+// Test that navigation to unsuitable URLS do not alert view manager.
 IN_PROC_BROWSER_TEST_F(PrivacySandboxNoticeEntryPointHandlersTest,
                        UnsuitableUrl) {
   EXPECT_CALL(*mock_view_manager_.get(), HandleChromeOwnedPageNavigation)
@@ -77,45 +66,38 @@
       GURL(chrome::kChromeUISettingsURL).Resolve(chrome::kAutofillSubPage)};
 
   for (size_t i = 0; i < urls_to_open.size(); ++i) {
-    if (i == 0) {
-      // Open the first URL in a new tab to trigger a new tab helper.
-      ASSERT_TRUE(ui_test_utils::NavigateToURLWithDisposition(
-          browser(), urls_to_open[i], WindowOpenDisposition::NEW_FOREGROUND_TAB,
-          ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP));
-    } else {
-      ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), urls_to_open[i]));
-    }
+    ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), urls_to_open[i]));
   }
 
   Mock::VerifyAndClearExpectations(mock_view_manager_.get());
 }
 
-IN_PROC_BROWSER_TEST_F(PrivacySandboxNoticeEntryPointHandlersTest,
-                       NoPromptInNonNormalBrowser) {
+class PrivacySandboxNoticeEntryPointHandlersTest_SuitableUrls
+    : public PrivacySandboxNoticeEntryPointHandlersTest,
+      public testing::WithParamInterface<GURL> {};
+
+// Test that navigation to suitable URLS alert view manager.
+IN_PROC_BROWSER_TEST_P(PrivacySandboxNoticeEntryPointHandlersTest_SuitableUrls,
+                       SuitableUrl) {
+  GURL url_to_open = GetParam();
+
   EXPECT_CALL(*mock_view_manager_.get(), HandleChromeOwnedPageNavigation)
-      .Times(0);
+      .Times(1);
 
-  NavigateParams params(browser(), GURL(chrome::kChromeUINewTabPageURL),
-                        ui::PAGE_TRANSITION_FIRST);
-  params.window_action = NavigateParams::SHOW_WINDOW;
-  params.disposition = WindowOpenDisposition::NEW_POPUP;
-
-  ui_test_utils::NavigateToURL(&params);
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url_to_open));
 
   Mock::VerifyAndClearExpectations(mock_view_manager_.get());
 }
 
-IN_PROC_BROWSER_TEST_F(PrivacySandboxNoticeEntryPointHandlersTest,
-                       NoPromptInSmallBrowser) {
-  EXPECT_CALL(*mock_view_manager_.get(), HandleChromeOwnedPageNavigation)
-      .Times(0);
-
-  ui_test_utils::SetAndWaitForBounds(*browser(), gfx::Rect(0, 0, 50, 50));
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(),
-                                           GURL(chrome::kChromeUISettingsURL)));
-
-  Mock::VerifyAndClearExpectations(mock_view_manager_.get());
-}
+// Define the test parameters.
+INSTANTIATE_TEST_SUITE_P(
+    AllSuitableUrls,
+    PrivacySandboxNoticeEntryPointHandlersTest_SuitableUrls,
+    testing::Values(GURL(chrome::kChromeUINewTabURL),
+                    GURL(chrome::kChromeUINewTabPageURL),
+                    GURL(url::kAboutBlankURL),
+                    GURL(chrome::kChromeUISettingsURL),
+                    GURL(chrome::kChromeUIHistoryURL)));
 
 }  // namespace
 }  // namespace privacy_sandbox
diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc
index 2244580..4a2103e 100644
--- a/chrome/browser/profiles/profile_impl.cc
+++ b/chrome/browser/profiles/profile_impl.cc
@@ -435,7 +435,7 @@
 #endif  // !BUILDFLAG(IS_ANDROID)
   registry->RegisterTimePref(prefs::kProfileCreationTime, base::Time());
 
-#if BUILDFLAG(IS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(ENABLE_PDF_INK2)
   registry->RegisterBooleanPref(prefs::kPdfAnnotationsEnabled, true);
 #endif
   registry->RegisterIntegerPref(prefs::kEnterpriseBadgingTemporarySetting, 0);
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu_unittest.cc b/chrome/browser/renderer_context_menu/render_view_context_menu_unittest.cc
index 1102bfbc..f1c3f3f 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu_unittest.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu_unittest.cc
@@ -558,7 +558,8 @@
   void OnPreresolveFinished(
       const GURL& url,
       const net::NetworkAnonymizationKey& network_anonymization_key,
-      mojo::PendingRemote<network::mojom::ReconnectEventObserver>& observer,
+      mojo::PendingRemote<network::mojom::ConnectionChangeObserverClient>&
+          observer,
       bool success) override {
     last_preresolved_url_ = url;
     if (!preresolved_finished_closure_.is_null()) {
diff --git a/chrome/browser/resources/bookmarks/command_manager.ts b/chrome/browser/resources/bookmarks/command_manager.ts
index b609657f..c6135de 100644
--- a/chrome/browser/resources/bookmarks/command_manager.ts
+++ b/chrome/browser/resources/bookmarks/command_manager.ts
@@ -403,12 +403,16 @@
         chrome.bookmarkManagerPrivate.redo();
         break;
       case Command.OPEN_INCOGNITO:
-      case Command.OPEN_NEW_GROUP:
       case Command.OPEN_NEW_TAB:
       case Command.OPEN_NEW_WINDOW:
       case Command.OPEN_SPLIT_VIEW:
         this.openBookmarkIds_(this.expandIds_(itemIds), command);
         break;
+      case Command.OPEN_NEW_GROUP:
+        // Do not expand itemsIds because the folder node is needed to associate
+        // with a tab group.
+        this.openBookmarkIds_(Array.from(itemIds), command);
+        break;
       case Command.OPEN:
         if (this.isFolder_(itemIds)) {
           const folderId = Array.from(itemIds)[0]!;
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access_manifest.json.jinja2 b/chrome/browser/resources/chromeos/accessibility/switch_access_manifest.json.jinja2
index b9ac2639..f356f254 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access_manifest.json.jinja2
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access_manifest.json.jinja2
@@ -41,7 +41,7 @@
       "all_frames": true,
 {% if is_manifest_v3 == '1' %}
       "js": [
-        "switch_access/gdocs_script.js"
+        "switch_access/mv3/gdocs_script.js"
       ],
       "world": "MAIN",
 {% else %}
diff --git a/chrome/browser/resources/chromeos/login/components/throbber_notice.html b/chrome/browser/resources/chromeos/login/components/throbber_notice.html
index 221f2e0..7b69e87 100644
--- a/chrome/browser/resources/chromeos/login/components/throbber_notice.html
+++ b/chrome/browser/resources/chromeos/login/components/throbber_notice.html
@@ -6,7 +6,7 @@
 
 <!-- TODO(http://crbug.com/1172980): Delete once oobe-loading-dialog replaces throbber-notice -->
 <style include="cros-color-overrides oobe-common-styles">
-  :host {
+  :host(:not([hidden])) {
     align-items: center;
     display: flex;
     flex-direction: column;
@@ -14,6 +14,10 @@
     min-height: 0;
   }
 
+  :host([hidden]) {
+    display: none !important;
+  }
+
   paper-spinner-lite {
     height: 38px;
     margin-bottom: 28px;
diff --git a/chrome/browser/resources/chromeos/login/screens/common/app_launch_splash.html b/chrome/browser/resources/chromeos/login/screens/common/app_launch_splash.html
index 02758e7..dcbe82c 100644
--- a/chrome/browser/resources/chromeos/login/screens/common/app_launch_splash.html
+++ b/chrome/browser/resources/chromeos/login/screens/common/app_launch_splash.html
@@ -96,7 +96,7 @@
       [[i18nDynamic(locale, 'configureNetwork')]]
     </a>
   </div>
-  <throbber-notice>
+  <throbber-notice hidden$="[[!showThrobber]]">
   </throbber-notice>
 </div>
 <p id="shortcutInfo">
diff --git a/chrome/browser/resources/chromeos/login/screens/common/app_launch_splash.ts b/chrome/browser/resources/chromeos/login/screens/common/app_launch_splash.ts
index 3acc487..dd48deef 100644
--- a/chrome/browser/resources/chromeos/login/screens/common/app_launch_splash.ts
+++ b/chrome/browser/resources/chromeos/login/screens/common/app_launch_splash.ts
@@ -60,15 +60,22 @@
         type: String,
         value: '',
       },
+      showThrobber: {
+        type: Boolean,
+        value: true,
+      },
     };
   }
 
   private appName: string;
   private appUrl: string;
   private launchText: string;
+  private showThrobber: boolean;
 
   override get EXTERNAL_API(): string[] {
-    return ['toggleNetworkConfig', 'setAppData', 'updateMessage'];
+    return [
+      'toggleNetworkConfig', 'setAppData', 'updateMessage', 'hideThrobber'
+    ];
   }
 
   override ready(): void {
@@ -119,6 +126,7 @@
     if (data) {
       this.setAppData(data);
     }
+    this.showThrobber = true;
   }
 
   setAppData(data: AppLaunchSplashScreenData): void {
@@ -163,6 +171,10 @@
   updateMessage(message: string): void {
     this.launchText = message;
   }
+
+  hideThrobber(): void {
+    this.showThrobber = false;
+  }
 }
 
 declare global {
diff --git a/chrome/browser/resources/lens/overlay/overlay_border_glow.css b/chrome/browser/resources/lens/overlay/overlay_border_glow.css
index 3e127ee..b7e5e24 100644
--- a/chrome/browser/resources/lens/overlay/overlay_border_glow.css
+++ b/chrome/browser/resources/lens/overlay/overlay_border_glow.css
@@ -10,17 +10,9 @@
 :host {
   --gradient-blue: #3186ff;
   --gradient-green: #34a853;
+  --gradient-mask-opacity-middle-val: 0;
   --gradient-red: #ff4641;
-  --gradient-rotation-angle: 0deg;
   --gradient-yellow: #ffd314;
-  height: 100%;
-  width: 100%;
-}
-
-@property --gradient-rotation-angle {
-  inherits: false;
-  initial-value: 0deg;
-  syntax: '<angle>';
 }
 
 @property --gradient-blue {
@@ -47,12 +39,39 @@
   syntax: '<color>';
 }
 
+@property --gradient-mask-opacity-middle-val {
+  inherits: false;
+  initial-value: 0;
+  syntax: '<number>';
+}
+
 @keyframes rotateGradient {
   0% {
-    --gradient-rotation-angle: 0deg;
+    /* Preserve the scale applied to blur the gradient. */
+    transform: scale(1.4) rotate(0deg);
+  }
+
+  100% {
+    /* Preserve the scale applied to blur the gradient. */
+    transform: scale(1.4) rotate(360deg);
+  }
+}
+
+@keyframes fade-out {
+  0% {
+    opacity: 0.3;
   }
   100% {
-    --gradient-rotation-angle: 360deg;
+    opacity: 0;
+  }
+}
+
+@keyframes fade-in {
+  0% {
+    opacity: 0;
+  }
+  100% {
+    opacity: 0.3;
   }
 }
 
@@ -61,37 +80,68 @@
     opacity: 0;
   }
   100% {
-    opacity: 0.6;
+    opacity: 1;
   }
 }
 
 @keyframes shaderOpacityToResting {
   0% {
-    opacity: 0.5;
+    --gradient-mask-opacity-middle-val: 0;
+    opacity: 1;
   }
   100% {
+    --gradient-mask-opacity-middle-val: 0.4;
     opacity: 0.3;
   }
 }
 
 #borderGlowContainer {
-  animation: 117ms cubic-bezier(0, 0, 0, 1) 0s 1 normal forwards running shaderOpacityIn,
-      1016ms cubic-bezier(0.2, 0, 0, 1) 117ms 1 normal forwards running shaderOpacityToResting,
-      20s linear 0s infinite normal forwards running rotateGradient;
-  background: conic-gradient(from var(--gradient-rotation-angle, 0deg) at center,
-      var(--gradient-blue) 0deg, var(--gradient-blue) 10deg,
-      var(--gradient-red) 80deg, var(--gradient-yellow) 120deg,
-      var(--gradient-green) 160deg, var(--gradient-blue) 200deg,
-      var(--gradient-blue) 360deg);
-  content: '';
-  filter: blur(20px);
+  animation: 117ms cubic-bezier(0.2, 0, 0, 1) 0ms 1 normal forwards running shaderOpacityIn,
+                1016ms cubic-bezier(0.2, 0, 0, 1) 117ms 1 normal forwards running shaderOpacityToResting;
+  /* height and width must be larger than the diagonal of the viewport,
+  in order to prevent gaps at the corners while rotating. */
+  height: calc(max(100vw, 100vh) * 1.5);
+  left: 50%;
+  mask-image: radial-gradient(
+    rgba(0, 0, 0, 0) 0,
+    rgba(0, 0, 0, var(--gradient-mask-opacity-middle-val)) 40%,
+    rgba(0, 0, 0, 1) 100%
+  );
+  mask-mode: alpha;
+  opacity: 0;
+  position: fixed;
+  top: 50%;
+  transform: translate(-50%, -50%) rotate(0deg) scale(0.7);
+  transform-origin: center center;
+  width: calc(max(100vw, 100vh) * 1.5);
+}
+
+#gradientColorLayer {
+  animation: rotateGradient 20s linear 0s infinite normal forwards running;
+  background: conic-gradient(
+      from 0deg at center,
+      var(--gradient-blue) 0deg,
+      var(--gradient-blue) 10deg,
+      var(--gradient-red) 80deg,
+      var(--gradient-yellow) 120deg,
+      var(--gradient-green) 160deg,
+      var(--gradient-blue) 200deg,
+      var(--gradient-blue) 360deg
+  );
   height: 100%;
   left: 0;
-  -webkit-mask: url(#roundedFrameMask);
-  mask: url(#roundedFrameMask);
-  opacity: 0;
   position: absolute;
+  /* Scale effect as a replacement for a less performant filter: blur(20px). */
+  transform: scale(1.4);
+  transform-origin: center center;
   top: 0;
-  transform: scale(140%);
   width: 100%;
-}
\ No newline at end of file
+}
+
+:host([is-fading-in]) #borderGlowContainer {
+  animation: fade-in 166ms forwards cubic-bezier(0.3, 0, 1, 1);
+}
+
+:host([is-fading-out]) #borderGlowContainer {
+  animation: fade-out 166ms forwards cubic-bezier(0.3, 0, 1, 1);
+}
diff --git a/chrome/browser/resources/lens/overlay/overlay_border_glow.html.ts b/chrome/browser/resources/lens/overlay/overlay_border_glow.html.ts
index cd8667c3..db5a366 100644
--- a/chrome/browser/resources/lens/overlay/overlay_border_glow.html.ts
+++ b/chrome/browser/resources/lens/overlay/overlay_border_glow.html.ts
@@ -8,56 +8,6 @@
 
 export function getHtml(this: OverlayBorderGlowElement) {
   return html`<div id="borderGlowContainer">
-  <svg style="position: absolute; width: 0; height: 0; overflow: hidden">
-    <defs>
-      <filter id="blur-filter">
-        <feGaussianBlur id="blur" in="SourceGraphic" stdDeviation="0.02">
-          <animate
-            id="blur-animation"
-            attributeName="stdDeviation"
-            attributeType="XML"
-            from="0.02"
-            to="0.05"
-            dur="117ms"
-            begin="0s"
-            fill="freeze"
-            calcMode="spline"
-            keyTimes="0;1"
-            keySplines="0 0 0 0.1"
-          />
-
-          <animate
-            id="blur-animationToResting"
-            attributeName="stdDeviation"
-            attributeType="XML"
-            from="0.05"
-            to="0.1"
-            dur="1016ms"
-            begin="117ms"
-            fill="freeze"
-            calcMode="spline"
-            keyTimes="0;1"
-            keySplines="0.2 0 0 1"
-          />
-        </feGaussianBlur>
-      </filter>
-
-      <mask id="roundedFrameMask" maskContentUnits="objectBoundingBox">
-        <rect width="1" height="1" fill="white" />
-
-        <rect
-          id="mask-hole-rect"
-          x=".18"
-          y=".18"
-          width=".64"
-          height="0.64"
-          rx="0.25"
-          ry="0.25"
-          fill="black"
-          filter="url(#blur-filter)"
-        />
-      </mask>
-    </defs>
-  </svg>
-</div>`;
+    <div id="gradientColorLayer"></div>
+  </div>`;
 }
diff --git a/chrome/browser/resources/lens/overlay/overlay_border_glow.ts b/chrome/browser/resources/lens/overlay/overlay_border_glow.ts
index f2b15f9..e13599b 100644
--- a/chrome/browser/resources/lens/overlay/overlay_border_glow.ts
+++ b/chrome/browser/resources/lens/overlay/overlay_border_glow.ts
@@ -17,16 +17,39 @@
   }
 
   static override get properties() {
-    return {};
+    return {
+      isFadingOut: {
+        type: Boolean,
+        reflect: true,
+      },
+      isFadingIn: {
+        type: Boolean,
+        reflect: true,
+      },
+    };
   }
 
   static override get styles() {
     return getCss();
   }
 
+  handleGestureStart() {
+    this.isFadingOut = true;
+  }
+
+  /* TODO(crbug.com/419035304): Trigger this when the CSB thumbnail is removed.
+   */
+  handleRemoveSearchboxThumbnail() {
+    this.isFadingOut = false;
+    this.isFadingIn = true;
+  }
+
   override render() {
     return getHtml.bind(this)();
   }
+
+  protected accessor isFadingOut: boolean = false;
+  protected accessor isFadingIn: boolean = false;
 }
 
 declare global {
diff --git a/chrome/browser/resources/lens/overlay/region_selection.html b/chrome/browser/resources/lens/overlay/region_selection.html
index 6e2343e..5c0c7e9 100644
--- a/chrome/browser/resources/lens/overlay/region_selection.html
+++ b/chrome/browser/resources/lens/overlay/region_selection.html
@@ -8,11 +8,28 @@
     left: 0;
     position: absolute;
   }
+
+  :host([scrim-enabled]) #regionSelectionCanvas {
+    background: #000000;
+    opacity: 0;
+    transition: opacity 166ms cubic-bezier(0.3, 0, 1, 1);
+  }
+
+  :host([scrim-enabled][is-selecting]) #regionSelectionCanvas {
+    opacity: 0.3;
+  }
+
+  :host([scrim-enabled][has-selected]) #regionSelectionCanvas {
+    opacity: 0.2;
+  }
 </style>
-<canvas id="regionSelectionCanvas" height="[[canvasPhysicalHeight]]"
+<canvas
+  id="regionSelectionCanvas"
+  height="[[canvasPhysicalHeight]]"
   width="[[canvasPhysicalWidth]]"
-  style$="height: [[canvasHeight]]px; width: [[canvasWidth]]px;"></canvas>
+  style$="height: [[canvasHeight]]px; width: [[canvasWidth]]px;"
+></canvas>
 <!-- Provides image source for canvas; element is not displayed. -->
 <div id="highlightImgContainer">
-  <canvas id="highlightImgCanvas">
+  <canvas id="highlightImgCanvas"></canvas>
 </div>
diff --git a/chrome/browser/resources/lens/overlay/region_selection.ts b/chrome/browser/resources/lens/overlay/region_selection.ts
index 6af1cbd..42d73203 100644
--- a/chrome/browser/resources/lens/overlay/region_selection.ts
+++ b/chrome/browser/resources/lens/overlay/region_selection.ts
@@ -56,7 +56,22 @@
       canvasWidth: Number,
       canvasPhysicalHeight: Number,
       canvasPhysicalWidth: Number,
+      hasSelected: {
+        reflectToAttribute: true,
+        type: Boolean,
+        value: false,
+      },
+      isSelecting: {
+        reflectToAttribute: true,
+        type: Boolean,
+        value: false,
+      },
       screenshotDataUri: String,
+      scrimEnabled: {
+        reflectToAttribute: true,
+        type: Boolean,
+        value: false,
+      },
       shaderLayerColorHexes: {
         type: Array,
         computed: 'computeShaderLayerColorHexes_(theme)',
@@ -73,16 +88,23 @@
   declare private canvasWidth: number;
   declare private canvasPhysicalHeight: number;
   declare private canvasPhysicalWidth: number;
+  // Whether the user has selected a region.
+  declare private hasSelected: boolean;
+  // Whether the user is currently selecting a region.
+  declare private isSelecting: boolean;
   private context: CanvasRenderingContext2D;
   // The data URI of the current overlay screenshot.
   declare private screenshotDataUri: string;
+  // Whether the scrim is enabled.
+  declare private scrimEnabled: boolean;
+  // Shader hex colors.
+  declare private shaderLayerColorHexes: string[];
   // The overlay theme.
   declare private theme: OverlayTheme;
   // The bounds of the parent element. This is updated by the parent to avoid
   // this class needing to call getBoundingClientRect()
   declare private selectionOverlayRect: DOMRect;
-  // Shader hex colors.
-  declare private shaderLayerColorHexes: string[];
+
   private browserProxy: BrowserProxy = BrowserProxyImpl.getInstance();
 
   // The tap region dimensions are the height and width that the region should
@@ -114,10 +136,14 @@
   // Handles a drag gesture by drawing a bounded box on the canvas.
   handleGestureDrag(event: GestureEvent) {
     this.clearCanvas();
+    this.isSelecting = true;
     this.renderBoundingBox(event);
   }
 
   handleGestureEnd(event: GestureEvent): boolean {
+    this.isSelecting = false;
+    this.hasSelected = true;
+
     // Issue the Lens request.
     const isClick = event.state === GestureState.STARTING;
     this.browserProxy.handler.issueLensRegionRequest(
@@ -145,6 +171,7 @@
   }
 
   cancelGesture() {
+    this.isSelecting = false;
     this.clearCanvas();
   }
 
diff --git a/chrome/browser/resources/lens/overlay/selection_overlay.html b/chrome/browser/resources/lens/overlay/selection_overlay.html
index 9a229a59..e4af3f2 100644
--- a/chrome/browser/resources/lens/overlay/selection_overlay.html
+++ b/chrome/browser/resources/lens/overlay/selection_overlay.html
@@ -375,7 +375,7 @@
       id="overlayShimmerCanvas"
     ></overlay-shimmer-canvas>
     <template is="dom-if" if="[[enableBorderGlow]]">
-      <overlay-border-glow id="overlayBorderGlowCanvas"></overlay-border-glow>
+      <overlay-border-glow id="overlayBorderGlow"></overlay-border-glow>
     </template>
     <post-selection-renderer
       id="postSelectionRenderer"
@@ -393,6 +393,7 @@
       theme="[[theme]]"
       screenshot-data-uri="[[screenshotDataUri]]"
       selection-overlay-rect="[[selectionOverlayRect]]"
+      scrim-enabled="[[enableBorderGlow]]"
     ></region-selection>
     <template is="dom-if" if="[[!simplifiedSelectionEnabled]]">
       <lens-text-layer
diff --git a/chrome/browser/resources/lens/overlay/selection_overlay.ts b/chrome/browser/resources/lens/overlay/selection_overlay.ts
index c84c9eb..6cc0dbf6 100644
--- a/chrome/browser/resources/lens/overlay/selection_overlay.ts
+++ b/chrome/browser/resources/lens/overlay/selection_overlay.ts
@@ -29,6 +29,7 @@
 import {INVOCATION_SOURCE} from './lens_overlay_app.js';
 import {ContextMenuOption, recordContextMenuOptionShown, recordLensOverlayInteraction} from './metrics_utils.js';
 import type {ObjectLayerElement} from './object_layer.js';
+import type {OverlayBorderGlowElement} from './overlay_border_glow.js';
 import type {OverlayShimmerCanvasElement} from './overlay_shimmer_canvas.js';
 import type {PostSelectionRendererElement} from './post_selection_renderer.js';
 import type {RegionSelectionElement} from './region_selection.js';
@@ -301,6 +302,8 @@
   declare private simplifiedSelectionEnabled: boolean;
   // The text selection layer rendered on the selection overlay if it exists.
   private textSelectionLayer: TextLayerBase;
+  // The border glow layer rendered on the selection overlay if it exists.
+  private overlayBorderGlow: OverlayBorderGlowElement;
 
   private eventTracker_: EventTracker = new EventTracker();
   // Listener ids for events from the browser side.
@@ -791,6 +794,9 @@
     }
 
     this.getTextSelectionLayer().onSelectionStart();
+    if (this.enableBorderGlow) {
+      this.getOverlayBorderGlow().handleGestureStart();
+    }
 
     if (this.$.postSelectionRenderer.handleGestureStart(this.currentGesture)) {
       this.draggingRespondent = DragFeature.POST_SELECTION;
@@ -1289,6 +1295,15 @@
     return this.textSelectionLayer;
   }
 
+  private getOverlayBorderGlow(): OverlayBorderGlowElement {
+    if (this.overlayBorderGlow) {
+      return this.overlayBorderGlow;
+    }
+    this.overlayBorderGlow =
+        this.shadowRoot!.querySelector('overlay-border-glow')!;
+    return this.overlayBorderGlow;
+  }
+
   private onCopyCommand() {
     if (!this.simplifiedSelectionEnabled) {
       this.handleCopy();
diff --git a/chrome/browser/resources/pdf/elements/ink_text_box.html.ts b/chrome/browser/resources/pdf/elements/ink_text_box.html.ts
index dc9579c..23493ee 100644
--- a/chrome/browser/resources/pdf/elements/ink_text_box.html.ts
+++ b/chrome/browser/resources/pdf/elements/ink_text_box.html.ts
@@ -8,17 +8,18 @@
 
 export function getHtml(this: InkTextBoxElement) {
   return html`<!--_html_template_start_-->
-    <div class="handle top left"></div>
-    <div class="handle top center"></div>
-    <div class="handle top right"></div>
-    <div class="handle left center"></div>
-    <div class="handle right center"></div>
-    <div class="handle bottom left"></div>
-    <div class="handle bottom center"></div>
-    <div class="handle bottom right"></div>
+    <!-- TODO(crbug.com/414858397): Add labels for screenreaders -->
     <textarea id="textbox" .value="${this.textValue_}" rows="1"
         @input="${this.onTextValueInput_}"
         @focus="${this.onTextareaFocus_}">
     </textarea>
+    <div class="handle top left" tabindex="0"></div>
+    <div class="handle top center" tabindex="0"></div>
+    <div class="handle top right" tabindex="0"></div>
+    <div class="handle left center" tabindex="0"></div>
+    <div class="handle right center" tabindex="0"></div>
+    <div class="handle bottom left" tabindex="0"></div>
+    <div class="handle bottom center" tabindex="0"></div>
+    <div class="handle bottom right" tabindex="0"></div>
   <!--_html_template_end_-->`;
 }
diff --git a/chrome/browser/resources/pdf/elements/ink_text_box.ts b/chrome/browser/resources/pdf/elements/ink_text_box.ts
index a047f17..2d1d3da 100644
--- a/chrome/browser/resources/pdf/elements/ink_text_box.ts
+++ b/chrome/browser/resources/pdf/elements/ink_text_box.ts
@@ -96,6 +96,8 @@
   private accessor zoom_: number = 1.0;
 
   private attributes_?: TextAttributes;
+  private currentArrowKey_: string|null = null;
+  private dragTarget_: HTMLElement|null = null;
   private eventTracker_: EventTracker = new EventTracker();
   // Whether this is an existing textbox. Tracked so that the textbox can
   // correctly notify the backend about changes (e.g. deleting all text in an
@@ -103,12 +105,18 @@
   // this change where we wouldn't commit an empty new annotation).
   private existing_: boolean = false;
   private id_: number = -1;
+  private keyDownCount_: number = -1;
   private pageNumber_: number = -1;
   private pageX_: number = 0;
   private pageY_: number = 0;
   private pointerStart_: {x: number, y: number}|null = null;
   private startPosition_: TextBoxRect|null = null;
 
+  override firstUpdated(changedProperties: PropertyValues<this>) {
+    super.firstUpdated(changedProperties);
+    this.setAttribute('tabindex', '0');
+  }
+
   override connectedCallback() {
     super.connectedCallback();
     this.eventTracker_.add(
@@ -125,6 +133,8 @@
             this.onViewportChanged_((e as CustomEvent<ViewportParams>).detail));
     this.eventTracker_.add(
         this, 'pointerdown', (e: PointerEvent) => this.onPointerDown_(e));
+    this.eventTracker_.add(
+        this, 'keydown', (e: KeyboardEvent) => this.onKeyDown_(e));
   }
 
   override disconnectedCallback() {
@@ -230,7 +240,51 @@
     }
   }
 
+  private removePointerDragListeners_() {
+    assert(this.dragTarget_);
+    this.eventTracker_.remove(this.dragTarget_, 'pointercancel');
+    this.eventTracker_.remove(this.dragTarget_, 'pointerup');
+    this.eventTracker_.remove(this.dragTarget_, 'pointermove');
+    this.dragTarget_ = null;
+    this.pointerStart_ = null;
+  }
+
+  private removeKeyDragListeners_() {
+    assert(this.dragTarget_);
+    this.eventTracker_.remove(this.dragTarget_, 'keyup');
+    this.eventTracker_.remove(this.dragTarget_, 'focusout');
+    this.dragTarget_ = null;
+    this.currentArrowKey_ = null;
+    this.keyDownCount_ = -1;
+  }
+
+  // Removes any drag listeners and resets location to the start position.
+  private resetDrag_() {
+    if (this.dragTarget_ === null) {
+      return;
+    }
+
+    // Reset location to the start position.
+    assert(this.startPosition_);
+    this.locationX_ = this.startPosition_.locationX;
+    this.locationY_ = this.startPosition_.locationY;
+    this.width_ = this.startPosition_.width;
+    this.height_ = this.startPosition_.height;
+    this.startPosition_ = null;
+
+    if (this.pointerStart_ !== null) {
+      this.removePointerDragListeners_();
+    } else if (this.currentArrowKey_ !== null) {
+      this.removeKeyDragListeners_();
+    }
+  }
+
   commitTextAnnotation() {
+    // If the user is still dragging the box by holding down a key or pointer,
+    // reset location to the start of the drag and remove listeners before
+    // deactivating the box and committing the annotation.
+    this.resetDrag_();
+
     // If this is a new/inactive box or a new box edited to empty, nothing to do
     // unless it was initialized from an existing annotation. If this was
     // an existing annotation, we need to notify the backend to re-render it,
@@ -314,6 +368,81 @@
     this.pageY_ = update.pageDimensions.y;
   }
 
+  private onKeyDown_(e: KeyboardEvent) {
+    const target = e.composedPath()[0];
+    if (e.key === 'Escape') {
+      if (target === this.$.textbox) {
+        this.focus();
+      } else {
+        this.commitTextAnnotation();
+      }
+      return;
+    }
+
+    // Ignore all other keyboard events on the textbox itself.
+    if (!(target instanceof HTMLElement) || target === this.$.textbox) {
+      return;
+    }
+
+    // Delete key not in the textbox deletes the annotation.
+    if (e.key === 'Delete') {
+      this.textValue_ = '';
+      this.commitTextAnnotation();
+      return;
+    }
+
+    // Ignore all other keys except arrows. Also ignore if the user is already
+    // dragging with the pointer.
+    if (!['ArrowDown', 'ArrowUp', 'ArrowLeft', 'ArrowRight'].includes(e.key) ||
+        this.pointerStart_ !== null) {
+      return;
+    }
+
+    // Reset state if this is a new arrow key.
+    if (this.currentArrowKey_ !== null && this.currentArrowKey_ !== e.key) {
+      this.onHandleKeyUp_();
+    }
+    this.currentArrowKey_ = e.key;
+
+    if (this.keyDownCount_ === -1) {
+      this.dragTarget_ = target;
+      this.eventTracker_.add(target, 'keyup', () => this.onHandleKeyUp_());
+      this.eventTracker_.add(target, 'focusout', () => this.onHandleKeyUp_());
+      this.keyDownCount_ = 0;
+      this.startPosition_ = {
+        locationX: this.locationX_,
+        locationY: this.locationY_,
+        width: this.width_,
+        height: this.height_,
+      };
+    }
+    this.keyDownCount_++;
+
+    let moveX = 0;
+    let moveY = 0;
+    switch (e.key) {
+      case 'ArrowDown':
+        moveY = this.keyDownCount_;
+        break;
+      case 'ArrowUp':
+        moveY = -1 * this.keyDownCount_;
+        break;
+      case 'ArrowLeft':
+        moveX = -1 * this.keyDownCount_;
+        break;
+      case 'ArrowRight':
+        moveX = this.keyDownCount_;
+        break;
+    }
+    this.onMove_(target, moveX, moveY);
+  }
+
+  private onHandleKeyUp_() {
+    this.startPosition_ = null;
+    this.removeKeyDragListeners_();
+    this.textBoxEdited_();
+  }
+
   protected onPointerDown_(e: PointerEvent) {
     const target = e.composedPath()[0];
     // Ignore pointer events on the textbox itself.
@@ -322,6 +451,12 @@
       return;
     }
 
+    // Don't allow dragging with the keyboard and pointer at the same time.
+    if (this.dragTarget_ !== null) {
+      return;
+    }
+
+    this.dragTarget_ = target;
     this.pointerStart_ = {x: e.x, y: e.y};
     this.startPosition_ = {
       locationX: this.locationX_,
@@ -331,10 +466,9 @@
     };
 
     this.eventTracker_.add(
-        target, 'pointercancel',
-        (e: PointerEvent) => this.onHandlePointerUp_(e));
+        target, 'pointercancel', () => this.onHandlePointerUp_());
     this.eventTracker_.add(
-        target, 'pointerup', (e: PointerEvent) => this.onHandlePointerUp_(e));
+        target, 'pointerup', () => this.onHandlePointerUp_());
     this.eventTracker_.add(
         target, 'pointermove',
         (e: PointerEvent) => this.onHandlePointerMove_(e));
@@ -344,48 +478,43 @@
   private onHandlePointerMove_(e: PointerEvent) {
     const target = e.target as HTMLElement;
     assert(this.pointerStart_);
+    this.onMove_(
+        target, e.x - this.pointerStart_.x, e.y - this.pointerStart_.y);
+  }
+
+  private onMove_(target: HTMLElement, moveX: number, moveY: number) {
     assert(this.startPosition_);
     if (!target.classList.contains('handle')) {
       // User is dragging the box itself.
-      const deltaX = e.x - this.pointerStart_.x;
-      const deltaY = e.y - this.pointerStart_.y;
-      this.locationX_ = this.startPosition_.locationX + deltaX;
-      this.locationY_ = this.startPosition_.locationY + deltaY;
+      this.locationX_ = this.startPosition_.locationX + moveX;
+      this.locationY_ = this.startPosition_.locationY + moveY;
       return;
     }
 
     if (target.classList.contains('left')) {
-      const deltaX = Math.min(
-          e.x - this.pointerStart_.x, this.startPosition_.width - MIN_WIDTH_PX);
+      const deltaX = Math.min(moveX, this.startPosition_.width - MIN_WIDTH_PX);
       this.locationX_ = this.startPosition_.locationX + deltaX;
       this.width_ = this.startPosition_.width - deltaX;
     } else if (target.classList.contains('right')) {
-      const deltaX = Math.max(
-          e.x - this.pointerStart_.x,
-          -1 * this.startPosition_.width + MIN_WIDTH_PX);
+      const deltaX =
+          Math.max(moveX, -1 * this.startPosition_.width + MIN_WIDTH_PX);
       this.width_ = this.startPosition_.width + deltaX;
     }
     if (target.classList.contains('top')) {
-      const deltaY = Math.min(
-          e.y - this.pointerStart_.y,
-          this.startPosition_.height - this.minHeight_);
+      const deltaY =
+          Math.min(moveY, this.startPosition_.height - this.minHeight_);
       this.height_ = this.startPosition_.height - deltaY;
       this.locationY_ = this.startPosition_.locationY + deltaY;
     } else if (target.classList.contains('bottom')) {
-      const deltaY = Math.max(
-          e.y - this.pointerStart_.y,
-          -1 * this.startPosition_.height + this.minHeight_);
+      const deltaY =
+          Math.max(moveY, -1 * this.startPosition_.height + this.minHeight_);
       this.height_ = this.startPosition_.height + deltaY;
     }
   }
 
-  private onHandlePointerUp_(e: PointerEvent) {
-    const target = e.target as HTMLElement;
-    this.pointerStart_ = null;
+  private onHandlePointerUp_() {
     this.startPosition_ = null;
-    this.eventTracker_.remove(target, 'pointercancel');
-    this.eventTracker_.remove(target, 'pointerup');
-    this.eventTracker_.remove(target, 'pointermove');
+    this.removePointerDragListeners_();
     this.textBoxEdited_();
   }
 
diff --git a/chrome/browser/resources/print_preview/BUILD.gn b/chrome/browser/resources/print_preview/BUILD.gn
index 01dc004..cd74f21 100644
--- a/chrome/browser/resources/print_preview/BUILD.gn
+++ b/chrome/browser/resources/print_preview/BUILD.gn
@@ -114,7 +114,6 @@
     "ui/settings_select.css",
     "ui/sidebar.css",
     "ui/throbber.css",
-    "ui/throbber_lit.css",
   ]
 
   icons_html_files = [ "ui/icons.html" ]
diff --git a/chrome/browser/resources/print_preview/ui/color_settings.ts b/chrome/browser/resources/print_preview/ui/color_settings.ts
index add8de4072..3519b4e 100644
--- a/chrome/browser/resources/print_preview/ui/color_settings.ts
+++ b/chrome/browser/resources/print_preview/ui/color_settings.ts
@@ -10,7 +10,7 @@
 import {CrLitElement} from 'chrome://resources/lit/v3_0/lit.rollup.js';
 
 import {getHtml} from './color_settings.html.js';
-import {getCss as getPrintPreviewSharedLitCss} from './print_preview_shared.css.js';
+import {getCss as getPrintPreviewSharedCss} from './print_preview_shared.css.js';
 import {SelectMixin} from './select_mixin.js';
 import {SettingsMixin} from './settings_mixin.js';
 
@@ -25,7 +25,7 @@
 
   static override get styles() {
     return [
-      getPrintPreviewSharedLitCss(),
+      getPrintPreviewSharedCss(),
       getMdSelectLitCss(),
     ];
   }
diff --git a/chrome/browser/resources/print_preview/ui/destination_dialog.css b/chrome/browser/resources/print_preview/ui/destination_dialog.css
index 6452c0f..57962b37 100644
--- a/chrome/browser/resources/print_preview/ui/destination_dialog.css
+++ b/chrome/browser/resources/print_preview/ui/destination_dialog.css
@@ -9,8 +9,8 @@
  * #import=chrome://resources/cr_elements/cr_shared_vars.css.js
  * #import=./print_preview_shared.css.js
  * #import=./print_preview_vars.css.js
- * #import=./throbber_lit.css.js
- * #include=print-preview-shared cr-hidden-style-lit throbber-lit
+ * #import=./throbber.css.js
+ * #include=print-preview-shared cr-hidden-style-lit throbber
  * #css_wrapper_metadata_end */
 
 :host-context([dir=rtl]) #manageIcon {
diff --git a/chrome/browser/resources/print_preview/ui/destination_list.css b/chrome/browser/resources/print_preview/ui/destination_list.css
index f6e26614..9420d67a 100644
--- a/chrome/browser/resources/print_preview/ui/destination_list.css
+++ b/chrome/browser/resources/print_preview/ui/destination_list.css
@@ -7,8 +7,8 @@
  * #import=chrome://resources/cr_elements/cr_hidden_style_lit.css.js
  * #import=chrome://resources/cr_elements/cr_shared_vars.css.js
  * #import=./print_preview_vars.css.js
- * #import=./throbber_lit.css.js
- * #include=cr-hidden-style-lit throbber-lit
+ * #import=./throbber.css.js
+ * #include=cr-hidden-style-lit throbber
  * #css_wrapper_metadata_end */
 
 :host {
diff --git a/chrome/browser/resources/print_preview/ui/destination_select.css b/chrome/browser/resources/print_preview/ui/destination_select.css
index 74d34912..128059c 100644
--- a/chrome/browser/resources/print_preview/ui/destination_select.css
+++ b/chrome/browser/resources/print_preview/ui/destination_select.css
@@ -9,8 +9,8 @@
  * #import=chrome://resources/cr_elements/cr_shared_vars.css.js
  * #import=chrome://resources/cr_elements/md_select_lit.css.js
  * #import=./print_preview_shared.css.js
- * #import=./throbber_lit.css.js
- * #include=cr-shared-style-lit print-preview-shared throbber-lit md-select-lit cr-hidden-style-lit
+ * #import=./throbber.css.js
+ * #include=cr-shared-style-lit print-preview-shared throbber md-select-lit cr-hidden-style-lit
  * #css_wrapper_metadata_end */
 
 :host {
diff --git a/chrome/browser/resources/print_preview/ui/destination_settings.ts b/chrome/browser/resources/print_preview/ui/destination_settings.ts
index 9ee618c8..a7c7db5 100644
--- a/chrome/browser/resources/print_preview/ui/destination_settings.ts
+++ b/chrome/browser/resources/print_preview/ui/destination_settings.ts
@@ -23,7 +23,7 @@
 import type {PrintPreviewDestinationDialogElement} from './destination_dialog.js';
 import type {PrintPreviewDestinationSelectElement} from './destination_select.js';
 import {getHtml} from './destination_settings.html.js';
-import {getCss as getPrintPreviewSharedLitCss} from './print_preview_shared.css.js';
+import {getCss as getPrintPreviewSharedCss} from './print_preview_shared.css.js';
 import {SettingsMixin} from './settings_mixin.js';
 
 export enum DestinationState {
@@ -61,7 +61,7 @@
 
   static override get styles() {
     return [
-      getPrintPreviewSharedLitCss(),
+      getPrintPreviewSharedCss(),
     ];
   }
 
diff --git a/chrome/browser/resources/print_preview/ui/link_container.css b/chrome/browser/resources/print_preview/ui/link_container.css
index 2cf7d6f..53d67a8 100644
--- a/chrome/browser/resources/print_preview/ui/link_container.css
+++ b/chrome/browser/resources/print_preview/ui/link_container.css
@@ -6,9 +6,9 @@
  * #type=style-lit
  * #import=chrome://resources/cr_elements/cr_hidden_style_lit.css.js
  * #import=chrome://resources/cr_elements/cr_shared_style_lit.css.js
- * #import=./throbber_lit.css.js
+ * #import=./throbber.css.js
  * #scheme=relative
- * #include=cr-shared-style-lit throbber-lit cr-hidden-style-lit
+ * #include=cr-shared-style-lit throbber cr-hidden-style-lit
  * #css_wrapper_metadata_end */
 
 :host {
diff --git a/chrome/browser/resources/print_preview/ui/throbber.css b/chrome/browser/resources/print_preview/ui/throbber.css
index 8278229..c05b602c 100644
--- a/chrome/browser/resources/print_preview/ui/throbber.css
+++ b/chrome/browser/resources/print_preview/ui/throbber.css
@@ -1,11 +1,15 @@
-/* Copyright 2022 The Chromium Authors
+/* 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
+ * #type=style-lit
  * #import=./print_preview_vars.css.js
  * #css_wrapper_metadata_end */
 
-/* Intentionally left empty as it is generated at build time from the equivalent
- * Lit version. */
+.throbber {
+  background: url(chrome://resources/images/throbber_small.svg) no-repeat;
+  display: inline-block;
+  height: var(--throbber-size);
+  width: var(--throbber-size);
+}
diff --git a/chrome/browser/resources/print_preview/ui/throbber_lit.css b/chrome/browser/resources/print_preview/ui/throbber_lit.css
deleted file mode 100644
index c05b602c..0000000
--- a/chrome/browser/resources/print_preview/ui/throbber_lit.css
+++ /dev/null
@@ -1,15 +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. */
-
-/* #css_wrapper_metadata_start
- * #type=style-lit
- * #import=./print_preview_vars.css.js
- * #css_wrapper_metadata_end */
-
-.throbber {
-  background: url(chrome://resources/images/throbber_small.svg) no-repeat;
-  display: inline-block;
-  height: var(--throbber-size);
-  width: var(--throbber-size);
-}
diff --git a/chrome/browser/resources/privacy_sandbox/BUILD.gn b/chrome/browser/resources/privacy_sandbox/BUILD.gn
index fb1064f..e8f9831 100644
--- a/chrome/browser/resources/privacy_sandbox/BUILD.gn
+++ b/chrome/browser/resources/privacy_sandbox/BUILD.gn
@@ -41,13 +41,15 @@
     "base_dialog_app.html.ts",
     "base_dialog_app.ts",
     "base_dialog_browser_proxy.ts",
-    "topics_consent.html.ts",
-    "topics_consent.ts",
+    "topics_consent_notice.html.ts",
+    "topics_consent_notice.ts",
     "base_dialog_mixin.ts",
-    "protected_audience_measurement.html.ts",
-    "protected_audience_measurement.ts",
-    "three_ads_apis.html.ts",
-    "three_ads_apis.ts",
+    "protected_audience_measurement_notice.html.ts",
+    "protected_audience_measurement_notice.ts",
+    "three_ads_apis_notice.html.ts",
+    "three_ads_apis_notice.ts",
+    "measurement_notice.html.ts",
+    "measurement_notice.ts",
   ]
 
   css_files = [
diff --git a/chrome/browser/resources/privacy_sandbox/base_dialog_app.html.ts b/chrome/browser/resources/privacy_sandbox/base_dialog_app.html.ts
index f602c41..4f4a1fd 100644
--- a/chrome/browser/resources/privacy_sandbox/base_dialog_app.html.ts
+++ b/chrome/browser/resources/privacy_sandbox/base_dialog_app.html.ts
@@ -10,21 +10,25 @@
 export function getHtml(this: BaseDialogApp) {
   return html`
     <cr-view-manager id="viewManager">
-      <topics-consent id="${
+      <topics-consent-notice id="${
       this.getNoticeId(PrivacySandboxNotice.kTopicsConsentNotice)}"
           slot="view"
           fill-content>
-      </topics-consent>
-      <protected-audience-measurement id="${
+      </topics-consent-notice>
+      <protected-audience-measurement-notice id="${
       this.getNoticeId(
           PrivacySandboxNotice.kProtectedAudienceMeasurementNotice)}"
           slot="view"
           fill-content>
-      </protected-audience-measurement>
-      <three-ads-apis id="${
+      </protected-audience-measurement-notice>
+      <three-ads-apis-notice id="${
       this.getNoticeId(
           PrivacySandboxNotice.kThreeAdsApisNotice)}" slot="view" fill-content>
-      </three-ads-apis
+      </three-ads-apis-notice>
+      <measurement-notice id="${
+      this.getNoticeId(
+          PrivacySandboxNotice.kMeasurementNotice)}" slot="view" fill-content>
+      </measurement-notice>
     </cr-view-manager>
   `;
 }
diff --git a/chrome/browser/resources/privacy_sandbox/base_dialog_app.ts b/chrome/browser/resources/privacy_sandbox/base_dialog_app.ts
index 877a2184..e84b175d 100644
--- a/chrome/browser/resources/privacy_sandbox/base_dialog_app.ts
+++ b/chrome/browser/resources/privacy_sandbox/base_dialog_app.ts
@@ -5,9 +5,10 @@
 import 'chrome://resources/cr_elements/cr_shared_style.css.js';
 import 'chrome://resources/cr_elements/cr_button/cr_button.js';
 import 'chrome://resources/cr_elements/cr_view_manager/cr_view_manager.js';
-import './topics_consent.js';
-import './protected_audience_measurement.js';
-import './three_ads_apis.js';
+import './topics_consent_notice.js';
+import './protected_audience_measurement_notice.js';
+import './three_ads_apis_notice.js';
+import './measurement_notice.js';
 import '/strings.m.js';
 
 import {CrLitElement} from '//resources/lit/v3_0/lit.rollup.js';
diff --git a/chrome/browser/resources/privacy_sandbox/three_ads_apis.html.ts b/chrome/browser/resources/privacy_sandbox/measurement_notice.html.ts
similarity index 66%
copy from chrome/browser/resources/privacy_sandbox/three_ads_apis.html.ts
copy to chrome/browser/resources/privacy_sandbox/measurement_notice.html.ts
index 9a5d4048..24a47f1d 100644
--- a/chrome/browser/resources/privacy_sandbox/three_ads_apis.html.ts
+++ b/chrome/browser/resources/privacy_sandbox/measurement_notice.html.ts
@@ -4,11 +4,11 @@
 
 import {html} from '//resources/lit/v3_0/lit.rollup.js';
 
-import type {ThreeAdsApis} from './three_ads_apis.js';
+import type {MeasurementNotice} from './measurement_notice.js';
 
-export function getHtml(this: ThreeAdsApis) {
+export function getHtml(this: MeasurementNotice) {
   return html`
-    <div>Three Ads Apis Notice Placeholder</div>
+    <div>Measurement Notice Placeholder</div>
     <cr-button id="ackButton" @click="${this.onAck}">
       Ack Placeholder
     </cr-button>
diff --git a/chrome/browser/resources/privacy_sandbox/measurement_notice.ts b/chrome/browser/resources/privacy_sandbox/measurement_notice.ts
new file mode 100644
index 0000000..aaa963d5
--- /dev/null
+++ b/chrome/browser/resources/privacy_sandbox/measurement_notice.ts
@@ -0,0 +1,32 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'chrome://resources/cr_elements/cr_button/cr_button.js';
+
+import {CrLitElement} from '//resources/lit/v3_0/lit.rollup.js';
+
+import {BaseDialogMixin} from './base_dialog_mixin.js';
+import {getHtml} from './measurement_notice.html.js';
+import {PrivacySandboxNotice} from './notice.mojom-webui.js';
+
+const MeasurementNoticeBase =
+    BaseDialogMixin(CrLitElement, PrivacySandboxNotice.kMeasurementNotice);
+
+export class MeasurementNotice extends MeasurementNoticeBase {
+  static get is() {
+    return 'measurement-notice';
+  }
+
+  override render() {
+    return getHtml.bind(this)();
+  }
+}
+
+declare global {
+  interface HTMLElementTagNameMap {
+    'measurement-notice': MeasurementNotice;
+  }
+}
+
+customElements.define(MeasurementNotice.is, MeasurementNotice);
diff --git a/chrome/browser/resources/privacy_sandbox/protected_audience_measurement.ts b/chrome/browser/resources/privacy_sandbox/protected_audience_measurement.ts
deleted file mode 100644
index c26684e..0000000
--- a/chrome/browser/resources/privacy_sandbox/protected_audience_measurement.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2025 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'chrome://resources/cr_elements/cr_button/cr_button.js';
-
-import {CrLitElement} from '//resources/lit/v3_0/lit.rollup.js';
-
-import {BaseDialogMixin} from './base_dialog_mixin.js';
-import {PrivacySandboxNotice} from './notice.mojom-webui.js';
-import {getHtml} from './protected_audience_measurement.html.js';
-
-const ProtectedAudienceMeasurementBase = BaseDialogMixin(
-    CrLitElement, PrivacySandboxNotice.kProtectedAudienceMeasurementNotice);
-
-export class ProtectedAudienceMeasurement extends
-    ProtectedAudienceMeasurementBase {
-  static get is() {
-    return 'protected-audience-measurement';
-  }
-
-  override render() {
-    return getHtml.bind(this)();
-  }
-}
-
-declare global {
-  interface HTMLElementTagNameMap {
-    'protected-audience-measurement': ProtectedAudienceMeasurement;
-  }
-}
-
-customElements.define(
-    ProtectedAudienceMeasurement.is, ProtectedAudienceMeasurement);
diff --git a/chrome/browser/resources/privacy_sandbox/protected_audience_measurement.html.ts b/chrome/browser/resources/privacy_sandbox/protected_audience_measurement_notice.html.ts
similarity index 69%
rename from chrome/browser/resources/privacy_sandbox/protected_audience_measurement.html.ts
rename to chrome/browser/resources/privacy_sandbox/protected_audience_measurement_notice.html.ts
index 41b04fe34d..830756b8 100644
--- a/chrome/browser/resources/privacy_sandbox/protected_audience_measurement.html.ts
+++ b/chrome/browser/resources/privacy_sandbox/protected_audience_measurement_notice.html.ts
@@ -4,9 +4,9 @@
 
 import {html} from '//resources/lit/v3_0/lit.rollup.js';
 
-import type {ProtectedAudienceMeasurement} from './protected_audience_measurement.js';
+import type {ProtectedAudienceMeasurementNotice} from './protected_audience_measurement_notice.js';
 
-export function getHtml(this: ProtectedAudienceMeasurement) {
+export function getHtml(this: ProtectedAudienceMeasurementNotice) {
   return html`
     <div>Protected Audience Measurement Notice Placeholder</div>
     <cr-button id="ackButton" @click="${this.onAck}">
diff --git a/chrome/browser/resources/privacy_sandbox/protected_audience_measurement_notice.ts b/chrome/browser/resources/privacy_sandbox/protected_audience_measurement_notice.ts
new file mode 100644
index 0000000..a48d586
--- /dev/null
+++ b/chrome/browser/resources/privacy_sandbox/protected_audience_measurement_notice.ts
@@ -0,0 +1,34 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'chrome://resources/cr_elements/cr_button/cr_button.js';
+
+import {CrLitElement} from '//resources/lit/v3_0/lit.rollup.js';
+
+import {BaseDialogMixin} from './base_dialog_mixin.js';
+import {PrivacySandboxNotice} from './notice.mojom-webui.js';
+import {getHtml} from './protected_audience_measurement_notice.html.js';
+
+const ProtectedAudienceMeasurementNoticeBase = BaseDialogMixin(
+    CrLitElement, PrivacySandboxNotice.kProtectedAudienceMeasurementNotice);
+
+export class ProtectedAudienceMeasurementNotice extends
+    ProtectedAudienceMeasurementNoticeBase {
+  static get is() {
+    return 'protected-audience-measurement-notice';
+  }
+
+  override render() {
+    return getHtml.bind(this)();
+  }
+}
+
+declare global {
+  interface HTMLElementTagNameMap {
+    'protected-audience-measurement-notice': ProtectedAudienceMeasurementNotice;
+  }
+}
+
+customElements.define(
+    ProtectedAudienceMeasurementNotice.is, ProtectedAudienceMeasurementNotice);
diff --git a/chrome/browser/resources/privacy_sandbox/three_ads_apis.html.ts b/chrome/browser/resources/privacy_sandbox/three_ads_apis_notice.html.ts
similarity index 75%
rename from chrome/browser/resources/privacy_sandbox/three_ads_apis.html.ts
rename to chrome/browser/resources/privacy_sandbox/three_ads_apis_notice.html.ts
index 9a5d4048..c9cf3ae 100644
--- a/chrome/browser/resources/privacy_sandbox/three_ads_apis.html.ts
+++ b/chrome/browser/resources/privacy_sandbox/three_ads_apis_notice.html.ts
@@ -4,9 +4,9 @@
 
 import {html} from '//resources/lit/v3_0/lit.rollup.js';
 
-import type {ThreeAdsApis} from './three_ads_apis.js';
+import type {ThreeAdsApisNotice} from './three_ads_apis_notice.js';
 
-export function getHtml(this: ThreeAdsApis) {
+export function getHtml(this: ThreeAdsApisNotice) {
   return html`
     <div>Three Ads Apis Notice Placeholder</div>
     <cr-button id="ackButton" @click="${this.onAck}">
diff --git a/chrome/browser/resources/privacy_sandbox/three_ads_apis.ts b/chrome/browser/resources/privacy_sandbox/three_ads_apis_notice.ts
similarity index 66%
rename from chrome/browser/resources/privacy_sandbox/three_ads_apis.ts
rename to chrome/browser/resources/privacy_sandbox/three_ads_apis_notice.ts
index 368882f7..68585622 100644
--- a/chrome/browser/resources/privacy_sandbox/three_ads_apis.ts
+++ b/chrome/browser/resources/privacy_sandbox/three_ads_apis_notice.ts
@@ -8,14 +8,14 @@
 
 import {BaseDialogMixin} from './base_dialog_mixin.js';
 import {PrivacySandboxNotice} from './notice.mojom-webui.js';
-import {getHtml} from './three_ads_apis.html.js';
+import {getHtml} from './three_ads_apis_notice.html.js';
 
-const ThreeAdsApisBase =
+const ThreeAdsApisNoticeBase =
     BaseDialogMixin(CrLitElement, PrivacySandboxNotice.kThreeAdsApisNotice);
 
-export class ThreeAdsApis extends ThreeAdsApisBase {
+export class ThreeAdsApisNotice extends ThreeAdsApisNoticeBase {
   static get is() {
-    return 'three-ads-apis';
+    return 'three-ads-apis-notice';
   }
 
   override render() {
@@ -25,8 +25,8 @@
 
 declare global {
   interface HTMLElementTagNameMap {
-    'three-ads-apis': ThreeAdsApis;
+    'three-ads-apis-notice': ThreeAdsApisNotice;
   }
 }
 
-customElements.define(ThreeAdsApis.is, ThreeAdsApis);
+customElements.define(ThreeAdsApisNotice.is, ThreeAdsApisNotice);
diff --git a/chrome/browser/resources/privacy_sandbox/topics_consent.html.ts b/chrome/browser/resources/privacy_sandbox/topics_consent_notice.html.ts
similarity index 65%
rename from chrome/browser/resources/privacy_sandbox/topics_consent.html.ts
rename to chrome/browser/resources/privacy_sandbox/topics_consent_notice.html.ts
index db488ab..44a49e2 100644
--- a/chrome/browser/resources/privacy_sandbox/topics_consent.html.ts
+++ b/chrome/browser/resources/privacy_sandbox/topics_consent_notice.html.ts
@@ -4,11 +4,11 @@
 
 import {html} from '//resources/lit/v3_0/lit.rollup.js';
 
-import type {TopicsConsent} from './topics_consent.js';
+import type {TopicsConsentNotice} from './topics_consent_notice.js';
 
-export function getHtml(this: TopicsConsent) {
+export function getHtml(this: TopicsConsentNotice) {
   return html`
-    <div>Topics Consent Placeholder</div>
+    <div>Topics Consent Notice Placeholder</div>
     <cr-button id="acceptButton" @click="${this.onOptIn}">
       Accept Placeholder
     </cr-button>
diff --git a/chrome/browser/resources/privacy_sandbox/topics_consent.ts b/chrome/browser/resources/privacy_sandbox/topics_consent_notice.ts
similarity index 66%
rename from chrome/browser/resources/privacy_sandbox/topics_consent.ts
rename to chrome/browser/resources/privacy_sandbox/topics_consent_notice.ts
index 8e3ee8d..6c7f26f8 100644
--- a/chrome/browser/resources/privacy_sandbox/topics_consent.ts
+++ b/chrome/browser/resources/privacy_sandbox/topics_consent_notice.ts
@@ -8,14 +8,14 @@
 
 import {BaseDialogMixin} from './base_dialog_mixin.js';
 import {PrivacySandboxNotice} from './notice.mojom-webui.js';
-import {getHtml} from './topics_consent.html.js';
+import {getHtml} from './topics_consent_notice.html.js';
 
-const TopicsConsentBase =
+const TopicsConsentNoticeBase =
     BaseDialogMixin(CrLitElement, PrivacySandboxNotice.kTopicsConsentNotice);
 
-export class TopicsConsent extends TopicsConsentBase {
+export class TopicsConsentNotice extends TopicsConsentNoticeBase {
   static get is() {
-    return 'topics-consent';
+    return 'topics-consent-notice';
   }
 
   override render() {
@@ -25,8 +25,8 @@
 
 declare global {
   interface HTMLElementTagNameMap {
-    'topics-consent': TopicsConsent;
+    'topics-consent-notice': TopicsConsentNotice;
   }
 }
 
-customElements.define(TopicsConsent.is, TopicsConsent);
+customElements.define(TopicsConsentNotice.is, TopicsConsentNotice);
diff --git a/chrome/browser/resources/side_panel/read_anything/read_aloud/speech_controller.ts b/chrome/browser/resources/side_panel/read_anything/read_aloud/speech_controller.ts
index 11b4f95b..9bf5e82 100644
--- a/chrome/browser/resources/side_panel/read_anything/read_aloud/speech_controller.ts
+++ b/chrome/browser/resources/side_panel/read_anything/read_aloud/speech_controller.ts
@@ -173,7 +173,6 @@
     // that this step can be skipped.
     chrome.readingMode.initAxPositionWithNode(firstTextNode);
     this.model_.setIsSpeechTreeInitialized(true);
-    chrome.readingMode.preprocessTextForSpeech();
   }
 
   onSelectionChange() {
diff --git a/chrome/browser/resources/side_panel/read_anything/read_anything.d.ts b/chrome/browser/resources/side_panel/read_anything/read_anything.d.ts
index e1649f0..9af343d 100644
--- a/chrome/browser/resources/side_panel/read_anything/read_anything.d.ts
+++ b/chrome/browser/resources/side_panel/read_anything/read_anything.d.ts
@@ -324,12 +324,6 @@
     // for text associated with these nodes.
     function getCurrentText(): number[];
 
-    // Begins processing the speech segments on the current page to be used by
-    // Read Aloud. This will split the speech into segments and process
-    // words to be used by word highlighting. This allows text to be traversed
-    // more quickly after speech begins.
-    function preprocessTextForSpeech(): void;
-
     // Resets the granularity index.
     function resetGranularityIndex(): void;
 
diff --git a/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc b/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc
index a6e4568..7fc45cf 100644
--- a/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc
+++ b/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc
@@ -2233,7 +2233,7 @@
 }
 
 TEST_F(ClientSideDetectionRTLookupResponseForceRequestTest,
-       AsyncCheckTrackerNotTriggerClassificationRequestAlreadyPhishing) {
+       AsyncCheckTrackerTriggersClassificationRequestOnLocalModelPhishing) {
   if (base::FeatureList::IsEnabled(kClientSideDetectionKillswitch)) {
     GTEST_SKIP();
   }
@@ -2269,19 +2269,120 @@
 
   SetRTResponseInCacheManager(/*is_enforced=*/true);
 
+  // get_valid_cached_result is set to nullptr, because the request type is not
+  // TRIGGER_MODELS. Force request triggers also bypass the CSD allowlist.
+  ExpectPreClassificationChecks(
+      example_url, &kFalse, /*match_csd_allowlist=*/nullptr,
+      /*get_valid_cached_result=*/nullptr, &kFalse, &kFalse);
+  // Calling this should trigger preclassification again, because local model
+  // phishy verdict is not a condition to skip the async check force request
+  // because the force request ping will contain information that the page load
+  // request may have missed.
+  CompleteAsyncCheck();
+  // Set up mock call to token fetcher.
+  SafeBrowsingTokenFetcher::Callback token_cb2;
+  EXPECT_CALL(*raw_token_fetcher_, Start(_))
+      .Times(1)
+      .WillRepeatedly(MoveArg<0>(&token_cb2));
+  task_environment()->RunUntilIdle();
+
+  ClientSideDetectionService::ClientReportPhishingRequestCallback cb2;
+  // The verdict's is_phishing is false, but we will still send a ping!
+  ClientPhishingRequest verdict2;
+  verdict2.set_url(example_url.spec());
+  verdict2.set_client_score(0.8f);
+  verdict2.set_is_phishing(false);
+  EXPECT_CALL(*csd_service_, SendClientReportPhishingRequest(
+                                 _, _, "fake_access_token_for_force_request"))
+      .WillOnce(MoveArg<1>(&cb2));
+
+  ASSERT_FALSE(token_cb2.is_null());
+  std::move(token_cb2).Run("fake_access_token_for_force_request");
+
+  // Token is now fetched, so we will now callback on
+  // ClientReportPhishingRequest.
+  EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get()));
+
+  task_environment()->RunUntilIdle();
+
+  // Enforce request should be triggered again, because the local model will
+  // think it's phishy, but force request pings can contain other information,
+  // such as the LLM information.
+  histogram_tester.ExpectBucketCount(
+      "SBClientPhishing.ClientSideDetectionTypeRequest",
+      ClientSideDetectionType::FORCE_REQUEST, 1);
+  histogram_tester.ExpectUniqueSample(
+      "SBClientPhishing.ClientSideDetection."
+      "AsyncCheckTriggerForceRequestResult",
+      ClientSideDetectionHost::AsyncCheckTriggerForceRequestResult::kTriggered,
+      1);
+}
+
+TEST_F(
+    ClientSideDetectionRTLookupResponseForceRequestTest,
+    AsyncCheckTrackerNotTriggerClassificationRequestOnTriggerModelPingConvertedToForceRequest) {
+  if (base::FeatureList::IsEnabled(kClientSideDetectionKillswitch)) {
+    GTEST_SKIP();
+  }
+
+  base::HistogramTester histogram_tester;
+
+  GURL example_url("http://suspiciousurl.com/");
+  database_manager_->SetAllowlistLookupDetailsForUrl(example_url, false);
+  ExpectPreClassificationChecks(
+      /*url=*/example_url, /*is_private=*/&kFalse,
+      /*match_csd_allowlist=*/&kFalse, /*get_valid_cached_result=*/&kFalse,
+      /*over_phishing_report_limit=*/&kFalse, /*is_local=*/&kFalse);
+  NavigateAndCommit(example_url);
+  WaitAndCheckPreClassificationChecks();
+
+  // Set up mock call to token fetcher.
+  SafeBrowsingTokenFetcher::Callback token_cb;
+  EXPECT_CALL(*raw_token_fetcher_, Start(_))
+      .Times(1)
+      .WillRepeatedly(MoveArg<0>(&token_cb));
+  ClientSideDetectionService::ClientReportPhishingRequestCallback cb;
+  ClientPhishingRequest verdict;
+  verdict.set_url(example_url.spec());
+  verdict.set_client_score(0.5f);
+  verdict.set_is_phishing(false);
+
+  EXPECT_CALL(*csd_service_, SendClientReportPhishingRequest(
+                                 PartiallyEqualVerdict(verdict), _,
+                                 "fake_access_token_for_force_request"))
+      .WillOnce(MoveArg<1>(&cb));
+  // Setup RTResponse in cache prior to calling PhishingDetectionDone.
+  SetRTResponseInCacheManager(/*is_enforced=*/true);
+
+  PhishingDetectionDone(mojo_base::ProtoWrapper(verdict));
+  task_environment()->RunUntilIdle();
+
+  // Wait for token fetcher to be called.
+  EXPECT_TRUE(Mock::VerifyAndClear(raw_token_fetcher_));
+
+  ASSERT_FALSE(token_cb.is_null());
+  std::move(token_cb).Run("fake_access_token_for_force_request");
+
+  // Check that the page is phishing and triggers a ping.
+  histogram_tester.ExpectBucketCount(
+      "SBClientPhishing.ClientSideDetectionTypeRequest",
+      ClientSideDetectionType::FORCE_REQUEST, 1);
+  histogram_tester.ExpectBucketCount("SBClientPhishing.RTLookupForceRequest",
+                                     true, 1);
+
   CompleteAsyncCheck();
   task_environment()->RunUntilIdle();
 
-  // Enforce request should not be triggered again, because the page is already
-  // phishing and the ping is already sent.
+  // Enforce request should NOT be triggered again, because the page load ping
+  // has been converted to a force request ping.
   histogram_tester.ExpectBucketCount(
       "SBClientPhishing.ClientSideDetectionTypeRequest",
-      ClientSideDetectionType::FORCE_REQUEST, 0);
+      ClientSideDetectionType::FORCE_REQUEST, 1);
   histogram_tester.ExpectUniqueSample(
       "SBClientPhishing.ClientSideDetection."
       "AsyncCheckTriggerForceRequestResult",
       ClientSideDetectionHost::AsyncCheckTriggerForceRequestResult::
-          kSkippedTriggerModelsPingNotSkipped,
+          kSkippedTriggerModelsPingSentAsForceRequest,
       1);
 }
 
diff --git a/chrome/browser/sessions/session_tab_helper_factory.cc b/chrome/browser/sessions/session_tab_helper_factory.cc
index 6ca3ae1..e634ae6 100644
--- a/chrome/browser/sessions/session_tab_helper_factory.cc
+++ b/chrome/browser/sessions/session_tab_helper_factory.cc
@@ -17,7 +17,7 @@
 #include "content/public/browser/web_contents.h"
 #endif
 
-#if BUILDFLAG(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS_CORE)
 #include "extensions/browser/extension_web_contents_observer.h"
 #endif
 
@@ -51,7 +51,7 @@
 #endif
   sessions::SessionTabHelper::CreateForWebContents(contents, std::move(lookup));
 
-#if BUILDFLAG(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS_CORE)
   auto* observer =
       extensions::ExtensionWebContentsObserver::GetForWebContents(contents);
 
diff --git a/chrome/browser/subresource_filter/safe_browsing_child_navigation_throttle_unittest.cc b/chrome/browser/subresource_filter/safe_browsing_child_navigation_throttle_unittest.cc
index c409cdd..34769be 100644
--- a/chrome/browser/subresource_filter/safe_browsing_child_navigation_throttle_unittest.cc
+++ b/chrome/browser/subresource_filter/safe_browsing_child_navigation_throttle_unittest.cc
@@ -8,6 +8,7 @@
 
 #include "base/functional/bind.h"
 #include "base/strings/stringprintf.h"
+#include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/content_settings/cookie_settings_factory.h"
@@ -25,6 +26,7 @@
 #include "components/subresource_filter/core/mojom/subresource_filter.mojom.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/test/navigation_simulator.h"
+#include "content/public/test/test_navigation_throttle_inserter.h"
 #include "net/base/features.h"
 #include "services/network/public/cpp/features.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -62,6 +64,8 @@
         HostContentSettingsMapFactory::GetForProfile(browser_context());
     profile_interaction_manager_ = std::make_unique<ProfileInteractionManager>(
         SubresourceFilterProfileContextFactory::GetForProfile(profile()));
+
+    SetUpThrottleInserter();
   }
 
   // content::RenderViewHostTestHarness:
@@ -75,21 +79,32 @@
   void DidStartNavigation(
       content::NavigationHandle* navigation_handle) override {
     ASSERT_FALSE(navigation_handle->IsInMainFrame());
-    // The |parent_filter_| is the parent frame's filter. Do not register a
-    // throttle if the parent is not activated with a valid filter.
-    if (parent_filter_) {
-      auto throttle = std::make_unique<SafeBrowsingChildNavigationThrottle>(
-          navigation_handle, parent_filter_.get(),
-          profile_interaction_manager_->AsWeakPtr(),
-          base::BindRepeating([](const GURL& filtered_url) {
-            return base::StringPrintf(
-                kDisallowChildFrameConsoleMessageFormat,
-                filtered_url.possibly_invalid_spec().c_str());
-          }),
-          /*ad_evidence=*/std::nullopt);
-      ASSERT_NE(nullptr, throttle->GetNameForLogging());
-      navigation_handle->RegisterThrottleForTesting(std::move(throttle));
-    }
+  }
+
+  void SetUpThrottleInserter() {
+    throttle_inserter_ =
+        std::make_unique<content::TestNavigationThrottleInserter>(
+            content::RenderViewHostTestHarness::web_contents(),
+            base::BindLambdaForTesting(
+                [&](content::NavigationThrottleRegistry& registry) -> void {
+                  // The |parent_filter_| is the parent frame's filter. Do not
+                  // register a throttle if the parent is not activated with a
+                  // valid filter.
+                  if (parent_filter_) {
+                    auto throttle =
+                        std::make_unique<SafeBrowsingChildNavigationThrottle>(
+                            registry, parent_filter_.get(),
+                            profile_interaction_manager_->AsWeakPtr(),
+                            base::BindRepeating([](const GURL& filtered_url) {
+                              return base::StringPrintf(
+                                  kDisallowChildFrameConsoleMessageFormat,
+                                  filtered_url.possibly_invalid_spec().c_str());
+                            }),
+                            /*ad_evidence=*/std::nullopt);
+                    EXPECT_NE(nullptr, throttle->GetNameForLogging());
+                    registry.AddThrottle(std::move(throttle));
+                  }
+                }));
   }
 
   void CreateSubframeAndInitNavigation(const GURL& first_url,
@@ -115,6 +130,7 @@
 
   scoped_refptr<HostContentSettingsMap> settings_map_;
   std::unique_ptr<ProfileInteractionManager> profile_interaction_manager_;
+  std::unique_ptr<content::TestNavigationThrottleInserter> throttle_inserter_;
 };
 
 TEST_F(SafeBrowsingChildNavigationThrottleAdTaggingTest,
@@ -203,6 +219,8 @@
         HostContentSettingsMapFactory::GetForProfile(browser_context());
     profile_interaction_manager_ = std::make_unique<ProfileInteractionManager>(
         SubresourceFilterProfileContextFactory::GetForProfile(profile()));
+
+    SetUpThrottleInserter();
   }
 };
 
diff --git a/chrome/browser/sync/test/integration/BUILD.gn b/chrome/browser/sync/test/integration/BUILD.gn
new file mode 100644
index 0000000..eb7c1089
--- /dev/null
+++ b/chrome/browser/sync/test/integration/BUILD.gn
@@ -0,0 +1,344 @@
+# 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.
+
+if (is_android) {
+  import("//build/config/android/rules.gni")
+} else {
+  import("//build/config/ui.gni")
+  import("//chrome/common/features.gni")
+  import("//printing/buildflags/buildflags.gni")
+}
+
+testonly = true
+
+source_set("sync_integration_tests_impl") {
+  configs += [ "//build/config:precompiled_headers" ]
+  defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
+  deps = [
+    "//chrome/browser/plus_addresses",
+    "//chrome/browser/prefs",
+    "//chrome/browser/reading_list",
+    "//chrome/browser/sync",
+    "//chrome/browser/sync",
+    "//chrome/browser/ui:browser_navigator_params_headers",
+    "//chrome/browser/ui:ui_features",
+    "//chrome/test:browser_tests_runner",
+    "//chrome/test:sync_integration_test_support",
+    "//chrome/test:test_support",
+    "//components/bookmarks/browser",
+    "//components/data_sharing/public",
+    "//components/password_manager/core/browser/sharing",
+    "//components/plus_addresses:test_support",
+    "//components/plus_addresses/resources/strings",
+    "//components/plus_addresses/webdata",
+    "//components/reading_list/core:test_support",
+    "//components/saved_tab_groups/test_support",
+    "//components/sync:test_support",
+    "//components/sync_bookmarks",
+    "//components/sync_device_info:test_support",
+    "//components/sync_preferences:common_syncable_prefs_database",
+    "//components/undo",
+    "//components/version_info",
+  ]
+
+  sources = [
+    "single_client_autofill_profile_sync_test.cc",
+    "single_client_bookmarks_sync_test.cc",
+    "single_client_collaboration_group_sync_test.cc",
+    "single_client_contact_info_sync_test.cc",
+    "single_client_custom_passphrase_sync_test.cc",
+    "single_client_device_info_sync_test.cc",
+    "single_client_history_delete_directives_sync_test.cc",
+    "single_client_history_sync_test.cc",
+    "single_client_offer_sync_test.cc",
+    "single_client_plus_address_setting_sync_test.cc",
+    "single_client_plus_address_sync_test.cc",
+    "single_client_preferences_sync_test.cc",
+    "single_client_reading_list_sync_test.cc",
+    "single_client_shared_tab_group_data_sync_test.cc",
+    "single_client_standalone_transport_sync_test.cc",
+    "single_client_sync_invalidations_test.cc",
+    "sync_exponential_backoff_test.cc",
+
+    # TODO(crbug.com/393119606): After enough of the implementation is ready
+    # (CL #N), add integration tests. Careful, sync_integration_tests runs in CQ
+    # but android_sync_integration_tests do not.
+
+    # TODO(crbug.com/397767033): After enough of the implementation is ready
+    # (CL #N), add integration tests. Careful, sync_integration_tests runs in CQ
+    # but android_sync_integration_tests do not.
+  ]
+
+  data = [ "//chrome/test/data/sync/" ]
+
+  if (is_android) {
+    sources += [
+      "//chrome/test/android/browsertests_apk/android_browsertests_jni_onload.cc",
+      "single_client_web_apks_sync_test.cc",
+    ]
+
+    # Defined here so it's shared between android_sync_integration_tests (upstream)
+    # and android_live_sync_integration_tests (downstream).
+    deps += [
+      "//chrome:chrome_android_core",
+      "//chrome/browser/android/webapk:webapk_sources",
+      "//chrome/browser/ui:ui_features",
+      "//chrome/test:chrome_test_launcher",
+      "//chrome/test:test_support_ui_android",
+      "//components/browser_sync:switches",
+      "//components/saved_tab_groups/public",
+      "//content/test:android_test_message_pump_support",
+      "//testing/android/native_test:native_test_support",
+    ]
+
+    data += [
+      "$root_gen_dir/chrome/android/chrome_apk_paks/chrome_100_percent.pak",
+      "$root_gen_dir/chrome/android/chrome_apk_paks/locales/en-US.pak",
+      "$root_gen_dir/chrome/android/chrome_apk_paks/resources.pak",
+      "$root_gen_dir/components/components_resources.pak",
+      "$root_gen_dir/components/dev_ui_components_resources.pak",
+    ]
+  } else {  # !is_android
+    sources += [
+      "apps_sync_test_base.cc",
+      "apps_sync_test_base.h",
+      "enable_disable_test.cc",
+      "local_sync_test.cc",
+      "migration_test.cc",
+      "password_manager_sync_test.cc",
+      "single_client_app_settings_sync_test.cc",
+      "single_client_common_sync_test.cc",
+      "single_client_dictionary_sync_test.cc",
+      "single_client_extension_apps_sync_test.cc",
+      "single_client_extensions_sync_test.cc",
+      "single_client_incoming_password_sharing_invitation_test.cc",
+      "single_client_nigori_sync_test.cc",
+      "single_client_outgoing_password_sharing_invitation_test.cc",
+      "single_client_password_sharing_policy_test.cc",
+      "single_client_passwords_sync_test.cc",
+      "single_client_polling_sync_test.cc",
+      "single_client_product_specifications_sync_test.cc",
+      "single_client_saved_tab_groups_sync_test.cc",
+      "single_client_search_engines_sync_test.cc",
+      "single_client_secondary_account_sync_test.cc",
+      "single_client_send_tab_to_self_sync_test.cc",
+      "single_client_sessions_sync_test.cc",
+      "single_client_sharing_message_sync_test.cc",
+      "single_client_themes_sync_test.cc",
+      "single_client_user_consents_sync_test.cc",
+      "single_client_user_events_sync_test.cc",
+      "single_client_valuables_sync_test.cc",
+      "single_client_wallet_credential_sync_test.cc",
+      "single_client_wallet_sync_test.cc",
+      "single_client_web_apps_sync_generated_icon_fix_sync_test.cc",
+      "single_client_web_apps_sync_test.cc",
+      "single_client_webauthn_credentials_sync_test.cc",
+      "sync_auth_test.cc",
+      "sync_errors_test.cc",
+      "web_apps_sync_test_base.cc",
+      "web_apps_sync_test_base.h",
+
+      # Tests with two clients can't run on Android.
+      "two_client_app_settings_sync_test.cc",
+      "two_client_autocomplete_sync_test.cc",
+      "two_client_autofill_sync_test.cc",
+      "two_client_bookmarks_sync_test.cc",
+      "two_client_contact_info_sync_test.cc",
+      "two_client_custom_passphrase_sync_test.cc",
+      "two_client_dictionary_sync_test.cc",
+      "two_client_extension_apps_sync_test.cc",
+      "two_client_extension_settings_sync_test.cc",
+      "two_client_extensions_sync_test.cc",
+      "two_client_history_sync_test.cc",
+      "two_client_passwords_sync_test.cc",
+      "two_client_power_bookmarks_sync_test.cc",
+      "two_client_preferences_sync_test.cc",
+      "two_client_search_engines_sync_test.cc",
+      "two_client_send_tab_to_self_sync_test.cc",
+      "two_client_sessions_sync_test.cc",
+      "two_client_shared_tab_group_data_sync_test.cc",
+      "two_client_themes_sync_test.cc",
+      "two_client_user_events_sync_test.cc",
+      "two_client_wallet_credential_sync_test.cc",
+      "two_client_wallet_sync_test.cc",
+      "two_client_web_apps_bmo_sync_test.cc",
+      "two_client_web_apps_generated_icon_fix_test.cc",
+      "two_client_web_apps_integration_test.cc",
+      "two_client_web_apps_integration_test_base.cc",
+      "two_client_web_apps_integration_test_base.h",
+      "two_client_web_apps_sync_test.cc",
+      "two_client_webauthn_credentials_sync_test.cc",
+    ]
+    if (is_win || is_mac || is_linux) {
+      sources += [ "two_client_web_apps_integration_test_mac_win_linux.cc" ]
+    }
+
+    if (is_win || is_linux) {
+      sources += [ "two_client_web_apps_integration_test_win_linux.cc" ]
+    }
+
+    if (is_mac) {
+      sources += [ "two_client_web_apps_integration_test_mac.cc" ]
+    }
+
+    if (is_win) {
+      sources += [ "two_client_web_apps_integration_test_win.cc" ]
+    }
+
+    if (is_chromeos) {
+      sources += [ "two_client_web_apps_integration_test_cros.cc" ]
+    }
+
+    if (!is_chromeos) {
+      sources += [
+        "select_type_and_migrate_local_data_items_when_active_sync_test.cc",
+      ]
+    }
+
+    deps += [
+      "//chrome:packed_resources",
+      "//chrome:resources",
+      "//chrome:strings",
+      "//chrome/app:chrome_dll_resources",
+      "//chrome/app:command_ids",
+      "//chrome/browser/affiliations",
+      "//chrome/browser/apps/link_capturing",
+      "//chrome/browser/autofill",
+      "//chrome/browser/browsing_data:constants",
+      "//chrome/browser/metrics/desktop_session_duration",
+      "//chrome/browser/search_engines",
+      "//chrome/browser/themes",
+      "//chrome/browser/ui:ui_features",
+      "//chrome/browser/web_applications:features",
+      "//chrome/browser/web_applications:web_applications_test_support",
+      "//chrome/common",
+      "//chrome/renderer",
+      "//chrome/test:test_support_ui",
+      "//components/app_constants",
+      "//components/bookmarks/test",
+      "//components/browser_sync",
+      "//components/commerce/core:feature_list",
+      "//components/consent_auditor",
+      "//components/favicon/core",
+      "//components/history/content/browser",
+      "//components/history/core/common",
+      "//components/power_bookmarks/common:test_support",
+      "//components/power_bookmarks/core",
+      "//components/reading_list/core",
+      "//components/saved_tab_groups/internal:tab_group_sync_bridge",
+      "//components/search_engines",
+      "//components/send_tab_to_self",
+      "//components/spellcheck/common",
+      "//components/sync",
+      "//components/trusted_vault",
+      "//components/trusted_vault:test_support",
+      "//components/webapps/browser",
+      "//components/webapps/common",
+      "//components/webauthn/core/browser",
+      "//components/webauthn/core/browser:passkey_model",
+      "//testing/gmock",
+      "//testing/gtest",
+      "//third_party/blink/public:blink",
+      "//third_party/icu",
+      "//third_party/leveldatabase",
+    ]
+
+    data_deps = [
+      "//testing:test_scripts_shared",
+      "//testing/buildbot/filters:sync_integration_tests_filters",
+      "//third_party/angle:includes",
+    ]
+
+    if (is_linux || is_chromeos || is_win) {
+      data_deps += [ "//chrome:packed_resources" ]
+    }
+
+    if (is_mac) {
+      # Dictionary sync is disabled on Mac.
+      sources -= [
+        "single_client_dictionary_sync_test.cc",
+        "two_client_dictionary_sync_test.cc",
+      ]
+
+      data_deps += [
+        "//chrome",
+        "//chrome:chrome_framework",
+      ]
+    }
+    if (is_win) {
+      deps += [
+        "//chrome:other_version",
+        "//third_party/wtl",
+        "//ui/resources",
+      ]
+    }
+    if (is_chromeos) {
+      sources += [
+        "single_client_app_list_sync_test.cc",
+        "single_client_arc_package_sync_test.cc",
+        "single_client_cookies_sync_test.cc",
+        "single_client_os_preferences_sync_test.cc",
+        "single_client_printers_sync_test.cc",
+        "single_client_wifi_configuration_sync_test.cc",
+        "single_client_workspace_desk_sync_test.cc",
+        "two_client_app_list_sync_test.cc",
+        "two_client_arc_package_sync_test.cc",
+        "two_client_os_preferences_sync_test.cc",
+        "two_client_printers_sync_test.cc",
+        "two_client_workspace_desk_sync_test.cc",
+      ]
+      deps += [
+        "//chrome/browser/ash/app_list",
+        "//chrome/browser/ash/app_list/arc",
+        "//chrome/browser/ash/app_list/test:test_support",
+        "//chrome/browser/ash/crosapi",
+        "//chrome/browser/ash/floating_sso",
+        "//chrome/browser/ash/floating_sso:test_support",
+        "//chrome/browser/ash/printing",
+        "//chrome/browser/ash/sync",
+        "//chrome/browser/ash/system_web_apps/test_support",
+        "//chromeos/ash/components/browser_context_helper",
+        "//chromeos/ash/components/dbus/shill",
+        "//chromeos/ash/components/sync_wifi",
+        "//chromeos/ash/components/sync_wifi:test_support",
+        "//chromeos/printing",
+        "//chromeos/services/network_config/public/cpp",
+        "//components/desks_storage",
+      ]
+    }
+
+    if (toolkit_views) {
+      deps += [ "//ui/views" ]
+    }
+    if (enable_printing) {
+      deps += [ "//printing" ]
+    }
+    if (enable_glic) {
+      deps += [
+        "//chrome/browser/glic",
+        "//chrome/browser/glic/test_support",
+      ]
+    }
+    data += [
+      "//chrome/test/data/password/",
+      "//chrome/test/data/webapps_integration/",
+      "//chrome/test/data/web_apps/",
+      "//chrome/test/data/banners/",
+      "//net/tools/testserver/",
+      "//third_party/pywebsocket3/src/mod_pywebsocket/",
+    ]
+  }
+}
+
+if (is_android) {
+  java_group("sync_integration_tests_java") {
+    deps = [
+      "//chrome/android:delegate_public_impl_java",
+      "//chrome/test:android_browsertests_assets",
+      "//chrome/test:android_browsertests_java",
+      "//chrome/test:sync_integration_test_support_java",
+      "//chrome/test:test_support_java",
+    ]
+  }
+}
diff --git a/chrome/browser/sync/test/integration/local_sync_test.cc b/chrome/browser/sync/test/integration/local_sync_test.cc
index 773feae..5990daa 100644
--- a/chrome/browser/sync/test/integration/local_sync_test.cc
+++ b/chrome/browser/sync/test/integration/local_sync_test.cc
@@ -12,6 +12,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/send_tab_to_self/send_tab_to_self_util.h"
 #include "chrome/browser/sync/sync_service_factory.h"
+#include "chrome/browser/sync/test/integration/encryption_helper.h"
 #include "chrome/browser/sync/test/integration/single_client_status_change_checker.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/ui_features.h"
@@ -26,10 +27,15 @@
 #include "content/public/test/browser_test.h"
 #include "crypto/ec_private_key.h"
 
+// The local sync backend is currently only supported on Windows, Mac, Linux.
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
+
 namespace {
 
 using syncer::SyncServiceImpl;
 
+constexpr char kTestPassphrase[] = "hunter2";
+
 class SyncTransportActiveChecker : public SingleClientStatusChangeChecker {
  public:
   explicit SyncTransportActiveChecker(SyncServiceImpl* service)
@@ -73,8 +79,6 @@
   base::ScopedTempDir local_sync_backend_dir_;
 };
 
-// The local sync backend is currently only supported on Windows, Mac, Linux.
-#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
 IN_PROC_BROWSER_TEST_F(LocalSyncTest, ShouldStart) {
   SyncServiceImpl* service =
       SyncServiceFactory::GetAsSyncServiceImplForProfileForTesting(
@@ -140,6 +144,47 @@
   EXPECT_FALSE(service->GetActiveDataTypes().Has(syncer::SEND_TAB_TO_SELF));
   EXPECT_FALSE(service->GetActiveDataTypes().Has(syncer::HISTORY));
 }
-#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
+
+IN_PROC_BROWSER_TEST_F(LocalSyncTest, ShouldHonorSelectedTypes) {
+  SyncServiceImpl* service =
+      SyncServiceFactory::GetAsSyncServiceImplForProfileForTesting(
+          browser()->profile());
+
+  // Wait until the first sync cycle is completed.
+  ASSERT_TRUE(SyncTransportActiveChecker(service).Wait());
+
+  ASSERT_TRUE(service->IsLocalSyncEnabled());
+  ASSERT_FALSE(service->IsSyncFeatureEnabled());
+  ASSERT_TRUE(service->GetActiveDataTypes().Has(syncer::BOOKMARKS));
+  ASSERT_TRUE(service->GetActiveDataTypes().Has(syncer::PASSWORDS));
+
+  service->GetUserSettings()->SetSelectedTypes(
+      /*sync_everything=*/false, {syncer::UserSelectableType::kPasswords});
+
+  ASSERT_TRUE(SyncTransportActiveChecker(service).Wait());
+
+  EXPECT_TRUE(service->GetActiveDataTypes().Has(syncer::PASSWORDS));
+  EXPECT_FALSE(service->GetActiveDataTypes().Has(syncer::BOOKMARKS));
+}
+
+// Setting up a custom passphrase is arguably meaningless for local sync, but it
+// has been allowed historically.
+IN_PROC_BROWSER_TEST_F(LocalSyncTest, ShouldSupportCustomPassphrase) {
+  SyncServiceImpl* service =
+      SyncServiceFactory::GetAsSyncServiceImplForProfileForTesting(
+          browser()->profile());
+
+  // Wait until the first sync cycle is completed.
+  ASSERT_TRUE(SyncTransportActiveChecker(service).Wait());
+
+  ASSERT_TRUE(service->IsLocalSyncEnabled());
+  ASSERT_FALSE(service->IsSyncFeatureEnabled());
+
+  service->GetUserSettings()->SetEncryptionPassphrase(kTestPassphrase);
+
+  EXPECT_TRUE(PassphraseAcceptedChecker(service).Wait());
+}
 
 }  // namespace
+
+#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
diff --git a/chrome/browser/sync/test/integration/sync_integration_tests.gni b/chrome/browser/sync/test/integration/sync_integration_tests.gni
deleted file mode 100644
index 39ded73..0000000
--- a/chrome/browser/sync/test/integration/sync_integration_tests.gni
+++ /dev/null
@@ -1,187 +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.
-
-sync_integration_tests_sources = [
-  "//chrome/browser/sync/test/integration/single_client_autofill_profile_sync_test.cc",
-  "//chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc",
-  "//chrome/browser/sync/test/integration/single_client_collaboration_group_sync_test.cc",
-  "//chrome/browser/sync/test/integration/single_client_contact_info_sync_test.cc",
-  "//chrome/browser/sync/test/integration/single_client_custom_passphrase_sync_test.cc",
-  "//chrome/browser/sync/test/integration/single_client_device_info_sync_test.cc",
-  "//chrome/browser/sync/test/integration/single_client_history_delete_directives_sync_test.cc",
-  "//chrome/browser/sync/test/integration/single_client_history_sync_test.cc",
-  "//chrome/browser/sync/test/integration/single_client_offer_sync_test.cc",
-  "//chrome/browser/sync/test/integration/single_client_plus_address_setting_sync_test.cc",
-  "//chrome/browser/sync/test/integration/single_client_plus_address_sync_test.cc",
-  "//chrome/browser/sync/test/integration/single_client_preferences_sync_test.cc",
-  "//chrome/browser/sync/test/integration/single_client_reading_list_sync_test.cc",
-  "//chrome/browser/sync/test/integration/single_client_shared_tab_group_data_sync_test.cc",
-  "//chrome/browser/sync/test/integration/single_client_standalone_transport_sync_test.cc",
-  "//chrome/browser/sync/test/integration/single_client_sync_invalidations_test.cc",
-  "//chrome/browser/sync/test/integration/sync_exponential_backoff_test.cc",
-
-  # TODO(crbug.com/393119606): After enough of the implementation is ready
-  # (CL #N), add integration tests. Careful, sync_integration_tests runs in CQ
-  # but android_sync_integration_tests do not.
-
-  # TODO(crbug.com/397767033): After enough of the implementation is ready
-  # (CL #N), add integration tests. Careful, sync_integration_tests runs in CQ
-  # but android_sync_integration_tests do not.
-]
-
-if (!is_android) {
-  sync_integration_tests_sources += [
-    "//chrome/browser/sync/test/integration/apps_sync_test_base.cc",
-    "//chrome/browser/sync/test/integration/apps_sync_test_base.h",
-    "//chrome/browser/sync/test/integration/enable_disable_test.cc",
-    "//chrome/browser/sync/test/integration/local_sync_test.cc",
-    "//chrome/browser/sync/test/integration/migration_test.cc",
-    "//chrome/browser/sync/test/integration/password_manager_sync_test.cc",
-    "//chrome/browser/sync/test/integration/single_client_app_settings_sync_test.cc",
-    "//chrome/browser/sync/test/integration/single_client_common_sync_test.cc",
-    "//chrome/browser/sync/test/integration/single_client_dictionary_sync_test.cc",
-    "//chrome/browser/sync/test/integration/single_client_extension_apps_sync_test.cc",
-    "//chrome/browser/sync/test/integration/single_client_extensions_sync_test.cc",
-    "//chrome/browser/sync/test/integration/single_client_incoming_password_sharing_invitation_test.cc",
-    "//chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc",
-    "//chrome/browser/sync/test/integration/single_client_outgoing_password_sharing_invitation_test.cc",
-    "//chrome/browser/sync/test/integration/single_client_password_sharing_policy_test.cc",
-    "//chrome/browser/sync/test/integration/single_client_passwords_sync_test.cc",
-    "//chrome/browser/sync/test/integration/single_client_polling_sync_test.cc",
-    "//chrome/browser/sync/test/integration/single_client_product_specifications_sync_test.cc",
-    "//chrome/browser/sync/test/integration/single_client_saved_tab_groups_sync_test.cc",
-    "//chrome/browser/sync/test/integration/single_client_search_engines_sync_test.cc",
-    "//chrome/browser/sync/test/integration/single_client_secondary_account_sync_test.cc",
-    "//chrome/browser/sync/test/integration/single_client_send_tab_to_self_sync_test.cc",
-    "//chrome/browser/sync/test/integration/single_client_sessions_sync_test.cc",
-    "//chrome/browser/sync/test/integration/single_client_sharing_message_sync_test.cc",
-    "//chrome/browser/sync/test/integration/single_client_themes_sync_test.cc",
-    "//chrome/browser/sync/test/integration/single_client_user_consents_sync_test.cc",
-    "//chrome/browser/sync/test/integration/single_client_user_events_sync_test.cc",
-    "//chrome/browser/sync/test/integration/single_client_valuables_sync_test.cc",
-    "//chrome/browser/sync/test/integration/single_client_wallet_credential_sync_test.cc",
-    "//chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc",
-    "//chrome/browser/sync/test/integration/single_client_web_apps_sync_generated_icon_fix_sync_test.cc",
-    "//chrome/browser/sync/test/integration/single_client_web_apps_sync_test.cc",
-    "//chrome/browser/sync/test/integration/single_client_webauthn_credentials_sync_test.cc",
-    "//chrome/browser/sync/test/integration/sync_auth_test.cc",
-    "//chrome/browser/sync/test/integration/sync_errors_test.cc",
-    "//chrome/browser/sync/test/integration/web_apps_sync_test_base.cc",
-    "//chrome/browser/sync/test/integration/web_apps_sync_test_base.h",
-
-    # Tests with two clients can't run on Android.
-    "//chrome/browser/sync/test/integration/two_client_app_settings_sync_test.cc",
-    "//chrome/browser/sync/test/integration/two_client_autocomplete_sync_test.cc",
-    "//chrome/browser/sync/test/integration/two_client_autofill_sync_test.cc",
-    "//chrome/browser/sync/test/integration/two_client_bookmarks_sync_test.cc",
-    "//chrome/browser/sync/test/integration/two_client_contact_info_sync_test.cc",
-    "//chrome/browser/sync/test/integration/two_client_custom_passphrase_sync_test.cc",
-    "//chrome/browser/sync/test/integration/two_client_dictionary_sync_test.cc",
-    "//chrome/browser/sync/test/integration/two_client_extension_apps_sync_test.cc",
-    "//chrome/browser/sync/test/integration/two_client_extension_settings_sync_test.cc",
-    "//chrome/browser/sync/test/integration/two_client_extensions_sync_test.cc",
-    "//chrome/browser/sync/test/integration/two_client_history_sync_test.cc",
-    "//chrome/browser/sync/test/integration/two_client_passwords_sync_test.cc",
-    "//chrome/browser/sync/test/integration/two_client_power_bookmarks_sync_test.cc",
-    "//chrome/browser/sync/test/integration/two_client_preferences_sync_test.cc",
-    "//chrome/browser/sync/test/integration/two_client_search_engines_sync_test.cc",
-    "//chrome/browser/sync/test/integration/two_client_send_tab_to_self_sync_test.cc",
-    "//chrome/browser/sync/test/integration/two_client_sessions_sync_test.cc",
-    "//chrome/browser/sync/test/integration/two_client_shared_tab_group_data_sync_test.cc",
-    "//chrome/browser/sync/test/integration/two_client_themes_sync_test.cc",
-    "//chrome/browser/sync/test/integration/two_client_user_events_sync_test.cc",
-    "//chrome/browser/sync/test/integration/two_client_wallet_credential_sync_test.cc",
-    "//chrome/browser/sync/test/integration/two_client_wallet_sync_test.cc",
-    "//chrome/browser/sync/test/integration/two_client_web_apps_bmo_sync_test.cc",
-    "//chrome/browser/sync/test/integration/two_client_web_apps_generated_icon_fix_test.cc",
-    "//chrome/browser/sync/test/integration/two_client_web_apps_integration_test.cc",
-    "//chrome/browser/sync/test/integration/two_client_web_apps_integration_test_base.cc",
-    "//chrome/browser/sync/test/integration/two_client_web_apps_integration_test_base.h",
-    "//chrome/browser/sync/test/integration/two_client_web_apps_sync_test.cc",
-    "//chrome/browser/sync/test/integration/two_client_webauthn_credentials_sync_test.cc",
-  ]
-}
-
-if (is_win || is_mac || is_linux) {
-  sync_integration_tests_sources += [ "//chrome/browser/sync/test/integration/two_client_web_apps_integration_test_mac_win_linux.cc" ]
-}
-
-if (is_win || is_linux) {
-  sync_integration_tests_sources += [ "//chrome/browser/sync/test/integration/two_client_web_apps_integration_test_win_linux.cc" ]
-}
-
-if (is_mac) {
-  sync_integration_tests_sources += [ "//chrome/browser/sync/test/integration/two_client_web_apps_integration_test_mac.cc" ]
-}
-
-if (is_win) {
-  sync_integration_tests_sources += [ "//chrome/browser/sync/test/integration/two_client_web_apps_integration_test_win.cc" ]
-}
-
-if (is_chromeos) {
-  sync_integration_tests_sources += [ "//chrome/browser/sync/test/integration/two_client_web_apps_integration_test_cros.cc" ]
-}
-
-if (!is_chromeos && !is_android) {
-  sync_integration_tests_sources += [ "//chrome/browser/sync/test/integration/select_type_and_migrate_local_data_items_when_active_sync_test.cc" ]
-}
-
-if (is_android) {
-  sync_integration_tests_sources += [
-    "//chrome/browser/sync/test/integration/single_client_web_apks_sync_test.cc",
-    "//chrome/test/android/browsertests_apk/android_browsertests_jni_onload.cc",
-  ]
-
-  # Defined here so it's shared between android_sync_integration_tests (upstream)
-  # and android_live_sync_integration_tests (downstream).
-  android_sync_integration_tests_deps = [
-    "//chrome:chrome_android_core",
-    "//chrome/android:delegate_public_impl_java",
-    "//chrome/browser/android/webapk:webapk_sources",
-    "//chrome/browser/plus_addresses",
-    "//chrome/browser/prefs",
-    "//chrome/browser/reading_list",
-    "//chrome/browser/sync",
-    "//chrome/browser/ui:browser_navigator_params_headers",
-    "//chrome/browser/ui:ui_features",
-    "//chrome/test:android_browsertests_assets",
-    "//chrome/test:android_browsertests_java",
-    "//chrome/test:browser_tests_runner",
-    "//chrome/test:chrome_test_launcher",
-    "//chrome/test:sync_integration_test_support",
-    "//chrome/test:sync_integration_test_support_java",
-    "//chrome/test:test_support",
-    "//chrome/test:test_support_java",
-    "//chrome/test:test_support_ui_android",
-    "//components/bookmarks/browser",
-    "//components/browser_sync:switches",
-    "//components/data_sharing/public",
-    "//components/password_manager/core/browser/sharing",
-    "//components/plus_addresses:test_support",
-    "//components/plus_addresses/resources/strings",
-    "//components/plus_addresses/webdata",
-    "//components/reading_list/core:test_support",
-    "//components/saved_tab_groups/public",
-    "//components/saved_tab_groups/test_support",
-    "//components/sync:test_support",
-    "//components/sync_bookmarks",
-    "//components/sync_device_info:test_support",
-    "//components/sync_preferences:common_syncable_prefs_database",
-    "//components/undo",
-    "//components/version_info",
-    "//content/test:android_test_message_pump_support",
-    "//testing/android/native_test:native_test_support",
-  ]
-
-  # Defined here so it's shared between android_sync_integration_tests (upstream)
-  # and android_live_sync_integration_tests (downstream).
-  android_sync_integration_tests_data = [
-    "//chrome/test/data/sync/",
-    "$root_gen_dir/chrome/android/chrome_apk_paks/chrome_100_percent.pak",
-    "$root_gen_dir/chrome/android/chrome_apk_paks/locales/en-US.pak",
-    "$root_gen_dir/chrome/android/chrome_apk_paks/resources.pak",
-    "$root_gen_dir/components/components_resources.pak",
-    "$root_gen_dir/components/dev_ui_components_resources.pak",
-  ]
-}
diff --git a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabClosureParams.java b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabClosureParams.java
index 85eaad98..8dafe09 100644
--- a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabClosureParams.java
+++ b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabClosureParams.java
@@ -177,12 +177,10 @@
         }
     }
 
-    /**
-     * Builder to configure params for closing all tabs. Closing all tabs always allows for undo if
-     * permitted by the tab model.
-     */
+    /** Builder to configure params for closing all tabs. */
     public static class CloseAllTabsBuilder {
         private boolean mUponExit;
+        private boolean mAllowUndo = true;
         private boolean mHideTabGroups;
         private @Nullable Runnable mUndoRunnable;
 
@@ -194,6 +192,12 @@
             return this;
         }
 
+        /** Set whether to allow undo. Default is true. */
+        public CloseAllTabsBuilder allowUndo(boolean allowUndo) {
+            mAllowUndo = allowUndo;
+            return this;
+        }
+
         /** Set whether to hide or delete tab groups. Default is delete. */
         public CloseAllTabsBuilder hideTabGroups(boolean hideTabGroups) {
             mHideTabGroups = hideTabGroups;
@@ -213,7 +217,7 @@
                     /* isAllTabs= */ true,
                     /* recommendedNextTab= */ null,
                     mUponExit,
-                    /* allowUndo= */ true,
+                    mAllowUndo,
                     mHideTabGroups,
                     /* saveToTabRestoreService= */ true,
                     TabCloseType.ALL,
diff --git a/chrome/browser/task_manager/providers/web_contents/web_contents_task_provider.cc b/chrome/browser/task_manager/providers/web_contents/web_contents_task_provider.cc
index f80c8a5..397b028 100644
--- a/chrome/browser/task_manager/providers/web_contents/web_contents_task_provider.cc
+++ b/chrome/browser/task_manager/providers/web_contents/web_contents_task_provider.cc
@@ -204,11 +204,11 @@
 
 RenderFrameHost* WebContentsTaskProvider::WebContentsEntry::FindLocalRoot(
     RenderFrameHost* render_frame_host) const {
-  SiteInstance* site_instance = render_frame_host->GetSiteInstance();
   RenderFrameHost* candidate = render_frame_host;
   while (RenderFrameHost* parent = candidate->GetParent()) {
-    if (parent->GetSiteInstance() != site_instance)
+    if (parent->GetProcess() != candidate->GetProcess()) {
       break;
+    }
     candidate = parent;
   }
   return candidate;
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 662e8849..283c198 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1152,23 +1152,6 @@
       "sad_tab_types.h",
       "scoped_tabbed_browser_displayer.cc",
       "scoped_tabbed_browser_displayer.h",
-      "search/instant_controller.cc",
-      "search/instant_controller.h",
-      "search/new_tab_page_navigation_throttle.cc",
-      "search/new_tab_page_navigation_throttle.h",
-      "search/ntp_user_data_logger.cc",
-      "search/ntp_user_data_logger.h",
-      "search/ntp_user_data_types.h",
-      "search/omnibox_utils.cc",
-      "search/omnibox_utils.h",
-      "search/search_ipc_router.cc",
-      "search/search_ipc_router.h",
-      "search/search_ipc_router_policy_impl.cc",
-      "search/search_ipc_router_policy_impl.h",
-      "search/search_tab_helper.cc",
-      "search/search_tab_helper.h",
-      "shared_highlighting/shared_highlighting_promo.cc",
-      "shared_highlighting/shared_highlighting_promo.h",
       "singleton_tabs.cc",
       "singleton_tabs.h",
       "startup/automation_infobar_delegate.cc",
@@ -1662,8 +1645,11 @@
       "//chrome/browser/first_party_sets",
       "//chrome/browser/lifetime:termination_notification",
       "//chrome/browser/privacy_sandbox",
+      "//chrome/browser/ui/search",
+      "//chrome/browser/ui/search:impl",
       "//chrome/browser/ui/search_engine_choice",
       "//chrome/browser/ui/search_engine_choice:impl",
+      "//chrome/browser/ui/shared_highlighting",
       "//chrome/browser/ui/webui/access_code_cast",
       "//chrome/browser/ui/webui/access_code_cast:impl",
 
@@ -1905,6 +1891,7 @@
       "//chrome/browser/ui/views/page_action",
       "//chrome/browser/ui/webui/searchbox",
       "//chrome/browser/ui/webui/top_chrome:impl",
+      "//chrome/browser/ui/search:impl",
 
       # TODO(crbug.com/364501603): remove this as it depends on
       # //c/b/ui/tabs/tab_strip_model.h, which is still part of
diff --git a/chrome/browser/ui/android/appmenu/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuPropertiesDelegate.java b/chrome/browser/ui/android/appmenu/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuPropertiesDelegate.java
index a996fc18..5b13f6f 100644
--- a/chrome/browser/ui/android/appmenu/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuPropertiesDelegate.java
+++ b/chrome/browser/ui/android/appmenu/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuPropertiesDelegate.java
@@ -17,8 +17,6 @@
 /** App Menu helper that handles hiding and showing menu items based on activity state. */
 @NullMarked
 public interface AppMenuPropertiesDelegate {
-    int INVALID_ITEM_ID = -1;
-
     /** Provides unique custom item view type across all custom binders. */
     public interface CustomItemViewTypeProvider {
         /**
diff --git a/chrome/browser/ui/android/appmenu/test/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuTestSupport.java b/chrome/browser/ui/android/appmenu/test/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuTestSupport.java
index fda2be59e..d947c43 100644
--- a/chrome/browser/ui/android/appmenu/test/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuTestSupport.java
+++ b/chrome/browser/ui/android/appmenu/test/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuTestSupport.java
@@ -5,10 +5,12 @@
 package org.chromium.chrome.browser.ui.appmenu;
 
 import android.os.Bundle;
+import android.view.MotionEvent;
 import android.view.View;
 import android.widget.ListView;
 
 import org.chromium.base.Callback;
+import org.chromium.build.annotations.Nullable;
 import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
 import org.chromium.ui.modelutil.PropertyModel;
 
@@ -42,11 +44,26 @@
     }
 
     /**
-     * Simulates a click on the menu item matching the provided id.
-     * @param coordinator The {@link AppMenuCoordinator} associated with the app menu being tested.
-     * @param menuItemId The id of the menu item to click.
+     * Simulates a click on a menu item.
+     *
+     * @see #callOnItemClick(AppMenuCoordinator, int, MotionEvent)
      */
     public static void callOnItemClick(AppMenuCoordinator coordinator, int menuItemId) {
+        callOnItemClick(coordinator, menuItemId, /* triggeringMotionEvent= */ null);
+    }
+
+    /**
+     * Simulates a click on the menu item matching the provided id.
+     *
+     * @param coordinator The {@link AppMenuCoordinator} associated with the app menu being tested.
+     * @param menuItemId The id of the menu item to click.
+     * @param triggeringMotionEvent The {@link MotionEvent} that triggered the click. See {@link
+     *     AppMenuClickHandler#onItemClick(PropertyModel, MotionEvent)}.
+     */
+    public static void callOnItemClick(
+            AppMenuCoordinator coordinator,
+            int menuItemId,
+            @Nullable MotionEvent triggeringMotionEvent) {
         PropertyModel model =
                 ((AppMenuCoordinatorImpl) coordinator)
                         .getAppMenuHandlerImplForTesting()
@@ -56,7 +73,7 @@
         ((AppMenuCoordinatorImpl) coordinator)
                 .getAppMenuHandlerImplForTesting()
                 .getAppMenu()
-                .onItemClick(model);
+                .onItemClick(model, triggeringMotionEvent);
     }
 
     /**
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java
index 18af96ce..aa4a78c 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java
@@ -672,15 +672,15 @@
     /**
      * Updates progress of current the URL focus change animation.
      *
-     * @param ntpSearchBoxScrollFraction The degree to which the omnibox has expanded to full width
-     *     in NTP due to the NTP search box is being scrolled up.
+     * @param ntpUrlExpansionFraction The degree to which the omnibox has expanded to full width on
+     *     the NTP.
      * @param urlFocusChangeFraction The degree to which the omnibox has expanded due to it is
      *     getting focused.
      */
     public void setUrlFocusChangeFraction(
-            float ntpSearchBoxScrollFraction, float urlFocusChangeFraction) {
+            float ntpUrlExpansionFraction, float urlFocusChangeFraction) {
         mLocationBarMediator.setUrlFocusChangeFraction(
-                ntpSearchBoxScrollFraction, urlFocusChangeFraction);
+                ntpUrlExpansionFraction, urlFocusChangeFraction);
     }
 
     /**
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java
index e109521..c49cf5e 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java
@@ -1093,9 +1093,6 @@
     private void setProfile(Profile profile) {
         if (profile == null || !mNativeInitialized) return;
 
-        mUrlCoordinator.setUrlBarHintText(
-                SearchEngineUtils.getForProfile(mProfileSupplier.get()).getSearchBoxHintText());
-
         assumeNonNull(mOmniboxPrerender);
         mOmniboxPrerender.initializeForProfile(profile);
         mSearchEngineUtils = SearchEngineUtils.getForProfile(profile);
@@ -1382,8 +1379,6 @@
     @Override
     public void onTemplateURLServiceChanged() {
         sLastCachedIsLensOnOmniboxEnabled = Boolean.valueOf(isLensEnabled(LensEntryPoint.OMNIBOX));
-        mUrlCoordinator.setUrlBarHintText(
-                SearchEngineUtils.getForProfile(mProfileSupplier.get()).getSearchBoxHintText());
     }
 
     // OmniboxStub implementation.
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarCoordinator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarCoordinator.java
index e0c45f7..d26f09d 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarCoordinator.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarCoordinator.java
@@ -11,6 +11,7 @@
 import android.view.inputmethod.InputMethodManager;
 
 import androidx.annotation.IntDef;
+import androidx.annotation.StringRes;
 
 import org.chromium.base.Callback;
 import org.chromium.build.annotations.NullMarked;
@@ -369,9 +370,9 @@
     }
 
     /**
-     * @see UrlBarMediator#setUrlBarHintText(String)
+     * @see UrlBarMediator#setUrlBarHintText(int)
      */
-    public void setUrlBarHintText(String hintTextRes) {
+    public void setUrlBarHintText(@StringRes int hintTextRes) {
         mMediator.setUrlBarHintText(hintTextRes);
     }
 }
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarMediator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarMediator.java
index 0f296ff..b25deb0b 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarMediator.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarMediator.java
@@ -12,6 +12,7 @@
 import android.view.View;
 
 import androidx.annotation.ColorInt;
+import androidx.annotation.StringRes;
 import androidx.annotation.VisibleForTesting;
 
 import org.chromium.base.Callback;
@@ -415,8 +416,8 @@
     }
 
     /** Sets the search box hint text. */
-    void setUrlBarHintText(String hintText) {
-        mModel.set(UrlBarProperties.HINT_TEXT, hintText);
+    void setUrlBarHintText(@StringRes int hintTextRes) {
+        mModel.set(UrlBarProperties.HINT_TEXT, hintTextRes);
     }
 
     void setShowOriginOnly(boolean showOriginOnly) {
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarMediatorUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarMediatorUnitTest.java
index 2032084..6659a43 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarMediatorUnitTest.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarMediatorUnitTest.java
@@ -46,13 +46,11 @@
     @Mock Callback<String> mAnotherUrlTextMockListener;
     @Mock Callback<Boolean> mFocusChangeCallback;
 
-    Context mContext;
     PropertyModel mModel;
     UrlBarMediator mMediator;
 
     @Before
     public void setUp() {
-        mContext = ContextUtils.getApplicationContext();
         mModel = new PropertyModel(UrlBarProperties.ALL_KEYS);
         mMediator =
                 new UrlBarMediator(
@@ -325,10 +323,11 @@
 
     @Test
     public void setUrlBarHintText() {
-        mMediator.setUrlBarHintText("Hint 1");
-        Assert.assertEquals("Hint 1", mModel.get(UrlBarProperties.HINT_TEXT));
-        mMediator.setUrlBarHintText("Incognito Hint");
-        Assert.assertEquals("Incognito Hint", mModel.get(UrlBarProperties.HINT_TEXT));
+        mMediator.setUrlBarHintText(R.string.hub_search_empty_hint);
+        Assert.assertEquals(R.string.hub_search_empty_hint, mModel.get(UrlBarProperties.HINT_TEXT));
+        mMediator.setUrlBarHintText(R.string.hub_search_empty_hint_incognito);
+        Assert.assertEquals(
+                R.string.hub_search_empty_hint_incognito, mModel.get(UrlBarProperties.HINT_TEXT));
     }
 
     @Test
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarProperties.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarProperties.java
index bb72256..deaddaf 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarProperties.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarProperties.java
@@ -178,9 +178,8 @@
     public static final WritableObjectPropertyKey<View.OnLongClickListener> LONG_CLICK_LISTENER =
             new WritableObjectPropertyKey<>();
 
-    /** Specifies the url bar hint text. */
-    public static final WritableObjectPropertyKey<String> HINT_TEXT =
-            new WritableObjectPropertyKey<>();
+    /** Specifies the resource ID for the url bar hint text. */
+    public static final WritableIntPropertyKey HINT_TEXT = new WritableIntPropertyKey();
 
     public static final PropertyKey[] ALL_KEYS =
             new PropertyKey[] {
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarViewBinder.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarViewBinder.java
index 3cdbed2..feb7595 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarViewBinder.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarViewBinder.java
@@ -136,7 +136,7 @@
         } else if (UrlBarProperties.LONG_CLICK_LISTENER.equals(propertyKey)) {
             view.setOnLongClickListener(model.get(UrlBarProperties.LONG_CLICK_LISTENER));
         } else if (UrlBarProperties.HINT_TEXT.equals(propertyKey)) {
-            view.setHint(model.get(UrlBarProperties.HINT_TEXT));
+            view.setHint(view.getContext().getString(model.get(UrlBarProperties.HINT_TEXT)));
         }
     }
 
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarViewBinderUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarViewBinderUnitTest.java
index 98976e5..8a339b9 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarViewBinderUnitTest.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarViewBinderUnitTest.java
@@ -190,10 +190,11 @@
     @Test
     @SmallTest
     public void testSetHintText() {
-        mModel.set(HINT_TEXT, "Hint Text");
-        Assert.assertEquals("Hint Text", mUrlBar.getHint());
-        mModel.set(HINT_TEXT, "Different Hint Text");
-        Assert.assertEquals("Different Hint Text", mUrlBar.getHint());
+        mModel.set(HINT_TEXT, R.string.hub_search_empty_hint);
+        Assert.assertEquals(mActivity.getString(R.string.hub_search_empty_hint), mUrlBar.getHint());
+        mModel.set(HINT_TEXT, R.string.hub_search_empty_hint_incognito);
+        Assert.assertEquals(
+                mActivity.getString(R.string.hub_search_empty_hint_incognito), mUrlBar.getHint());
     }
 
     @Test
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java
index 81817ee..fbea20b 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java
@@ -45,6 +45,7 @@
 import org.chromium.components.content_settings.CookieControlsBridge;
 import org.chromium.components.content_settings.CookieControlsObserver;
 import org.chromium.components.metrics.OmniboxEventProtos.OmniboxEventProto.PageClassification;
+import org.chromium.components.omnibox.OmniboxFeatures;
 import org.chromium.components.page_info.PageInfoController;
 import org.chromium.components.permissions.PermissionDialogController;
 import org.chromium.components.search_engines.TemplateUrlService;
@@ -72,6 +73,7 @@
     private final Supplier<Profile> mProfileSupplier;
     private final @Nullable Supplier<MerchantTrustSignalsCoordinator>
             mMerchantTrustSignalsCoordinatorSupplier;
+    private final boolean mAlwaysShowDseIconOnNtp;
     private boolean mUrlHasFocus;
     private boolean mVerboseStatusSpaceAvailable;
     private boolean mPageIsPaintPreview;
@@ -167,6 +169,7 @@
 
         mIsTablet = isTablet;
         mShowStatusIconWhenUrlFocused = mIsTablet;
+        mAlwaysShowDseIconOnNtp = OmniboxFeatures.sOmniboxMobileParityUpdate.isEnabled();
 
         mPermissionDialogController = permissionDialogController;
         mPermissionDialogController.addObserver(this);
@@ -322,7 +325,7 @@
         if (!shouldShowLogo) return;
 
         if (mProfileSupplier.hasValue() && isNtpVisible()) {
-            setStatusIconShown(shouldShowLogo && (mUrlHasFocus || mUrlFocusPercent > 0));
+            setStatusIconShown(mAlwaysShowDseIconOnNtp || mUrlHasFocus || mUrlFocusPercent > 0);
         } else {
             setStatusIconShown(true);
         }
@@ -353,7 +356,7 @@
         updateStatusVisibility();
 
         // Only fade the animation on the new tab page.
-        if (mProfileSupplier.hasValue() && isNtpVisible()) {
+        if (mProfileSupplier.hasValue() && isNtpVisible() && !mAlwaysShowDseIconOnNtp) {
             setStatusIconAlpha(percent);
         } else {
             setStatusIconAlpha(1f);
@@ -459,7 +462,8 @@
     }
 
     private boolean isNtpVisible() {
-        return mLocationBarDataProvider.getNewTabPageDelegate().isCurrentlyVisible();
+        return mLocationBarDataProvider.getNewTabPageDelegate() != null
+                && mLocationBarDataProvider.getNewTabPageDelegate().isCurrentlyVisible();
     }
 
     /**
@@ -565,7 +569,7 @@
             return true;
         }
 
-        return (mUrlHasFocus || mUrlFocusPercent > 0)
+        return (mAlwaysShowDseIconOnNtp || mUrlHasFocus || mUrlFocusPercent > 0)
                 && isNtpVisible()
                 && mProfileSupplier.hasValue();
     }
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediatorUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediatorUnitTest.java
index 5160b5d..ceb9a6a02 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediatorUnitTest.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediatorUnitTest.java
@@ -39,6 +39,7 @@
 import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.base.supplier.OneshotSupplierImpl;
 import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.base.test.util.Features.DisableFeatures;
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
 import org.chromium.chrome.browser.merchant_viewer.MerchantTrustSignalsCoordinator;
 import org.chromium.chrome.browser.omnibox.LocationBarDataProvider;
@@ -57,6 +58,7 @@
 import org.chromium.components.content_settings.CookieControlsState;
 import org.chromium.components.feature_engagement.Tracker;
 import org.chromium.components.metrics.OmniboxEventProtos.OmniboxEventProto.PageClassification;
+import org.chromium.components.omnibox.OmniboxFeatureList;
 import org.chromium.components.permissions.PermissionDialogController;
 import org.chromium.components.prefs.PrefService;
 import org.chromium.components.search_engines.TemplateUrlService;
@@ -197,6 +199,7 @@
 
     @Test
     @SmallTest
+    @DisableFeatures(OmniboxFeatureList.OMNIBOX_MOBILE_PARITY_UPDATE)
     public void searchEngineLogo_isGoogleLogo_hideAfterUnfocusFinished() {
         doReturn(true).when(mNewTabPageDelegate).isCurrentlyVisible();
 
@@ -207,6 +210,26 @@
 
     @Test
     @SmallTest
+    public void searchEngineLogoPersistent() {
+        doReturn(true).when(mNewTabPageDelegate).isCurrentlyVisible();
+
+        mMediator.setUrlHasFocus(true);
+        mMediator.setUrlHasFocus(false);
+        Assert.assertTrue(mModel.get(StatusProperties.SHOW_STATUS_ICON));
+        Assert.assertTrue(mMediator.shouldDisplaySearchEngineIcon());
+
+        mMediator.setUrlFocusChangePercent(0.5f);
+        Assert.assertEquals(1f, mModel.get(StatusProperties.STATUS_ICON_ALPHA), 0f);
+
+        doReturn(false).when(mNewTabPageDelegate).isCurrentlyVisible();
+
+        mMediator.setUrlHasFocus(true);
+        mMediator.setUrlHasFocus(false);
+        Assert.assertFalse(mMediator.shouldDisplaySearchEngineIcon());
+    }
+
+    @Test
+    @SmallTest
     public void searchEngineLogo_isGoogleLogo_noHideIconAfterUnfocusedWhenScrolled() {
         mMediator.setUrlHasFocus(false);
         mMediator.setShowIconsWhenUrlFocused(true);
@@ -218,6 +241,7 @@
 
     @Test
     @SmallTest
+    @DisableFeatures(OmniboxFeatureList.OMNIBOX_MOBILE_PARITY_UPDATE)
     public void searchEngineLogo_isGoogleLogoOnNtp() {
         doReturn(true).when(mNewTabPageDelegate).isCurrentlyVisible();
 
@@ -229,6 +253,7 @@
 
     @Test
     @SmallTest
+    @DisableFeatures(OmniboxFeatureList.OMNIBOX_MOBILE_PARITY_UPDATE)
     public void searchEngineLogo_isGoogleLogoOnNtpTablet() {
         setupStatusMediator(/* isTablet= */ true);
         doReturn(true).when(mNewTabPageDelegate).isCurrentlyVisible();
@@ -366,6 +391,7 @@
 
     @Test
     @SmallTest
+    @DisableFeatures(OmniboxFeatureList.OMNIBOX_MOBILE_PARITY_UPDATE)
     public void searchEngineLogo_intermediateUrlFocusPercent() {
         doReturn(true).when(mNewTabPageDelegate).isCurrentlyVisible();
         mMediator.setUrlFocusChangePercent(0f);
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarPositionController.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarPositionController.java
index 1461e4b..f78def6 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarPositionController.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarPositionController.java
@@ -8,6 +8,7 @@
 import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
 import android.view.Gravity;
 import android.view.View;
+import android.view.ViewGroup;
 import android.widget.FrameLayout;
 
 import androidx.annotation.IntDef;
@@ -343,6 +344,8 @@
         int newTopHeight;
         int controlContainerHeight = mControlContainer.getToolbarHeight();
 
+        assert !((ViewGroup) mToolbarProgressBarContainer.getParent()).isInLayout()
+                : "Cannot change position during CoordinatorLayout layout pass";
         if (newControlsPosition == ControlsPosition.TOP) {
             newTopHeight = mBrowserControlsSizer.getTopControlsHeight() + controlContainerHeight;
             mLayerVisibility = LayerVisibility.HIDDEN;
@@ -353,6 +356,7 @@
             progressBarLayoutParams.setAnchorId(mControlContainer.getView().getId());
             progressBarLayoutParams.anchorGravity = Gravity.BOTTOM;
             progressBarLayoutParams.gravity = Gravity.TOP;
+            mToolbarProgressBarContainer.setLayoutParams(progressBarLayoutParams);
         } else {
             newTopHeight = mBrowserControlsSizer.getTopControlsHeight() - controlContainerHeight;
             mLayerVisibility = LayerVisibility.VISIBLE;
@@ -361,6 +365,7 @@
             progressBarLayoutParams.setAnchorId(View.NO_ID);
             progressBarLayoutParams.anchorGravity = Gravity.NO_GRAVITY;
             progressBarLayoutParams.gravity = Gravity.BOTTOM;
+            mToolbarProgressBarContainer.setLayoutParams(progressBarLayoutParams);
         }
 
         boolean animatingToTop = stateTransition == StateTransition.ANIMATE_TO_TOP;
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarPositionControllerTest.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarPositionControllerTest.java
index 149cb5a..eb95f90 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarPositionControllerTest.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarPositionControllerTest.java
@@ -17,6 +17,7 @@
 import android.content.pm.PackageManager;
 import android.view.Gravity;
 import android.view.View;
+import android.view.ViewGroup;
 import android.widget.FrameLayout;
 import android.widget.FrameLayout.LayoutParams;
 
@@ -234,6 +235,7 @@
     @Mock private ControlContainer mControlContainer;
     @Mock private View mControlContainerView;
     @Mock private View mProgressBarContainer;
+    @Mock private ViewGroup mProgressBarParent;
 
     private Context mContext;
     private final ObservableSupplierImpl<Boolean> mIsNtpShowing =
@@ -283,6 +285,7 @@
         doReturn(mControlContainerView).when(mControlContainer).getView();
         doReturn(CONTROL_CONTAINER_ID).when(mControlContainerView).getId();
         doReturn(mProgressBarLayoutParams).when(mProgressBarContainer).getLayoutParams();
+        doReturn(mProgressBarParent).when(mProgressBarContainer).getParent();
         mContext = ContextUtils.getApplicationContext();
         doReturn(mContext.getResources()).when(mProgressBarContainer).getResources();
         mWindowAndroid = new WindowAndroid(mContext, false);
@@ -404,9 +407,11 @@
     @EnableFeatures(ChromeFeatureList.ANDROID_BOTTOM_TOOLBAR + ":default_to_top/false")
     public void testDefaultBottom() {
         assertControlsAtBottom();
+        verify(mProgressBarContainer).setLayoutParams(mProgressBarLayoutParams);
 
         setUserToolbarAnchorPreference(/* showToolbarOnTop= */ true);
         assertControlsAtTop();
+        verify(mProgressBarContainer, times(2)).setLayoutParams(mProgressBarLayoutParams);
     }
 
     @Test
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarProgressBar.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarProgressBar.java
index a6da50c8..b3498fd0 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarProgressBar.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarProgressBar.java
@@ -178,6 +178,11 @@
         setProgressBarColors();
     }
 
+    @Override
+    protected boolean useGradientDrawable() {
+        return ChromeFeatureList.sAndroidProgressBarVisualUpdate.isEnabled();
+    }
+
     public void setAnimatingView(ToolbarProgressBarAnimatingView animatingView) {
         mAnimatingView = animatingView;
         setProgressBarColors();
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarProgressBarTest.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarProgressBarTest.java
index dc5f065..5971b59d 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarProgressBarTest.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarProgressBarTest.java
@@ -13,6 +13,9 @@
 import android.animation.Animator;
 import android.content.res.Resources;
 import android.graphics.Color;
+import android.graphics.drawable.ClipDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
@@ -373,4 +376,25 @@
                 SemanticColorUtils.getProgressBarTrackColor(mActivity),
                 mProgressBar.getBackgroundColor());
     }
+
+    @Test
+    @Features.DisableFeatures(ChromeFeatureList.ANDROID_PROGRESS_BAR_VISUAL_UPDATE)
+    @Feature({"Android-Progress-Bar"})
+    public void testProgressBar_staticBackground() {
+        assertFalse(mProgressBar.useGradientDrawable());
+        assertTrue(mProgressBar.getDrawable() instanceof ClipDrawable);
+    }
+
+    @Test
+    @Features.EnableFeatures(ChromeFeatureList.ANDROID_PROGRESS_BAR_VISUAL_UPDATE)
+    @Feature({"Android-Progress-Bar"})
+    public void testProgressBar_movableBackground() {
+        assertTrue(mProgressBar.useGradientDrawable());
+        Drawable drawable = mProgressBar.getDrawable();
+        assertTrue(drawable instanceof LayerDrawable);
+        LayerDrawable layerDrawable = (LayerDrawable) drawable;
+        assertEquals(2, layerDrawable.getNumberOfLayers());
+        assertTrue(layerDrawable.getDrawable(0) instanceof ClipDrawable);
+        assertTrue(layerDrawable.getDrawable(1) instanceof ClipDrawable);
+    }
 }
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
index d5ae108..ce05627 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
@@ -278,6 +278,8 @@
     // to webpages.
     private boolean mIsInLoadingPhaseFromNtpToWebpage;
 
+    private final boolean mAlwaysShowDseIconOnNtp;
+
     // The following are some properties used during animation.  We use explicit property classes
     // to avoid the cost of reflection for each animation setup.
 
@@ -315,6 +317,7 @@
                 ColorUtils.setAlphaComponentWithFloat(
                         SemanticColorUtils.getDefaultIconColorAccent1(context),
                         LocationBarBackgroundColorAlphaForNtp);
+        mAlwaysShowDseIconOnNtp = OmniboxFeatures.sOmniboxMobileParityUpdate.isEnabled();
     }
 
     @Override
@@ -1069,8 +1072,15 @@
                             isLocationBarRtl,
                             locationBarBaseTranslationX,
                             isUrlFocusChangeInProgressWithScrollCompleted));
+            // A url expansion fraction < 1.0 fades and translates the DSE icon away from its final
+            // state. If the DSE icon is always visible on the NTP, it should stay at full alpha and
+            // in its final location rather than being affected by scroll offset.
+            float ntpUrlExpansionFraction =
+                    mAlwaysShowDseIconOnNtp && isLocationBarShownInNtp
+                            ? 1.0f
+                            : mNtpSearchBoxScrollFraction;
             mLocationBar.setUrlFocusChangeFraction(
-                    mNtpSearchBoxScrollFraction, mUrlFocusChangeFraction);
+                    ntpUrlExpansionFraction, mUrlFocusChangeFraction);
 
             // Only transition theme colors if in static tab mode that is not the NTP or while
             // focusing on the NTP. In NTP, toolbar and locationbar need to transite color only when
@@ -1220,7 +1230,9 @@
         // Skip if in or entering tab switcher mode.
         if (mTabSwitcherState == TAB_SWITCHER || mTabSwitcherState == ENTERING_TAB_SWITCHER) return;
 
-        boolean isExpanded = mUrlExpansionFraction > 0f;
+        boolean isExpanded =
+                mUrlExpansionFraction > 0f
+                        || (mAlwaysShowDseIconOnNtp && isLocationBarShownInNtp());
         boolean isPartiallyExpanded = isExpanded && mUrlExpansionFraction < 1f;
         // We only need to avoid getting clipped in the period where the fakebox drawable belongs to
         // us but is taller than our bounds. It doesn't yet belong to us when the expansion fraction
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc
index fdc37f8..0deaca9 100644
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -943,8 +943,15 @@
   std::u16string title = base::UTF8ToUTF16(user_title_);
 
   if (title.empty()) {
-    title = FormatTitleForDisplay(
-        tab_strip_model_->GetWebContentsAt(index)->GetTitle());
+    title = tab_strip_model_->GetWebContentsAt(index)->GetTitle();
+    if (is_type_picture_in_picture()) {
+      content::WebContents* pip_web_contents =
+          PictureInPictureWindowManager::GetInstance()->GetWebContents();
+      if (pip_web_contents) {
+        title = pip_web_contents->GetTitle();
+      }
+    }
+    title = FormatTitleForDisplay(title);
   }
 
   if (title.empty() && (is_type_normal() || is_type_popup())) {
diff --git a/chrome/browser/ui/lens/BUILD.gn b/chrome/browser/ui/lens/BUILD.gn
index 95dfdc1..eee3690 100644
--- a/chrome/browser/ui/lens/BUILD.gn
+++ b/chrome/browser/ui/lens/BUILD.gn
@@ -129,6 +129,7 @@
     "//chrome/browser/ui/browser_window",
     "//chrome/browser/ui/color:color_headers",
     "//chrome/browser/ui/hats",
+    "//chrome/browser/ui/search",
     "//chrome/browser/ui/user_education",
     "//chrome/browser/ui/views/bubble",
     "//chrome/browser/ui/views/page_action",
diff --git a/chrome/browser/ui/lens/lens_overlay_controller.cc b/chrome/browser/ui/lens/lens_overlay_controller.cc
index 93e10f9..ce9ace1 100644
--- a/chrome/browser/ui/lens/lens_overlay_controller.cc
+++ b/chrome/browser/ui/lens/lens_overlay_controller.cc
@@ -2086,6 +2086,13 @@
 
   page_->ShouldShowContextualSearchBox(should_show_csb);
 
+  // If should show CSB, and the CSB viewport thumbnail is enabled, send it now.
+  if (should_show_csb &&
+      lens::features::GetVisualSelectionUpdatesEnableCsbThumbnail()) {
+    GetLensSearchboxController()->HandleThumbnailCreatedBitmap(
+        init_data.initial_rgb_screenshot_);
+  }
+
   // Send the initial document type to the overlay web UI.
   NotifyPageContentUpdated();
 
diff --git a/chrome/browser/ui/lens/lens_search_contextualization_controller.cc b/chrome/browser/ui/lens/lens_search_contextualization_controller.cc
index 4e54487..3c807881 100644
--- a/chrome/browser/ui/lens/lens_search_contextualization_controller.cc
+++ b/chrome/browser/ui/lens/lens_search_contextualization_controller.cc
@@ -15,6 +15,7 @@
 #include "chrome/browser/ui/lens/lens_session_metrics_logger.h"
 #include "components/lens/lens_features.h"
 #include "components/tabs/public/tab_interface.h"
+#include "chrome/browser/ui/lens/lens_searchbox_controller.h"
 #include "components/zoom/zoom_controller.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/render_widget_host_view.h"
@@ -642,6 +643,9 @@
       lens::MimeType::kUnknown, pdf_current_page, GetUiScaleFactor(),
       base::TimeTicks::Now());
 
+  // Pass the thumbnail to the searchbox controller.
+  GetSearchboxController()->HandleThumbnailCreatedBitmap(bitmap);
+
   state_ = State::kActive;
   TryUpdatePageContextualization(std::move(callback));
 }
@@ -758,4 +762,12 @@
   return query_controller;
 }
 
+lens::LensSearchboxController*
+LensSearchContextualizationController::GetSearchboxController() {
+  auto* searchbox_controller =
+      lens_search_controller_->lens_searchbox_controller();
+  CHECK(searchbox_controller);
+  return searchbox_controller;
+}
+
 }  // namespace lens
diff --git a/chrome/browser/ui/lens/lens_search_contextualization_controller.h b/chrome/browser/ui/lens/lens_search_contextualization_controller.h
index aeb9e63..0616c8b 100644
--- a/chrome/browser/ui/lens/lens_search_contextualization_controller.h
+++ b/chrome/browser/ui/lens/lens_search_contextualization_controller.h
@@ -37,6 +37,8 @@
 
 namespace lens {
 
+class LensSearchboxController;
+
 // Callback type alias for page content bytes retrieved. Multiple pieces and
 // types of content may be retrieved and returned in `page_contents`.
 // `primary_content_type` is the main type used in the request flow and used to
@@ -246,6 +248,7 @@
   float GetUiScaleFactor();
 
   lens::LensOverlayQueryController* GetQueryController();
+  lens::LensSearchboxController* GetSearchboxController();
 
   // The current state of the contextualization flow.
   State state_ = State::kOff;
diff --git a/chrome/browser/ui/lens/lens_searchbox_controller.cc b/chrome/browser/ui/lens/lens_searchbox_controller.cc
index 2fd6245..72e15c7 100644
--- a/chrome/browser/ui/lens/lens_searchbox_controller.cc
+++ b/chrome/browser/ui/lens/lens_searchbox_controller.cc
@@ -16,6 +16,7 @@
 #include "components/sessions/content/session_tab_helper.h"
 #include "components/sessions/core/session_id.h"
 #include "net/base/url_util.h"
+#include "skia/ext/codec_utils.h"
 #include "third_party/metrics_proto/omnibox_event.pb.h"
 #include "url/gurl.h"
 
@@ -80,15 +81,33 @@
 
 void LensSearchboxController::SetSearchboxThumbnail(
     const std::string& thumbnail_uri) {
+  // Init data can be empty if overlay is opened in a normal tab by navigating to the WebUI url in the omnibox.
+  if (!init_data_) {
+    return;
+  }
+
+  // Store the thumbnail.
+  init_data_->thumbnail_uri = thumbnail_uri;
+
   if (side_panel_searchbox_handler_ &&
       side_panel_searchbox_handler_->IsRemoteBound()) {
-    init_data_->thumbnail_uri = thumbnail_uri;
     side_panel_searchbox_handler_->SetThumbnail(thumbnail_uri);
-  } else {
-    // If the side panel was not bound at the time of request, we store the
-    // thumbnail as pending to send it to the searchbox on bind.
-    pending_thumbnail_uri_ = thumbnail_uri;
   }
+
+  if (overlay_searchbox_handler_ &&
+      overlay_searchbox_handler_->IsRemoteBound()) {
+    overlay_searchbox_handler_->SetThumbnail(thumbnail_uri);
+  }
+}
+
+void LensSearchboxController::HandleThumbnailCreatedBitmap(
+    const SkBitmap& thumbnail) {
+  if (thumbnail.drawsNothing()) {
+    return;
+  }
+  std::string image_png = skia::EncodePngAsDataUri(thumbnail.pixmap());
+  init_data_->thumbnail_uri = image_png;
+  SetSearchboxThumbnail(init_data_->thumbnail_uri);
 }
 
 void LensSearchboxController::HandleThumbnailCreated(
@@ -135,7 +154,6 @@
   side_panel_ghost_loader_page_.reset();
   init_data_ = std::make_unique<LensSearchboxInitializationData>();
   pending_text_query_ = std::nullopt;
-  pending_thumbnail_uri_ = std::nullopt;
   pending_suggest_inputs_callbacks_.Notify(std::nullopt);
 }
 
@@ -255,22 +273,15 @@
 }
 
 void LensSearchboxController::OnPageBound() {
-  // If the side panel closes before the remote gets bound,
-  // side_panel_searchbox_handler_ could become unset. Verify it is set before
-  // sending to the side panel.
-  if (!side_panel_searchbox_handler_ ||
-      !side_panel_searchbox_handler_->IsRemoteBound()) {
-    return;
-  }
-
   // Send any pending inputs for the searchbox.
-  if (pending_text_query_.has_value()) {
+  if (pending_text_query_.has_value() && side_panel_searchbox_handler_ &&
+      side_panel_searchbox_handler_->IsRemoteBound()) {
     side_panel_searchbox_handler_->SetInputText(*pending_text_query_);
     pending_text_query_.reset();
   }
-  if (pending_thumbnail_uri_.has_value()) {
-    SetSearchboxThumbnail(*pending_thumbnail_uri_);
-    pending_thumbnail_uri_.reset();
+  // If there is a thumbnail, make sure the searchbox receives it.
+  if (init_data_ && !init_data_->thumbnail_uri.empty()) {
+    SetSearchboxThumbnail(init_data_->thumbnail_uri);
   }
 }
 
diff --git a/chrome/browser/ui/lens/lens_searchbox_controller.h b/chrome/browser/ui/lens/lens_searchbox_controller.h
index 8cebb3c..f307957 100644
--- a/chrome/browser/ui/lens/lens_searchbox_controller.h
+++ b/chrome/browser/ui/lens/lens_searchbox_controller.h
@@ -82,6 +82,9 @@
   // `pending_thumbnail_uri_` instead.
   void SetSearchboxThumbnail(const std::string& thumbnail_uri);
 
+  // Handles the create of a new thumbnail from a bitmap.
+  void HandleThumbnailCreatedBitmap(const SkBitmap& thumbnail);
+
   // Handles the creation of a new thumbnail based on the user selection.
   void HandleThumbnailCreated(const std::string& thumbnail_bytes);
 
diff --git a/chrome/browser/ui/omnibox/BUILD.gn b/chrome/browser/ui/omnibox/BUILD.gn
index 3f1ac60..be345b7c 100644
--- a/chrome/browser/ui/omnibox/BUILD.gn
+++ b/chrome/browser/ui/omnibox/BUILD.gn
@@ -162,6 +162,7 @@
       "//base/test:test_support",
       "//chrome/browser/profiles:profile",
       "//chrome/browser/search_engines",
+      "//chrome/browser/ui/search",
       "//chrome/test:test_support",
       "//chrome/test:test_support_ui",
       "//components/infobars/core",
@@ -192,6 +193,7 @@
         "//base/test:test_support",
         "//chrome/browser/profiles:profile",
         "//chrome/browser/search_engines",
+        "//chrome/browser/ui/search",
         "//chrome/test:test_support",
         "//chrome/test:test_support_ui",
         "//components/omnibox/browser",
diff --git a/chrome/browser/ui/performance_controls/memory_saver_chip_tab_helper.cc b/chrome/browser/ui/performance_controls/memory_saver_chip_tab_helper.cc
index 59568041..8ccc0c2 100644
--- a/chrome/browser/ui/performance_controls/memory_saver_chip_tab_helper.cc
+++ b/chrome/browser/ui/performance_controls/memory_saver_chip_tab_helper.cc
@@ -10,7 +10,7 @@
 #include "chrome/browser/performance_manager/public/user_tuning/user_performance_tuning_manager.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser_finder.h"
-#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
 #include "chrome/browser/ui/page_action/page_action_icon_type.h"
 #include "chrome/browser/ui/performance_controls/memory_saver_chip_controller.h"
 #include "chrome/browser/ui/performance_controls/memory_saver_utils.h"
@@ -18,6 +18,7 @@
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/common/pref_names.h"
 #include "components/performance_manager/public/user_tuning/prefs.h"
+#include "components/tabs/public/tab_interface.h"
 #include "content/public/browser/visibility.h"
 #include "content/public/common/url_constants.h"
 
@@ -65,12 +66,11 @@
   return should_animate;
 }
 
-MemorySaverChipTabHelper::MemorySaverChipTabHelper(
-    content::WebContents* contents)
-    : content::WebContentsObserver(contents),
-      content::WebContentsUserData<MemorySaverChipTabHelper>(*contents) {
+MemorySaverChipTabHelper::MemorySaverChipTabHelper(tabs::TabInterface& tab)
+    : ContentsObservingTabFeature(tab) {
   pref_service_ =
-      Profile::FromBrowserContext(contents->GetBrowserContext())->GetPrefs();
+      Profile::FromBrowserContext(tab.GetBrowserWindowInterface()->GetProfile())
+          ->GetPrefs();
 
   if (UserPerformanceTuningManager::HasInstance()) {
     user_performance_tuning_manager_observation_.Observe(
@@ -85,7 +85,7 @@
 
 bool MemorySaverChipTabHelper::ComputeShouldHighlightMemorySavings() {
   bool const savings_over_threshold =
-      memory_saver::GetDiscardedMemorySavingsInBytes(&GetWebContents()) >
+      memory_saver::GetDiscardedMemorySavingsInBytes(web_contents()) >
       kExpandedMemorySaverChipThresholdBytes;
 
   base::Time const last_expanded_timestamp =
@@ -96,7 +96,7 @@
 
   auto* const pre_discard_resource_usage =
       UserPerformanceTuningManager::PreDiscardResourceUsage::FromWebContents(
-          &GetWebContents());
+          web_contents());
   bool const tab_discard_time_over_threshold =
       pre_discard_resource_usage &&
       (base::LiveTicks::Now() -
@@ -154,19 +154,7 @@
     return;
   }
 
-  // TODO(crbug.com/401033983): This code should only be running in a tab,
-  // so TabInterface::GetFromContents() should be reliable. However, some tests
-  // appear to end up with this tab helper failing to extract a TabInterface, so
-  // MaybeGetFromContents() is used as a test-only check. See
-  // crbug.com/417176824 for those tests. This should disappear when moving
-  // MemorySaver to run as a TabFeature.
-  tabs::TabInterface* tab_interface =
-      tabs::TabInterface::MaybeGetFromContents(web_contents());
-  if (tab_interface == nullptr) {
-    CHECK_IS_TEST();
-    return;
-  }
-  tabs::TabFeatures* tab_features = tab_interface->GetTabFeatures();
+  tabs::TabFeatures* tab_features = tab().GetTabFeatures();
   if (!tab_features) {
     // Tab features may not be present at shutdown.
     return;
@@ -187,7 +175,7 @@
 
     case memory_saver::ChipState::EXPANDED_WITH_SAVINGS:
       const int64_t bytes_saved =
-          memory_saver::GetDiscardedMemorySavingsInBytes(&GetWebContents());
+          memory_saver::GetDiscardedMemorySavingsInBytes(web_contents());
       controller->ShowMemorySavedChip(bytes_saved);
       break;
   }
diff --git a/chrome/browser/ui/performance_controls/memory_saver_chip_tab_helper.h b/chrome/browser/ui/performance_controls/memory_saver_chip_tab_helper.h
index 8a676a4..2645232 100644
--- a/chrome/browser/ui/performance_controls/memory_saver_chip_tab_helper.h
+++ b/chrome/browser/ui/performance_controls/memory_saver_chip_tab_helper.h
@@ -7,11 +7,11 @@
 
 #include "chrome/browser/performance_manager/public/user_tuning/user_performance_tuning_manager.h"
 #include "chrome/browser/resource_coordinator/lifecycle_unit_state.mojom-shared.h"
+#include "chrome/browser/ui/tabs/contents_observing_tab_feature.h"
 #include "components/prefs/pref_service.h"
+#include "components/tabs/public/tab_interface.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/render_widget_host.h"
-#include "content/public/browser/web_contents_observer.h"
-#include "content/public/browser/web_contents_user_data.h"
 
 namespace memory_saver {
 enum class ChipState {
@@ -33,15 +33,11 @@
 // user which conveys information about the discarded tab to the user.
 // The MemorySaverChipTabHelper is a per-tab class which manages the state of
 // the memory saver chip.
-class MemorySaverChipTabHelper
-    : public content::WebContentsObserver,
-      public content::WebContentsUserData<MemorySaverChipTabHelper>,
-      public performance_manager::user_tuning::UserPerformanceTuningManager::
-          Observer {
+class MemorySaverChipTabHelper : public tabs::ContentsObservingTabFeature,
+                                 public performance_manager::user_tuning::
+                                     UserPerformanceTuningManager::Observer {
  public:
-  MemorySaverChipTabHelper(const MemorySaverChipTabHelper&) = delete;
-  MemorySaverChipTabHelper& operator=(const MemorySaverChipTabHelper&) = delete;
-
+  explicit MemorySaverChipTabHelper(tabs::TabInterface& tab);
   ~MemorySaverChipTabHelper() override;
 
   static constexpr int kChipAnimationCount = 3;
@@ -62,9 +58,6 @@
   bool ShouldChipAnimate();
 
  private:
-  friend class content::WebContentsUserData<MemorySaverChipTabHelper>;
-  explicit MemorySaverChipTabHelper(content::WebContents* contents);
-
   // Threshold was selected based on the 75th percentile of tab memory usage
   static constexpr int64_t kExpandedMemorySaverChipThresholdBytes =
       197 * 1024 * 1024;
diff --git a/chrome/browser/ui/performance_controls/performance_controls_hats_service_unittest.cc b/chrome/browser/ui/performance_controls/performance_controls_hats_service_unittest.cc
index 1aa16d92..b1a51a8 100644
--- a/chrome/browser/ui/performance_controls/performance_controls_hats_service_unittest.cc
+++ b/chrome/browser/ui/performance_controls/performance_controls_hats_service_unittest.cc
@@ -54,11 +54,8 @@
 
 }  // namespace
 
-// TODO(agale): Remove base class and consolidate with PPM class.
-class PerformanceControlsHatsServiceTest : public testing::Test {
- public:
-  PerformanceControlsHatsServiceTest() = default;
-
+class PerformanceControlsHatsServicePPMTest : public testing::Test {
+ protected:
   void SetUp() override {
     testing::Test::SetUp();
 
@@ -73,13 +70,24 @@
     EXPECT_CALL(*mock_hats_service(), CanShowAnySurvey(_))
         .WillRepeatedly(testing::Return(true));
 
-    feature_list_.InitWithFeaturesAndParameters(GetFeatures(), {});
+    feature_list_.InitWithFeaturesAndParameters(
+        {
+            {performance_manager::features::kPerformanceControlsPPMSurvey,
+             GetFieldTrialParams()},
+        },
+        {});
     performance_manager::user_tuning::prefs::RegisterLocalStatePrefs(
         local_state_.registry());
     environment_.SetUp(&local_state_);
 
     performance_controls_hats_service_ =
         std::make_unique<PerformanceControlsHatsService>(profile);
+
+    // Override the random delay.
+    performance_controls_hats_service()->SetDelayBeforePPMSurveyForTesting(
+        (kPerformanceControlsPPMSurveyMinDelay.Get() +
+         kPerformanceControlsPPMSurveyMaxDelay.Get()) /
+        2);
   }
 
   void TearDown() override {
@@ -91,6 +99,8 @@
     environment_.TearDown();
   }
 
+  virtual base::FieldTrialParams GetFieldTrialParams() const { return {}; }
+
   void SetBatterySaverMode(
       const performance_manager::user_tuning::prefs::BatterySaverModeState
           battery_saver_mode) {
@@ -113,15 +123,9 @@
 
   content::BrowserTaskEnvironment& task_env() { return task_environment_; }
 
- protected:
+ private:
   performance_manager::user_tuning::TestUserPerformanceTuningManagerEnvironment
       environment_;
-
-  virtual const std::vector<base::test::FeatureRefAndParams> GetFeatures() {
-    return {};
-  }
-
- private:
   content::BrowserTaskEnvironment task_environment_{
       content::BrowserTaskEnvironment::TimeSource::MOCK_TIME};
   base::test::ScopedFeatureList feature_list_;
@@ -132,28 +136,6 @@
   raw_ptr<MockHatsService> mock_hats_service_;
 };
 
-class PerformanceControlsHatsServicePPMTest
-    : public PerformanceControlsHatsServiceTest {
- protected:
-  void SetUp() override {
-    PerformanceControlsHatsServiceTest::SetUp();
-    // Override the random delay.
-    performance_controls_hats_service()->SetDelayBeforePPMSurveyForTesting(
-        (kPerformanceControlsPPMSurveyMinDelay.Get() +
-         kPerformanceControlsPPMSurveyMaxDelay.Get()) /
-        2);
-  }
-
-  const std::vector<base::test::FeatureRefAndParams> GetFeatures() override {
-    return {
-        {performance_manager::features::kPerformanceControlsPPMSurvey,
-         GetFieldTrialParams()},
-    };
-  }
-
-  virtual base::FieldTrialParams GetFieldTrialParams() const { return {}; }
-};
-
 TEST_F(PerformanceControlsHatsServicePPMTest, NoPPMSurveyBeforeDelay) {
   EXPECT_CALL(
       *mock_hats_service(),
diff --git a/chrome/browser/ui/search/BUILD.gn b/chrome/browser/ui/search/BUILD.gn
new file mode 100644
index 0000000..53abab1
--- /dev/null
+++ b/chrome/browser/ui/search/BUILD.gn
@@ -0,0 +1,167 @@
+# Copyright 2024 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/chromeos/args.gni")
+
+assert(is_win || is_mac || is_linux || is_chromeos)
+
+source_set("search") {
+  sources = [
+    "instant_controller.h",
+    "new_tab_page_navigation_throttle.h",
+    "ntp_user_data_logger.h",
+    "ntp_user_data_types.h",
+    "omnibox_utils.h",
+    "search_ipc_router.h",
+    "search_ipc_router_policy_impl.h",
+    "search_tab_helper.h",
+  ]
+
+  public_deps = [
+    "//base",
+    "//chrome/browser/profiles:profile",
+    "//chrome/browser/search",
+    "//chrome/browser/ui/omnibox",
+    "//chrome/browser/ui/tabs:tab_strip_model_observer",
+    "//chrome/common",
+    "//chrome/common/search:mojo_bindings",
+    "//components/ntp_tiles",
+    "//components/omnibox/common",
+    "//content/public/browser",
+    "//mojo/public/cpp/bindings",
+  ]
+}
+
+source_set("impl") {
+  sources = [
+    "instant_controller.cc",
+    "new_tab_page_navigation_throttle.cc",
+    "search_ipc_router.cc",
+    "search_ipc_router_policy_impl.cc",
+
+    # The three files below cause circular dependencies against //c/b:b or //c/b/ui:ui.
+    "ntp_user_data_logger.cc",
+    "omnibox_utils.cc",
+    "search_tab_helper.cc",
+  ]
+  deps = [
+    ":search",
+    "//base",
+    "//chrome/browser/profiles:profile",
+    "//chrome/browser/search",
+    "//chrome/browser/search/background",
+    "//chrome/browser/search_engines",
+    "//chrome/browser/ui/bookmarks",
+    "//chrome/browser/ui/tab_contents",
+    "//chrome/browser/ui/tabs:tab_strip",
+    "//chrome/browser/ui/user_education",
+    "//chrome/common",
+    "//components/bookmarks/browser",
+    "//components/feature_engagement/public:feature_constants",
+    "//components/navigation_metrics",
+    "//components/profile_metrics",
+    "//components/search",
+    "//components/vector_icons",
+    "//content/public/browser",
+  ]
+  public_deps = [ "//chrome/browser:browser_public_dependencies" ]
+}
+
+source_set("browser_tests") {
+  testonly = true
+  defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
+  sources = [
+    "instant_test_base.cc",
+    "instant_test_base.h",
+    "new_tab_page_navigation_throttle_browsertest.cc",
+    "third_party_ntp_browsertest.cc",
+  ]
+
+  public_deps = [ "//net:test_support" ]
+  deps = [
+    "//base",
+    "//chrome/browser/profiles:profile",
+    "//chrome/browser/search",
+    "//chrome/browser/search_engines",
+    "//chrome/browser/ui/tab_contents",
+    "//chrome/browser/ui/tabs:tab_strip",
+    "//chrome/common",
+    "//chrome/common:constants",
+    "//chrome/test:test_support",
+    "//chrome/test:test_support_ui",
+    "//components/network_session_configurator/common",
+    "//components/search_engines",
+    "//content/public/browser",
+    "//content/test:test_support",
+    "//url",
+  ]
+}
+
+if (!is_chromeos_device) {
+  source_set("interactive_ui_tests") {
+    testonly = true
+    defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
+    sources = [
+      "instant_extended_interactive_uitest.cc",
+      "instant_test_base.cc",
+      "instant_test_base.h",
+      "third_party_ntp_uitest.cc",
+    ]
+
+    public_deps = [ "//net:test_support" ]
+
+    deps = [
+      "//base",
+      "//chrome/browser/profiles:profile",
+      "//chrome/browser/search",
+      "//chrome/browser/search_engines",
+      "//chrome/browser/ui/omnibox",
+      "//chrome/browser/ui/tabs:tab_strip",
+      "//chrome/common",
+      "//chrome/test:test_support",
+      "//chrome/test:test_support_ui",
+      "//components/network_session_configurator/common",
+      "//components/omnibox/browser",
+      "//components/search_engines",
+      "//content/public/browser",
+      "//content/test:test_support",
+      "//url",
+    ]
+  }
+}
+
+# TODO(crbug.com/418180294): Build ntp_test_utils.cc|h from this BUILD.gn
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "ntp_user_data_logger_unittest.cc",
+    "search_ipc_router_policy_unittest.cc",
+    "search_ipc_router_unittest.cc",
+  ]
+  deps = [
+    ":search",
+    "//base",
+    "//base/test:test_support",
+    "//chrome/browser/profiles:profile",
+    "//chrome/browser/search",
+    "//chrome/browser/search_engines",
+    "//chrome/browser/ui/tabs:tab_strip",
+    "//chrome/common",
+    "//chrome/common:non_code_constants",
+    "//chrome/test:test_support",
+    "//components/favicon_base",
+    "//components/ntp_tiles",
+    "//components/omnibox/common",
+    "//components/search",
+    "//components/search_engines",
+    "//content/public/browser",
+    "//content/test:test_support",
+    "//ipc",
+    "//ipc:message_support",
+    "//ipc:test_sink",
+    "//ui/base:types",
+    "//url",
+  ]
+}
diff --git a/chrome/browser/ui/shared_highlighting/BUILD.gn b/chrome/browser/ui/shared_highlighting/BUILD.gn
new file mode 100644
index 0000000..e82971c2
--- /dev/null
+++ b/chrome/browser/ui/shared_highlighting/BUILD.gn
@@ -0,0 +1,25 @@
+# Copyright 2025 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+assert(is_win || is_mac || is_linux || is_chromeos)
+
+source_set("shared_highlighting") {
+  sources = [
+    "shared_highlighting_promo.cc",
+    "shared_highlighting_promo.h",
+  ]
+
+  public_deps = [
+    "//base",
+    "//content/public/browser",
+    "//mojo/public/cpp/bindings",
+    "//third_party/blink/public/common",
+  ]
+
+  deps = [
+    "//chrome/browser/ui/user_education",
+    "//components/feature_engagement/public:feature_constants",
+    "//services/service_manager/public/cpp",
+  ]
+}
diff --git a/chrome/browser/ui/shared_highlighting/shared_highlighting_promo.cc b/chrome/browser/ui/shared_highlighting/shared_highlighting_promo.cc
index a07e6dd..624172f8d 100644
--- a/chrome/browser/ui/shared_highlighting/shared_highlighting_promo.cc
+++ b/chrome/browser/ui/shared_highlighting/shared_highlighting_promo.cc
@@ -6,8 +6,6 @@
 
 #include <memory>
 
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/user_education/browser_user_education_interface.h"
 #include "components/feature_engagement/public/feature_constants.h"
 #include "content/public/browser/render_frame_host.h"
diff --git a/chrome/browser/ui/tab_helpers.cc b/chrome/browser/ui/tab_helpers.cc
index dcb5019..3575eaca 100644
--- a/chrome/browser/ui/tab_helpers.cc
+++ b/chrome/browser/ui/tab_helpers.cc
@@ -86,7 +86,6 @@
 #include "chrome/browser/ui/find_bar/find_bar_state.h"
 #include "chrome/browser/ui/focus_tab_after_navigation_helper.h"
 #include "chrome/browser/ui/passwords/manage_passwords_ui_controller.h"
-#include "chrome/browser/ui/performance_controls/memory_saver_chip_tab_helper.h"
 #include "chrome/browser/ui/prefs/prefs_tab_helper.h"
 #include "chrome/browser/ui/privacy_sandbox/privacy_sandbox_prompt_helper.h"
 #include "chrome/browser/ui/recently_audible_helper.h"
@@ -657,7 +656,6 @@
   SadTabHelper::CreateForWebContents(web_contents);
   SearchTabHelper::CreateForWebContents(web_contents);
   TabDialogs::CreateForWebContents(web_contents);
-  MemorySaverChipTabHelper::CreateForWebContents(web_contents);
   if (base::FeatureList::IsEnabled(features::kTabHoverCardImages) ||
       base::FeatureList::IsEnabled(features::kWebUITabStrip)) {
     ThumbnailTabHelper::CreateForWebContents(web_contents);
diff --git a/chrome/browser/ui/tabs/public/tab_features.h b/chrome/browser/ui/tabs/public/tab_features.h
index b6cef5f..2bc5f30f 100644
--- a/chrome/browser/ui/tabs/public/tab_features.h
+++ b/chrome/browser/ui/tabs/public/tab_features.h
@@ -19,6 +19,7 @@
 class IntentPickerViewPageActionController;
 class LensOverlayController;
 class LensSearchController;
+class MemorySaverChipTabHelper;
 class PinnedTranslateActionListener;
 class Profile;
 class PwaInstallPageActionController;
@@ -237,6 +238,10 @@
     return resource_usage_helper_.get();
   }
 
+  MemorySaverChipTabHelper* memory_saver_chip_helper() {
+    return memory_saver_chip_helper_.get();
+  }
+
   // Note: Temporary until there is a more uniform way to swap out features for
   // testing.
   TabResourceUsageTabHelper* SetResourceUsageHelperForTesting(
@@ -385,6 +390,8 @@
 
   std::unique_ptr<TabResourceUsageTabHelper> resource_usage_helper_;
 
+  std::unique_ptr<MemorySaverChipTabHelper> memory_saver_chip_helper_;
+
   // Must be the last member.
   base::WeakPtrFactory<TabFeatures> weak_factory_{this};
 };
diff --git a/chrome/browser/ui/tabs/tab_features.cc b/chrome/browser/ui/tabs/tab_features.cc
index 94b553c..96785f03 100644
--- a/chrome/browser/ui/tabs/tab_features.cc
+++ b/chrome/browser/ui/tabs/tab_features.cc
@@ -38,6 +38,7 @@
 #include "chrome/browser/ui/lens/lens_search_controller.h"
 #include "chrome/browser/ui/page_action/page_action_icon_type.h"
 #include "chrome/browser/ui/performance_controls/memory_saver_chip_controller.h"
+#include "chrome/browser/ui/performance_controls/memory_saver_chip_tab_helper.h"
 #include "chrome/browser/ui/performance_controls/tab_resource_usage_tab_helper.h"
 #include "chrome/browser/ui/tabs/inactive_window_mouse_event_controller.h"
 #include "chrome/browser/ui/tabs/public/tab_dialog_manager.h"
@@ -334,6 +335,8 @@
 
   resource_usage_helper_ = std::make_unique<TabResourceUsageTabHelper>(tab);
 
+  memory_saver_chip_helper_ = std::make_unique<MemorySaverChipTabHelper>(tab);
+
   task_manager::WebContentsTags::CreateForTabContents(tab.GetContents());
 
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \
diff --git a/chrome/browser/ui/ui_features.cc b/chrome/browser/ui/ui_features.cc
index 7f08dbe..c908d06b 100644
--- a/chrome/browser/ui/ui_features.cc
+++ b/chrome/browser/ui/ui_features.cc
@@ -58,6 +58,9 @@
 BASE_FEATURE(kOfferPinToTaskbarWhenSettingToDefault,
              "OfferPinToTaskbarWhenSettingDefault",
              base::FEATURE_ENABLED_BY_DEFAULT);
+BASE_FEATURE(kOfferPinToTaskbarInFirstRunExperience,
+             "OfferPinToTaskbarInFirstRunExperience",
+             base::FEATURE_DISABLED_BY_DEFAULT);
 #endif
 
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
@@ -308,7 +311,7 @@
 
 BASE_FEATURE(kEnterpriseUpdatedProfileCreationScreen,
              "EnterpriseUpdatedProfileCreationScreen",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 BASE_FEATURE(kManagedProfileRequiredInterstitial,
              "ManagedProfileRequiredInterstitial",
diff --git a/chrome/browser/ui/ui_features.h b/chrome/browser/ui/ui_features.h
index bbf6d18..bed90da 100644
--- a/chrome/browser/ui/ui_features.h
+++ b/chrome/browser/ui/ui_features.h
@@ -43,6 +43,7 @@
 
 #if BUILDFLAG(IS_WIN)
 BASE_DECLARE_FEATURE(kOfferPinToTaskbarWhenSettingToDefault);
+BASE_DECLARE_FEATURE(kOfferPinToTaskbarInFirstRunExperience);
 #endif
 
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
diff --git a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_interactive_uitest.cc b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_interactive_uitest.cc
index 8dea7d6..81549ecf 100644
--- a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_interactive_uitest.cc
+++ b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_interactive_uitest.cc
@@ -62,6 +62,7 @@
 #include "ui/base/interaction/element_identifier.h"
 #include "ui/base/interaction/element_tracker.h"
 #include "ui/base/interaction/interaction_sequence.h"
+#include "ui/base/interaction/interactive_test.h"
 #include "ui/base/interaction/polling_state_observer.h"
 #include "ui/base/page_transition_types.h"
 #include "ui/base/test/ui_controls.h"
@@ -82,6 +83,10 @@
 #include "ui/views/widget/widget_utils.h"
 #include "url/url_constants.h"
 
+namespace {
+constexpr char kSkipPixelTestsReason[] = "Should only run in pixel_tests.";
+}  // anonymous namespace
+
 namespace tab_groups {
 
 DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kFirstTab);
@@ -175,6 +180,11 @@
         WaitForState(kTabCountState, expected_count),
         StopObservingState(kTabCountState));
   }
+
+  MultiStep OpenTabGroupEditorMenu(tab_groups::TabGroupId group_id) {
+    return Steps(HoverTabGroupHeader(group_id), ClickMouse(ui_controls::RIGHT),
+                 WaitForShow(kTabGroupEditorBubbleId));
+  }
 };
 
 class SavedTabGroupInteractiveTest
@@ -209,11 +219,6 @@
                  MoveMouseTo(kTabToHover));
   }
 
-  MultiStep OpenTabGroupEditorMenu(tab_groups::TabGroupId group_id) {
-    return Steps(HoverTabGroupHeader(group_id), ClickMouse(ui_controls::RIGHT),
-                 WaitForShow(kTabGroupEditorBubbleId));
-  }
-
   MultiStep WaitToFetchFavicon(int tab_index, const GURL& favicon_url) {
     DEFINE_LOCAL_STATE_IDENTIFIER_VALUE(FaviconFetchObserver,
                                         kFaviconFetchObserver);
@@ -1174,6 +1179,57 @@
 };
 
 IN_PROC_BROWSER_TEST_F(TabGroupShortcutsInteractiveTest,
+                       ScreenshotAcceleratorsInTabGroupEditorBubble) {
+  // Add 1 tab into the browser. And verify there are 2 tabs (The tab when you
+  // open the browser and the added one).
+  ASSERT_TRUE(
+      AddTabAtIndex(0, GURL(url::kAboutBlankURL), ui::PAGE_TRANSITION_TYPED));
+  ASSERT_EQ(2, browser()->tab_strip_model()->count());
+
+  const TabGroupId group_id = browser()->tab_strip_model()->AddToNewGroup({0});
+
+  RunTestSequence(
+      FinishTabstripAnimations(), OpenTabGroupEditorMenu(group_id),
+      SetOnIncompatibleAction(OnIncompatibleAction::kSkipTest,
+                              kSkipPixelTestsReason),
+      // Verify the tab group editor bubble has the accelerators for New tab in
+      // group and Close group.
+      Screenshot(kTabGroupEditorBubbleId,
+                 "tab_group_editor_bubble_with_accelerators", "6564307"),
+      // Close the tab group editor bubble to prevent flakes on mac.
+      HoverTabAt(0), ClickMouse(), WaitForHide(kTabGroupEditorBubbleId));
+}
+
+IN_PROC_BROWSER_TEST_F(TabGroupShortcutsInteractiveTest,
+                       ScreenshotAcceleratorsInTabGroupSubmenu) {
+  // Add 1 tab into the browser. And verify there are 2 tabs (The tab when you
+  // open the browser and the added one).
+  ASSERT_TRUE(
+      AddTabAtIndex(0, GURL(url::kAboutBlankURL), ui::PAGE_TRANSITION_TYPED));
+  ASSERT_EQ(2, browser()->tab_strip_model()->count());
+
+  browser()->tab_strip_model()->AddToNewGroup({0});
+
+  const char kEverythingMenuRootViewId[] = "EverythingMenuRootView";
+
+  RunTestSequence(
+      FinishTabstripAnimations(), PressButton(kToolbarAppMenuButtonElementId),
+      SelectMenuItem(AppMenuModel::kTabGroupsMenuItem),
+      NameViewRelative(
+          AppMenuModel::kTabGroupsMenuItem, kEverythingMenuRootViewId,
+          [](views::MenuItemView* item) { return item->GetSubmenu(); }),
+      WaitForShow(kEverythingMenuRootViewId),
+      SetOnIncompatibleAction(OnIncompatibleAction::kSkipTest,
+                              kSkipPixelTestsReason),
+      // Verify the app menu has the accelerator for Create new tab group.
+      Screenshot(kEverythingMenuRootViewId,
+                 "tab_group_app_menu_with_accelerators", "6564307"),
+      // Close the app menu to prevent flakes on mac.
+      HoverTabAt(0), ClickMouse(),
+      WaitForHide(AppMenuModel::kTabGroupsMenuItem));
+}
+
+IN_PROC_BROWSER_TEST_F(TabGroupShortcutsInteractiveTest,
                        NewTabAddedToEndOfActiveTabsGroupWithKeyboardShortcut) {
   ui::Accelerator create_accelerator;
   ASSERT_TRUE(BrowserView::GetBrowserViewForBrowser(browser())->GetAccelerator(
diff --git a/chrome/browser/ui/views/bookmarks/saved_tab_groups/shared_tab_group_interactive_uitest.cc b/chrome/browser/ui/views/bookmarks/saved_tab_groups/shared_tab_group_interactive_uitest.cc
index d293c76..18da2e2 100644
--- a/chrome/browser/ui/views/bookmarks/saved_tab_groups/shared_tab_group_interactive_uitest.cc
+++ b/chrome/browser/ui/views/bookmarks/saved_tab_groups/shared_tab_group_interactive_uitest.cc
@@ -9,6 +9,7 @@
 #include "chrome/browser/tab_group_sync/tab_group_sync_service_factory.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_element_identifiers.h"
+#include "chrome/browser/ui/tabs/features.h"
 #include "chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_metrics.h"
 #include "chrome/browser/ui/tabs/tab_group_model.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
@@ -62,7 +63,7 @@
     scoped_feature_list_.InitWithFeatures(
         {tab_groups::kTabGroupSyncServiceDesktopMigration,
          data_sharing::features::kDataSharingFeature},
-        {});
+        {tabs::kTabGroupShortcuts});
     InProcessBrowserTest::SetUp();
   }
 
diff --git a/chrome/browser/ui/views/new_tab_footer/BUILD.gn b/chrome/browser/ui/views/new_tab_footer/BUILD.gn
index d40c739..7cbb750 100644
--- a/chrome/browser/ui/views/new_tab_footer/BUILD.gn
+++ b/chrome/browser/ui/views/new_tab_footer/BUILD.gn
@@ -28,3 +28,14 @@
     "//ui/views/controls/webview",
   ]
 }
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [ "footer_controller_unittest.cc" ]
+  deps = [
+    ":new_tab_footer",
+    "//base/test:test_support",
+    "//chrome/browser/ui:ui_features",
+    "//chrome/test:test_support",
+  ]
+}
diff --git a/chrome/browser/ui/views/new_tab_footer/footer_controller.cc b/chrome/browser/ui/views/new_tab_footer/footer_controller.cc
index 8ae1c724..111c192 100644
--- a/chrome/browser/ui/views/new_tab_footer/footer_controller.cc
+++ b/chrome/browser/ui/views/new_tab_footer/footer_controller.cc
@@ -8,6 +8,8 @@
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/views/new_tab_footer/footer_web_view.h"
 #include "chrome/browser/ui/webui/new_tab_footer/new_tab_footer_helper.h"
+#include "chrome/common/pref_names.h"
+#include "components/prefs/pref_service.h"
 #include "content/public/browser/navigation_entry.h"
 
 namespace new_tab_footer {
@@ -19,6 +21,14 @@
   if (features::IsNtpFooterEnabledWithoutSideBySide()) {
     footer_web_view_ = tab_->GetBrowserWindowInterface()->NewTabFooterWebView();
   }
+
+  profile_ = tab_->GetBrowserWindowInterface()->GetProfile();
+  pref_change_registrar_.Init(profile_->GetPrefs());
+  pref_change_registrar_.Add(
+      prefs::kNtpFooterVisible,
+      base::BindRepeating(&NewTabFooterController::UpdateFooterVisibility,
+                          weak_factory_.GetWeakPtr()));
+
   content::WebContentsObserver::Observe(tab_->GetContents());
   tab_did_activate_callback_subscription_ = tab_->RegisterDidActivate(
       base::BindRepeating(&NewTabFooterController::TabForegrounded,
@@ -45,8 +55,11 @@
     url = tab_->GetContents()->GetController().GetVisibleEntry()->GetURL();
   }
 
-  if (ntp_footer::IsExtensionNtp(
-          url, tab_->GetBrowserWindowInterface()->GetProfile())) {
+  bool is_footer_visible_pref =
+      profile_->GetPrefs()->GetBoolean(prefs::kNtpFooterVisible);
+  bool can_show_footer =
+      is_footer_visible_pref && ntp_footer::IsExtensionNtp(url, profile_);
+  if (can_show_footer) {
     ShowUI();
   } else {
     CloseUI();
diff --git a/chrome/browser/ui/views/new_tab_footer/footer_controller.h b/chrome/browser/ui/views/new_tab_footer/footer_controller.h
index 755a3df7..ce32123 100644
--- a/chrome/browser/ui/views/new_tab_footer/footer_controller.h
+++ b/chrome/browser/ui/views/new_tab_footer/footer_controller.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_UI_VIEWS_NEW_TAB_FOOTER_FOOTER_CONTROLLER_H_
 
 #include "chrome/browser/ui/views/new_tab_footer/footer_web_view.h"
+#include "components/prefs/pref_change_registrar.h"
 #include "components/tabs/public/tab_interface.h"
 #include "content/public/browser/web_contents_observer.h"
 
@@ -33,6 +34,8 @@
   const raw_ptr<tabs::TabInterface> tab_;
   raw_ptr<new_tab_footer::NewTabFooterWebView> footer_web_view_;
   base::CallbackListSubscription tab_did_activate_callback_subscription_;
+  PrefChangeRegistrar pref_change_registrar_;
+  raw_ptr<Profile> profile_;
 
   base::WeakPtrFactory<NewTabFooterController> weak_factory_{this};
 };
diff --git a/chrome/browser/ui/views/new_tab_footer/footer_controller_unittest.cc b/chrome/browser/ui/views/new_tab_footer/footer_controller_unittest.cc
new file mode 100644
index 0000000..8e90ca5
--- /dev/null
+++ b/chrome/browser/ui/views/new_tab_footer/footer_controller_unittest.cc
@@ -0,0 +1,235 @@
+// 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/views/new_tab_footer/footer_controller.h"
+
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/extensions/chrome_test_extension_loader.h"
+#include "chrome/browser/extensions/extension_service_test_base.h"
+#include "chrome/browser/extensions/extension_web_ui.h"
+#include "chrome/browser/ui/browser_window/test/mock_browser_window_interface.h"
+#include "chrome/browser/ui/tabs/test/mock_tab_interface.h"
+#include "chrome/browser/ui/ui_features.h"
+#include "chrome/browser/ui/views/new_tab_footer/footer_web_view.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/search/ntp_features.h"
+#include "content/public/test/web_contents_tester.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/common/extension_builder.h"
+#include "extensions/test/test_extension_dir.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+const char kNonExtensionNtpUrl[] = "https://www.google.com";
+}
+
+class FakeNewTabFooterWebView : public new_tab_footer::NewTabFooterWebView {
+ public:
+  explicit FakeNewTabFooterWebView(BrowserWindowInterface* browser_window)
+      : NewTabFooterWebView(browser_window) {}
+  ~FakeNewTabFooterWebView() override {}
+
+  bool did_show_ui() { return did_show_ui_; }
+  bool did_close_ui() { return did_close_ui_; }
+
+  void Clear() {
+    did_show_ui_ = false;
+    did_close_ui_ = false;
+  }
+
+ protected:
+  // WebUIContentsWrapper::Host:
+  void ShowUI() override { did_show_ui_ = true; }
+  void CloseUI() override { did_close_ui_ = true; }
+
+  bool did_show_ui_ = false;
+  bool did_close_ui_ = false;
+};
+
+class FakeBrowserWindowInterface : public MockBrowserWindowInterface {
+ public:
+  explicit FakeBrowserWindowInterface(Profile* profile) { profile_ = profile; }
+  ~FakeBrowserWindowInterface() override = default;
+
+  // MockBrowserWindowInterface:
+  FakeNewTabFooterWebView* NewTabFooterWebView() override {
+    return footer_web_view_;
+  }
+  Profile* GetProfile() override { return profile_.get(); }
+
+  void SetFooterWebView(FakeNewTabFooterWebView* footer_web_view) {
+    footer_web_view_ = footer_web_view;
+  }
+
+ protected:
+  raw_ptr<Profile> profile_;
+  raw_ptr<FakeNewTabFooterWebView> footer_web_view_;
+};
+
+class FakeTabInterface : public tabs::MockTabInterface {
+ public:
+  FakeTabInterface(FakeBrowserWindowInterface* browser_window,
+                   content::BrowserContext* browser_context) {
+    browser_window_ = browser_window;
+    web_contents_ = content::WebContentsTester::CreateTestWebContents(
+        browser_context, nullptr);
+    footer_web_view_ =
+        std::make_unique<FakeNewTabFooterWebView>(browser_window);
+    browser_window_->SetFooterWebView(footer_web_view_.get());
+  }
+  ~FakeTabInterface() override {
+    browser_window_->SetFooterWebView(nullptr);
+    footer_web_view_.reset();
+  }
+
+  // tabs::MockTabInterface:
+  FakeBrowserWindowInterface* GetBrowserWindowInterface() override {
+    return browser_window_;
+  }
+  content::WebContents* GetContents() const override {
+    return web_contents_.get();
+  }
+  bool IsActivated() const override { return true; }
+
+  void NavigateTo(const GURL url) {
+    EXPECT_TRUE(web_contents_.get());
+    content::WebContentsTester* web_contents_tester =
+        content::WebContentsTester::For(web_contents_.get());
+    web_contents_tester->NavigateAndCommit(url);
+  }
+
+ protected:
+  std::unique_ptr<content::WebContents> web_contents_;
+  std::unique_ptr<FakeNewTabFooterWebView> footer_web_view_;
+  raw_ptr<FakeBrowserWindowInterface> browser_window_;
+};
+
+class FooterControllerExtensionTest
+    : public extensions::ExtensionServiceTestBase {
+ public:
+  void SetUp() override {
+    feature_list_.InitWithFeatures(
+        /*enabled_features=*/{ntp_features::kNtpFooter},
+        /*disabled_features=*/{features::kSideBySide});
+    ExtensionServiceTestBase::SetUp();
+    InitializeEmptyExtensionService();
+
+    browser_window_ = std::make_unique<FakeBrowserWindowInterface>(profile());
+    tab_ = std::make_unique<FakeTabInterface>(browser_window_.get(),
+                                              browser_context());
+    controller_ =
+        std::make_unique<new_tab_footer::NewTabFooterController>(tab_.get());
+  }
+
+  void TearDown() override {
+    controller_.reset();
+    tab_.reset();
+    browser_window_.reset();
+    ExtensionServiceTestBase::TearDown();
+  }
+
+  scoped_refptr<const extensions::Extension> LoadNtpExtension() {
+    extensions::TestExtensionDir extension_dir;
+    const std::string kManifest = R"(
+      {
+        "chrome_url_overrides": {
+            "newtab": "ext.html"
+        },
+        "name": "Extension-overridden NTP",
+          "manifest_version": 3,
+          "version": "0.1"
+      })";
+    extension_dir.WriteManifest(kManifest);
+    extension_dir.WriteFile(FILE_PATH_LITERAL("ext.html"),
+                            "<body>Extension-overridden NTP</body>");
+    extensions::ChromeTestExtensionLoader extension_loader(profile());
+    scoped_refptr<const extensions::Extension> extension =
+        extension_loader.LoadExtension(extension_dir.Pack());
+    return extension;
+  }
+
+  FakeBrowserWindowInterface* browser_window() { return browser_window_.get(); }
+
+  FakeTabInterface* tab_interface() { return tab_.get(); }
+
+ protected:
+  base::test::ScopedFeatureList feature_list_;
+  std::unique_ptr<FakeBrowserWindowInterface> browser_window_;
+  std::unique_ptr<FakeTabInterface> tab_;
+  std::unique_ptr<new_tab_footer::NewTabFooterController> controller_;
+};
+
+TEST_F(FooterControllerExtensionTest, FooterShown_ExtensionNTP) {
+  profile()->GetPrefs()->SetBoolean(prefs::kNtpFooterVisible, true);
+  auto extension = LoadNtpExtension();
+  ASSERT_TRUE(extension);
+  // Force activation of the URL override. The usual observer for
+  // extension load isn't created in the unit test.
+  ExtensionWebUI::RegisterOrActivateChromeURLOverrides(
+      profile_.get(),
+      extensions::URLOverrides::GetChromeURLOverrides(extension.get()));
+  EXPECT_FALSE(browser_window()->NewTabFooterWebView()->did_show_ui());
+  tab_interface()->NavigateTo(extension->url());
+
+  EXPECT_TRUE(browser_window()->NewTabFooterWebView()->did_show_ui());
+}
+
+TEST_F(FooterControllerExtensionTest, FooterHidden_NonExtensionNTP) {
+  profile()->GetPrefs()->SetBoolean(prefs::kNtpFooterVisible, true);
+  // After a pref change, there's an attempt to show the footer.
+  EXPECT_TRUE(browser_window()->NewTabFooterWebView()->did_close_ui());
+  // Clear the value of `FakeNewTabFooterWebView::did_close_ui()` to
+  // check that the navigation also results in a hidden footer.
+  browser_window()->NewTabFooterWebView()->Clear();
+
+  EXPECT_FALSE(browser_window()->NewTabFooterWebView()->did_close_ui());
+  tab_interface()->NavigateTo(GURL(kNonExtensionNtpUrl));
+
+  EXPECT_TRUE(browser_window()->NewTabFooterWebView()->did_close_ui());
+}
+
+// Ensures footer is shown on extension NTPs when
+// `prefs::kNtpFooterVisible` is set to true.
+TEST_F(FooterControllerExtensionTest, FooterShown_UserPref) {
+  profile()->GetPrefs()->SetBoolean(prefs::kNtpFooterVisible, false);
+  auto extension = LoadNtpExtension();
+  ASSERT_TRUE(extension);
+  // Force activation of the URL override. The usual observer for
+  // extension load isn't created in the unit test.
+  ExtensionWebUI::RegisterOrActivateChromeURLOverrides(
+      profile_.get(),
+      extensions::URLOverrides::GetChromeURLOverrides(extension.get()));
+
+  tab_interface()->NavigateTo(extension->url());
+  EXPECT_FALSE(browser_window()->NewTabFooterWebView()->did_show_ui());
+
+  profile()->GetPrefs()->SetBoolean(prefs::kNtpFooterVisible, true);
+  EXPECT_TRUE(browser_window()->NewTabFooterWebView()->did_show_ui());
+}
+
+// Ensures footer is hidden on extension NTPs when
+// `prefs::kNtpFooterVisible` is set to false.
+TEST_F(FooterControllerExtensionTest, FooterHidden_UserPref) {
+  profile()->GetPrefs()->SetBoolean(prefs::kNtpFooterVisible, true);
+  // After a pref change, there's an attempt to show the footer.
+  EXPECT_TRUE(browser_window()->NewTabFooterWebView()->did_close_ui());
+  // Clear the value of `FakeNewTabFooterWebView::did_close_ui()` to check
+  // the effect of pref changes on an extension NTP.
+  browser_window()->NewTabFooterWebView()->Clear();
+  EXPECT_FALSE(browser_window()->NewTabFooterWebView()->did_close_ui());
+
+  auto extension = LoadNtpExtension();
+  ASSERT_TRUE(extension);
+  // Force activation of the URL override. The usual observer for
+  // extension load isn't created in the unit test.
+  ExtensionWebUI::RegisterOrActivateChromeURLOverrides(
+      profile_.get(),
+      extensions::URLOverrides::GetChromeURLOverrides(extension.get()));
+
+  tab_interface()->NavigateTo(extension->url());
+  EXPECT_FALSE(browser_window()->NewTabFooterWebView()->did_close_ui());
+
+  profile()->GetPrefs()->SetBoolean(prefs::kNtpFooterVisible, false);
+  EXPECT_TRUE(browser_window()->NewTabFooterWebView()->did_close_ui());
+}
diff --git a/chrome/browser/ui/views/passwords/password_bubble_interactive_uitest.cc b/chrome/browser/ui/views/passwords/password_bubble_interactive_uitest.cc
index 30f4fb7..872ee55 100644
--- a/chrome/browser/ui/views/passwords/password_bubble_interactive_uitest.cc
+++ b/chrome/browser/ui/views/passwords/password_bubble_interactive_uitest.cc
@@ -1351,7 +1351,20 @@
                   EnsureNotPresent(SharedPasswordsNotificationView::kTopView));
 }
 
-IN_PROC_BROWSER_TEST_F(PasswordBubbleInteractiveUiTest, ClickNever) {
+class TwoButtonPasswordBubbleInteractiveUiTest
+    : public PasswordBubbleInteractiveUiTest {
+ public:
+  TwoButtonPasswordBubbleInteractiveUiTest() {
+    scoped_feature_list_.InitWithFeatureState(
+        features::kThreeButtonPasswordSaveDialog, false);
+  }
+  ~TwoButtonPasswordBubbleInteractiveUiTest() override = default;
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(TwoButtonPasswordBubbleInteractiveUiTest, ClickNever) {
   SetupPendingPassword();
   const auto button = views::DialogClientView::kCancelButtonElementId;
   RunTestSequence(PressButton(button), WaitForHide(button),
@@ -1359,6 +1372,7 @@
                       "PasswordManager.SaveUIDismissalReason",
                       password_manager::metrics_util::CLICKED_NEVER, 1));
 }
+
 class ThreeButtonPasswordBubbleInteractiveUiTest
     : public PasswordBubbleInteractiveUiTest {
  public:
diff --git a/chrome/browser/ui/views/passwords/password_save_update_view.cc b/chrome/browser/ui/views/passwords/password_save_update_view.cc
index ad68ce2..ced47c5c 100644
--- a/chrome/browser/ui/views/passwords/password_save_update_view.cc
+++ b/chrome/browser/ui/views/passwords/password_save_update_view.cc
@@ -137,13 +137,6 @@
     accessibility_alert_ =
         root_view->AddChildView(std::make_unique<views::View>());
     AddChildViewRaw(accessibility_alert_.get());
-
-    if (base::FeatureList::IsEnabled(
-            features::kThreeButtonPasswordSaveDialog)) {
-      extra_view_ = SetExtraView(std::make_unique<views::MdTextButton>());
-      extra_view_->SetProperty(views::kElementIdentifierKey,
-                               kExtraButtonElementId);
-    }
   }
 
   {
@@ -163,13 +156,17 @@
                 base::Unretained(this))));
 
     if (is_update_bubble_) {
-      CHECK(!extra_view_);
       SetCancelCallback(base::BindOnce(button_clicked, base::Unretained(this),
                                        &Controller::OnNoThanksClicked));
-    } else if (extra_view_) {
+    } else if (base::FeatureList::IsEnabled(
+                   features::kThreeButtonPasswordSaveDialog)) {
       // 3-button save dialog variant.
       SetCancelCallback(base::BindOnce(button_clicked, base::Unretained(this),
                                        &Controller::OnNotNowClicked));
+
+      extra_view_ = SetExtraView(std::make_unique<views::MdTextButton>());
+      extra_view_->SetProperty(views::kElementIdentifierKey,
+                               kExtraButtonElementId);
       extra_view_->SetCallback(
           base::BindOnce(button_clicked, base::Unretained(this),
                          &Controller::OnNeverForThisSiteClicked));
diff --git a/chrome/browser/ui/views/performance_controls/memory_saver_chip_view.cc b/chrome/browser/ui/views/performance_controls/memory_saver_chip_view.cc
index 36f57f7..c4f29fd 100644
--- a/chrome/browser/ui/views/performance_controls/memory_saver_chip_view.cc
+++ b/chrome/browser/ui/views/performance_controls/memory_saver_chip_view.cc
@@ -23,6 +23,7 @@
 #include "chrome/browser/ui/views/page_action/page_action_icon_view.h"
 #include "chrome/browser/ui/views/performance_controls/memory_saver_bubble_view.h"
 #include "chrome/grit/generated_resources.h"
+#include "components/tabs/public/tab_interface.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/base/text/bytes_formatting.h"
@@ -73,8 +74,13 @@
     return;
   }
 
+  auto* const tab = tabs::TabInterface::GetFromContents(web_contents);
+  if (!tab) {
+    return;
+  }
   MemorySaverChipTabHelper* const tab_helper =
-      MemorySaverChipTabHelper::FromWebContents(web_contents);
+      tab->GetTabFeatures()->memory_saver_chip_helper();
+  CHECK(tab_helper);
   auto chip_state = tab_helper->chip_state();
 
   if (chip_state != memory_saver::ChipState::HIDDEN) {
diff --git a/chrome/browser/ui/views/performance_controls/memory_saver_chip_view_unittest.cc b/chrome/browser/ui/views/performance_controls/memory_saver_chip_view_unittest.cc
index 9f555d5..5f9e8d1 100644
--- a/chrome/browser/ui/views/performance_controls/memory_saver_chip_view_unittest.cc
+++ b/chrome/browser/ui/views/performance_controls/memory_saver_chip_view_unittest.cc
@@ -304,10 +304,11 @@
   SetTabDiscardState(0, true);
 
   // Ensure that the expanded-with-savings chip was shown.
-  content::WebContents* web_contents =
-      browser()->tab_strip_model()->GetWebContentsAt(0);
-  MemorySaverChipTabHelper* const tab_helper =
-      MemorySaverChipTabHelper::FromWebContents(web_contents);
+  MemorySaverChipTabHelper* const tab_helper = browser()
+                                                   ->tab_strip_model()
+                                                   ->GetTabAtIndex(0)
+                                                   ->GetTabFeatures()
+                                                   ->memory_saver_chip_helper();
   EXPECT_EQ(tab_helper->chip_state(),
             memory_saver::ChipState::EXPANDED_WITH_SAVINGS);
   EXPECT_TRUE(GetPageActionView()->GetVisible());
diff --git a/chrome/browser/ui/views/performance_controls/performance_intervention_interactive_ui_test.cc b/chrome/browser/ui/views/performance_controls/performance_intervention_interactive_ui_test.cc
index a899a459..f255eff 100644
--- a/chrome/browser/ui/views/performance_controls/performance_intervention_interactive_ui_test.cc
+++ b/chrome/browser/ui/views/performance_controls/performance_intervention_interactive_ui_test.cc
@@ -442,17 +442,18 @@
 IN_PROC_BROWSER_TEST_F(PerformanceInterventionInteractiveTest,
                        TakeSuggestedAction) {
   RunTestSequence(
+      InstrumentTab(kFirstTab), NavigateWebContents(kFirstTab, GetURL()),
       AddInstrumentedTab(kSecondTab, GetURL()),
-      AddInstrumentedTab(kThirdTab, GetURL()), SelectTab(kTabStripElementId, 0),
-      TriggerOnActionableTabListChange({1, 2}),
+      AddInstrumentedTab(kThirdTab, GetURL()), WaitForShow(kThirdTab),
+      TriggerOnActionableTabListChange({0, 1}),
       WaitForShow(kToolbarPerformanceInterventionButtonElementId),
       WaitForShow(PerformanceInterventionBubble::
                       kPerformanceInterventionDialogDeactivateButton),
       ObserveState(kTabDiscardedState, 2),
       PressButton(PerformanceInterventionBubble::
                       kPerformanceInterventionDialogDeactivateButton),
-      WaitForState(kTabDiscardedState, true), CheckTabDiscardStatus(0, false),
-      CheckTabDiscardStatus(1, true), CheckTabDiscardStatus(2, true));
+      WaitForState(kTabDiscardedState, true), CheckTabDiscardStatus(0, true),
+      CheckTabDiscardStatus(1, true), CheckTabDiscardStatus(2, false));
 }
 
 // The dialog should discard tabs suggested in the tab list
@@ -462,9 +463,10 @@
   const char kSuggestedCloseButton[] = "SuggestedCloseButton";
 
   RunTestSequence(
+      InstrumentTab(kFirstTab), NavigateWebContents(kFirstTab, GetURL()),
       AddInstrumentedTab(kSecondTab, GetURL()),
-      AddInstrumentedTab(kThirdTab, GetURL()), SelectTab(kTabStripElementId, 0),
-      TriggerOnActionableTabListChange({1, 2}),
+      AddInstrumentedTab(kThirdTab, GetURL()), WaitForShow(kThirdTab),
+      TriggerOnActionableTabListChange({0, 1}),
       WaitForShow(kToolbarPerformanceInterventionButtonElementId),
       WaitForShow(
           PerformanceInterventionBubble::kPerformanceInterventionTabList),
@@ -489,7 +491,7 @@
       PressButton(PerformanceInterventionBubble::
                       kPerformanceInterventionDialogDeactivateButton),
       WaitForState(kTabDiscardedState, true), CheckTabDiscardStatus(0, false),
-      CheckTabDiscardStatus(1, false), CheckTabDiscardStatus(2, true));
+      CheckTabDiscardStatus(1, true), CheckTabDiscardStatus(2, false));
 }
 
 // Intervention dialog should only show when the performance intervention
diff --git a/chrome/browser/ui/views/performance_controls/test_support/memory_saver_unit_test_mixin.h b/chrome/browser/ui/views/performance_controls/test_support/memory_saver_unit_test_mixin.h
index e9df6a7..a9915f1 100644
--- a/chrome/browser/ui/views/performance_controls/test_support/memory_saver_unit_test_mixin.h
+++ b/chrome/browser/ui/views/performance_controls/test_support/memory_saver_unit_test_mixin.h
@@ -7,9 +7,12 @@
 
 #include <concepts>
 
+#include "chrome/browser/extensions/api/tabs/tabs_api.h"
 #include "chrome/browser/ui/performance_controls/memory_saver_chip_tab_helper.h"
+#include "chrome/browser/ui/tabs/public/tab_features.h"
 #include "chrome/browser/ui/views/frame/test_with_browser_view.h"
 #include "chrome/browser/ui/views/performance_controls/test_support/discard_mock_navigation_handle.h"
+#include "components/tabs/public/tab_interface.h"
 #include "content/public/test/mock_navigation_handle.h"
 
 // Template to be used as a mixin class for memory saver tests extending
@@ -24,9 +27,6 @@
 
   ~MemorySaverUnitTestMixin() override = default;
 
-  MemorySaverUnitTestMixin(const MemorySaverUnitTestMixin&) = delete;
-  MemorySaverUnitTestMixin& operator=(const MemorySaverUnitTestMixin&) = delete;
-
   void SetMemorySaverModeEnabled(bool enabled) {
     performance_manager::user_tuning::UserPerformanceTuningManager::
         GetInstance()
@@ -40,7 +40,6 @@
     T::AddTab(T::browser(), GURL("http://foo.com"));
     content::WebContents* const contents =
         T::browser()->tab_strip_model()->GetActiveWebContents();
-    MemorySaverChipTabHelper::CreateForWebContents(contents);
     performance_manager::user_tuning::UserPerformanceTuningManager::
         PreDiscardResourceUsage::CreateForWebContents(contents, memory_savings,
                                                       discard_reason);
@@ -53,7 +52,11 @@
         std::make_unique<DiscardMockNavigationHandle>();
     navigation_handle.get()->SetWasDiscarded(is_discarded);
     navigation_handle.get()->SetWebContents(web_contents);
-    MemorySaverChipTabHelper::FromWebContents(web_contents)
+    T::browser()
+        ->tab_strip_model()
+        ->GetTabAtIndex(tab_index)
+        ->GetTabFeatures()
+        ->memory_saver_chip_helper()
         ->DidStartNavigation(navigation_handle.get());
 
     T::browser_view()
diff --git a/chrome/browser/ui/views/tabs/compound_tab_container.cc b/chrome/browser/ui/views/tabs/compound_tab_container.cc
index 89c707db..3068beb 100644
--- a/chrome/browser/ui/views/tabs/compound_tab_container.cc
+++ b/chrome/browser/ui/views/tabs/compound_tab_container.cc
@@ -676,11 +676,6 @@
   return unpinned_tab_container_->GetActiveTabWidth();
 }
 
-int CompoundTabContainer::GetInactiveTabWidth() const {
-  // Only the unpinned container has variable-width tabs.
-  return unpinned_tab_container_->GetInactiveTabWidth();
-}
-
 gfx::Rect CompoundTabContainer::GetIdealBounds(int model_index) const {
   // Ideal bounds for pinned tabs are fine as-is.
   if (model_index < NumPinnedTabs()) {
diff --git a/chrome/browser/ui/views/tabs/compound_tab_container.h b/chrome/browser/ui/views/tabs/compound_tab_container.h
index 579f102..9bb50d65 100644
--- a/chrome/browser/ui/views/tabs/compound_tab_container.h
+++ b/chrome/browser/ui/views/tabs/compound_tab_container.h
@@ -97,7 +97,6 @@
   const std::map<tab_groups::TabGroupId, std::unique_ptr<TabGroupViews>>&
   get_group_views_for_testing() const override;
   int GetActiveTabWidth() const override;
-  int GetInactiveTabWidth() const override;
   gfx::Rect GetIdealBounds(int model_index) const override;
   gfx::Rect GetIdealBounds(tab_groups::TabGroupId group) const override;
 
diff --git a/chrome/browser/ui/views/tabs/dragging/tab_drag_controller.cc b/chrome/browser/ui/views/tabs/dragging/tab_drag_controller.cc
index a1e6a2c..cc71040 100644
--- a/chrome/browser/ui/views/tabs/dragging/tab_drag_controller.cc
+++ b/chrome/browser/ui/views/tabs/dragging/tab_drag_controller.cc
@@ -1255,10 +1255,6 @@
     }
   }
 
-  // If we're dragging a saved group, resume tracking now that the group is
-  // re-attached.
-  MaybeResumeTrackingSavedTabGroup();
-
   AttachImpl();
 
   attached_context_->OwnDragController(std::move(controller));
@@ -1313,10 +1309,6 @@
 
   TabStripModel* attached_model = attached_context_->GetTabStripModel();
 
-  // If we're dragging a saved tab group, suspend tracking between Detach and
-  // Attach. Otherwise, the group will get emptied out as we close all the tabs.
-  MaybePauseTrackingSavedTabGroup();
-
   for (TabDragData& tab_drag_datum : drag_data_.tab_drag_data_) {
     // Marking the view as detached tells the TabStrip to not animate its
     // closure, as it's actually being moved.
@@ -1696,10 +1688,6 @@
   CHECK(attached_context_);
   CHECK(source_context_);
 
-  // If we're dragging a saved tab group, suspend tracking during the revert.
-  // Otherwise, the group will get emptied out as we revert all the tabs.
-  MaybePauseTrackingSavedTabGroup();
-
   base::AutoReset<bool> is_mutating_setter(&is_mutating_, true);
   base::AutoReset<bool> is_removing_last_tab_setter(
       &is_removing_last_tab_for_revert_, true);
@@ -1728,7 +1716,6 @@
            1;
     }
   }
-  MaybeResumeTrackingSavedTabGroup();
 
   if (did_restore_window_) {
     MaximizeAttachedWindow();
@@ -2501,47 +2488,6 @@
   }
 }
 
-void TabDragController::MaybePauseTrackingSavedTabGroup() {
-  if (!drag_data_.group_drag_data_.has_value()) {
-    return;
-  }
-
-  const Browser* const browser =
-      BrowserView::GetBrowserViewForNativeWindow(
-          GetAttachedBrowserWidget()->GetNativeWindow())
-          ->browser();
-
-  tab_groups::TabGroupSyncService* tab_group_service =
-      tab_groups::SavedTabGroupUtils::GetServiceForProfile(browser->profile());
-
-  if (!tab_group_service ||
-      !tab_group_service->GetGroup(drag_data_.group_drag_data_.value().group)) {
-    return;
-  }
-
-  observation_pauser_ = tab_group_service->CreateScopedLocalObserverPauser();
-}
-
-void TabDragController::MaybeResumeTrackingSavedTabGroup() {
-  if (!drag_data_.group_drag_data_.has_value() || !observation_pauser_) {
-    return;
-  }
-
-  const Browser* const browser =
-      BrowserView::GetBrowserViewForNativeWindow(
-          GetAttachedBrowserWidget()->GetNativeWindow())
-          ->browser();
-
-  tab_groups::TabGroupSyncService* tab_group_service =
-      tab_groups::SavedTabGroupUtils::GetServiceForProfile(browser->profile());
-
-  if (!tab_group_service) {
-    return;
-  }
-
-  observation_pauser_.reset();
-}
-
 void TabDragController::StartDraggingTabsSession(
     bool initial_move,
     gfx::Point start_point_in_screen) {
diff --git a/chrome/browser/ui/views/tabs/dragging/tab_drag_controller.h b/chrome/browser/ui/views/tabs/dragging/tab_drag_controller.h
index d4c5f0e..45e5f05c 100644
--- a/chrome/browser/ui/views/tabs/dragging/tab_drag_controller.h
+++ b/chrome/browser/ui/views/tabs/dragging/tab_drag_controller.h
@@ -492,12 +492,6 @@
   // as a result of a drag finishing.
   void NotifyEventIfTabAddedToGroup();
 
-  // Similar implementations present in
-  // chrome/browser/ui/webui/tab_strip/tab_strip_page_handler.cc. If logic  is
-  // updated in one, the other should also be updated.
-  void MaybePauseTrackingSavedTabGroup();
-  void MaybeResumeTrackingSavedTabGroup();
-
   // Initializes `dragging_tabs_session_`, and performs a first MoveAttached
   // within `attached_context_`.
   void StartDraggingTabsSession(bool initial_move,
diff --git a/chrome/browser/ui/views/tabs/fake_tab_slot_controller.cc b/chrome/browser/ui/views/tabs/fake_tab_slot_controller.cc
index 39af71a3..7fd15ee 100644
--- a/chrome/browser/ui/views/tabs/fake_tab_slot_controller.cc
+++ b/chrome/browser/ui/views/tabs/fake_tab_slot_controller.cc
@@ -146,10 +146,6 @@
   return nullptr;
 }
 
-int FakeTabSlotController::GetInactiveTabWidth() const {
-  return inactive_tab_width_;
-}
-
 bool FakeTabSlotController::IsFrameCondensed() const {
   return false;
 }
diff --git a/chrome/browser/ui/views/tabs/fake_tab_slot_controller.h b/chrome/browser/ui/views/tabs/fake_tab_slot_controller.h
index cf153987..d926ed18 100644
--- a/chrome/browser/ui/views/tabs/fake_tab_slot_controller.h
+++ b/chrome/browser/ui/views/tabs/fake_tab_slot_controller.h
@@ -97,7 +97,6 @@
   void ShiftGroupLeft(const tab_groups::TabGroupId& group) override {}
   void ShiftGroupRight(const tab_groups::TabGroupId& group) override {}
   const Browser* GetBrowser() const override;
-  int GetInactiveTabWidth() const override;
   bool IsFrameCondensed() const override;
   TabGroup* GetTabGroup(const tab_groups::TabGroupId& group_id) const override;
 
diff --git a/chrome/browser/ui/views/tabs/tab_container.h b/chrome/browser/ui/views/tabs/tab_container.h
index 196fff1..ed2983d 100644
--- a/chrome/browser/ui/views/tabs/tab_container.h
+++ b/chrome/browser/ui/views/tabs/tab_container.h
@@ -187,9 +187,6 @@
   // Returns the current width of the active tab.
   virtual int GetActiveTabWidth() const = 0;
 
-  // Returns the current width of inactive tabs.
-  virtual int GetInactiveTabWidth() const = 0;
-
   // Returns ideal bounds for the tab at `model_index` in this TabContainer's
   // coordinate space.
   virtual gfx::Rect GetIdealBounds(int model_index) const = 0;
diff --git a/chrome/browser/ui/views/tabs/tab_container_impl.cc b/chrome/browser/ui/views/tabs/tab_container_impl.cc
index f502911..83db6780 100644
--- a/chrome/browser/ui/views/tabs/tab_container_impl.cc
+++ b/chrome/browser/ui/views/tabs/tab_container_impl.cc
@@ -826,10 +826,6 @@
   return layout_helper_->active_tab_width();
 }
 
-int TabContainerImpl::GetInactiveTabWidth() const {
-  return layout_helper_->inactive_tab_width();
-}
-
 gfx::Rect TabContainerImpl::GetIdealBounds(int model_index) const {
   return tabs_view_model_.ideal_bounds(model_index);
 }
diff --git a/chrome/browser/ui/views/tabs/tab_container_impl.h b/chrome/browser/ui/views/tabs/tab_container_impl.h
index 5a346a5d..b1af740 100644
--- a/chrome/browser/ui/views/tabs/tab_container_impl.h
+++ b/chrome/browser/ui/views/tabs/tab_container_impl.h
@@ -131,7 +131,6 @@
   get_group_views_for_testing() const override;
 
   int GetActiveTabWidth() const override;
-  int GetInactiveTabWidth() const override;
 
   gfx::Rect GetIdealBounds(int model_index) const override;
   gfx::Rect GetIdealBounds(tab_groups::TabGroupId group) const override;
diff --git a/chrome/browser/ui/views/tabs/tab_group_editor_bubble_view_browsertest.cc b/chrome/browser/ui/views/tabs/tab_group_editor_bubble_view_browsertest.cc
index ceedb4a..d72a0089 100644
--- a/chrome/browser/ui/views/tabs/tab_group_editor_bubble_view_browsertest.cc
+++ b/chrome/browser/ui/views/tabs/tab_group_editor_bubble_view_browsertest.cc
@@ -11,6 +11,7 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/tabs/features.h"
 #include "chrome/browser/ui/tabs/tab_group_deletion_dialog_controller.h"
 #include "chrome/browser/ui/tabs/tab_group_model.h"
 #include "chrome/browser/ui/test/test_browser_dialog.h"
@@ -35,7 +36,8 @@
   TabGroupEditorBubbleViewDialogBrowserTest() {
     scoped_feature_list_.InitWithFeatures(
         {}, {data_sharing::features::kDataSharingFeature,
-             data_sharing::features::kDataSharingJoinOnly});
+             data_sharing::features::kDataSharingJoinOnly,
+             tabs::kTabGroupShortcuts});
   }
 
  protected:
diff --git a/chrome/browser/ui/views/tabs/tab_slot_controller.h b/chrome/browser/ui/views/tabs/tab_slot_controller.h
index 76fa7644..add0678 100644
--- a/chrome/browser/ui/views/tabs/tab_slot_controller.h
+++ b/chrome/browser/ui/views/tabs/tab_slot_controller.h
@@ -256,10 +256,6 @@
 
   virtual const Browser* GetBrowser() const = 0;
 
-  // Returns the current width of inactive tabs. An individual inactive tab may
-  // differ from this width slightly due to rounding.
-  virtual int GetInactiveTabWidth() const = 0;
-
   // See BrowserNonClientFrameView::IsFrameCondensed().
   virtual bool IsFrameCondensed() const = 0;
 
diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc
index 561724d..e1ddab79 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip.cc
@@ -2069,10 +2069,6 @@
   return controller_->GetBrowser();
 }
 
-int TabStrip::GetInactiveTabWidth() const {
-  return tab_container_->GetInactiveTabWidth();
-}
-
 bool TabStrip::IsFrameCondensed() const {
   return controller_->IsFrameCondensed();
 }
@@ -2561,5 +2557,4 @@
                                ui::metadata::SkColorConverter)
 ADD_READONLY_PROPERTY_METADATA(float, HoverOpacityForRadialHighlight)
 ADD_READONLY_PROPERTY_METADATA(int, ActiveTabWidth)
-ADD_READONLY_PROPERTY_METADATA(int, InactiveTabWidth)
 END_METADATA
diff --git a/chrome/browser/ui/views/tabs/tab_strip.h b/chrome/browser/ui/views/tabs/tab_strip.h
index 66f457e..e6d84f5 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.h
+++ b/chrome/browser/ui/views/tabs/tab_strip.h
@@ -340,7 +340,6 @@
   void ShiftGroupLeft(const tab_groups::TabGroupId& group) override;
   void ShiftGroupRight(const tab_groups::TabGroupId& group) override;
   const Browser* GetBrowser() const override;
-  int GetInactiveTabWidth() const override;
   bool IsFrameCondensed() const override;
 #if BUILDFLAG(IS_CHROMEOS)
   bool IsLockedForOnTask() override;
diff --git a/chrome/browser/ui/views/tabs/tab_strip_unittest.cc b/chrome/browser/ui/views/tabs/tab_strip_unittest.cc
index caa2c82..917db15 100644
--- a/chrome/browser/ui/views/tabs/tab_strip_unittest.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip_unittest.cc
@@ -186,7 +186,6 @@
   }
 
   int GetActiveTabWidth() { return tab_strip_->GetActiveTabWidth(); }
-  int GetInactiveTabWidth() { return tab_strip_->GetInactiveTabWidth(); }
 
   // End any outstanding drag and animate tabs back to their ideal bounds.
   void StopDragging() { tab_strip_->GetDragContext()->StoppedDragging(); }
@@ -477,18 +476,15 @@
   SetMaxTabStripWidth(1000);
 
   EXPECT_EQ(standard_width, GetActiveTabWidth());
-  EXPECT_EQ(standard_width, GetInactiveTabWidth());
 
   SetMaxTabStripWidth(240);
 
   EXPECT_LT(GetActiveTabWidth(), standard_width);
-  EXPECT_EQ(GetInactiveTabWidth(), GetActiveTabWidth());
 
   SetMaxTabStripWidth(50);
 
   EXPECT_EQ(TabStyle::Get()->GetMinimumActiveWidth(/*is_split*/ false),
             GetActiveTabWidth());
-  EXPECT_EQ(TabStyle::Get()->GetMinimumInactiveWidth(), GetInactiveTabWidth());
 }
 
 // The active tab should always be at least as wide as its minimum width.
diff --git a/chrome/browser/ui/views/webid/account_selection_bubble_view.cc b/chrome/browser/ui/views/webid/account_selection_bubble_view.cc
index 56f998d77..2a8322c 100644
--- a/chrome/browser/ui/views/webid/account_selection_bubble_view.cc
+++ b/chrome/browser/ui/views/webid/account_selection_bubble_view.cc
@@ -57,6 +57,7 @@
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/layout/box_layout_view.h"
 #include "ui/views/layout/flex_layout.h"
+#include "ui/views/layout/flex_layout_types.h"
 #include "ui/views/view_class_properties.h"
 #include "ui/views/widget/widget.h"
 
@@ -228,7 +229,7 @@
     const IdentityRequestAccountPtr& account,
     const std::u16string& title) {
   UpdateHeader(account->identity_provider->idp_metadata.brand_decoded_icon,
-               title, webid::GetSubtitle(rp_data_),
+               title, u"",
                /*show_back_button=*/false,
                /*should_circle_crop_header_icon=*/true);
 
@@ -498,6 +499,11 @@
       header->AddChildView(std::make_unique<views::View>());
   titles_container->SetLayoutManager(std::make_unique<views::BoxLayout>(
       views::BoxLayout::Orientation::kVertical));
+  views::FlexSpecification flex_spec(views::LayoutOrientation::kHorizontal,
+                                     views::MinimumFlexSizeRule::kScaleToZero,
+                                     views::MaximumFlexSizeRule::kUnbounded);
+  titles_container->SetProperty(views::kFlexBehaviorKey, flex_spec);
+
   // Add the title.
   title_label_ = titles_container->AddChildView(std::make_unique<views::Label>(
       title_, views::style::CONTEXT_DIALOG_BODY_TEXT,
diff --git a/chrome/browser/ui/webui/ash/login/app_launch_splash_screen_handler.cc b/chrome/browser/ui/webui/ash/login/app_launch_splash_screen_handler.cc
index 00ab868..5e26139 100644
--- a/chrome/browser/ui/webui/ash/login/app_launch_splash_screen_handler.cc
+++ b/chrome/browser/ui/webui/ash/login/app_launch_splash_screen_handler.cc
@@ -61,6 +61,10 @@
                   l10n_util::GetStringUTF8(GetProgressMessageFromState(state)));
 }
 
+void AppLaunchSplashScreenHandler::HideThrobber() {
+  CallExternalAPI("hideThrobber");
+}
+
 int AppLaunchSplashScreenHandler::GetProgressMessageFromState(
     AppLaunchState state) {
   switch (state) {
@@ -78,6 +82,8 @@
       return IDS_APP_START_NETWORK_WAIT_TIMEOUT_MESSAGE;
     case AppLaunchState::kShowingNetworkConfigureUI:
       return IDS_APP_START_SHOWING_NETWORK_CONFIGURE_UI_MESSAGE;
+    case AppLaunchSplashScreenView::AppLaunchState::kChromeAppDeprecated:
+      return IDS_KIOSK_APP_ERROR_UNABLE_TO_LAUNCH_CHROME_APP;
   }
 }
 
diff --git a/chrome/browser/ui/webui/ash/login/app_launch_splash_screen_handler.h b/chrome/browser/ui/webui/ash/login/app_launch_splash_screen_handler.h
index 466253d..d0128b9 100644
--- a/chrome/browser/ui/webui/ash/login/app_launch_splash_screen_handler.h
+++ b/chrome/browser/ui/webui/ash/login/app_launch_splash_screen_handler.h
@@ -21,6 +21,7 @@
     kWaitingAppWindow,
     kNetworkWaitTimeout,
     kShowingNetworkConfigureUI,
+    kChromeAppDeprecated,
   };
 
   inline constexpr static StaticOobeScreenId kScreenId{"app-launch-splash",
@@ -37,6 +38,8 @@
   // Sets whether configure network control is visible.
   virtual void ToggleNetworkConfig(bool visible) = 0;
 
+  virtual void HideThrobber() = 0;
+
   // Sets the contents of the screen with the given `data`.
   virtual void SetAppData(base::Value::Dict data) = 0;
 
@@ -66,6 +69,7 @@
   void Show(base::Value::Dict data) override;
   void ToggleNetworkConfig(bool visible) override;
   void UpdateAppLaunchText(AppLaunchState state) override;
+  void HideThrobber() override;
   void SetAppData(base::Value::Dict data) override;
 
   // Gets a WeakPtr to the instance.
diff --git a/chrome/browser/ui/webui/searchbox/BUILD.gn b/chrome/browser/ui/webui/searchbox/BUILD.gn
index 3a492d7..1d4b9b7 100644
--- a/chrome/browser/ui/webui/searchbox/BUILD.gn
+++ b/chrome/browser/ui/webui/searchbox/BUILD.gn
@@ -41,6 +41,7 @@
     "//chrome/browser/search_engines",
     "//chrome/browser/ui/bookmarks",
     "//chrome/browser/ui/omnibox",
+    "//chrome/browser/ui/search",
     "//components/bookmarks/browser",
     "//components/navigation_metrics",
     "//components/omnibox/browser",
diff --git a/chrome/browser/ui/webui/settings/settings_ui.cc b/chrome/browser/ui/webui/settings/settings_ui.cc
index aa58ce93..f0249546 100644
--- a/chrome/browser/ui/webui/settings/settings_ui.cc
+++ b/chrome/browser/ui/webui/settings/settings_ui.cc
@@ -648,8 +648,8 @@
                autofill::AutofillAiAction::kListEntityInstancesInSettings)},
   };
 
-  const bool show_ai_settings_for_testing =
-      optimization_guide::features::kShowAiSettingsForTesting.Get();
+  const bool show_ai_settings_for_testing = base::FeatureList::IsEnabled(
+      optimization_guide::features::kAiSettingsPageForceAvailable);
 
   // Show the AI features section in the AI page if any of the AI features are
   // enabled.
diff --git a/chrome/browser/ui/webui/signin/login_ui_test_utils.cc b/chrome/browser/ui/webui/signin/login_ui_test_utils.cc
index 141caf73..aa0244f7 100644
--- a/chrome/browser/ui/webui/signin/login_ui_test_utils.cc
+++ b/chrome/browser/ui/webui/signin/login_ui_test_utils.cc
@@ -54,7 +54,8 @@
 // success or failure notification is fired.
 class SignInObserver : public signin::IdentityManager::Observer {
  public:
-  SignInObserver() = default;
+  explicit SignInObserver(signin::ConsentLevel consent_level)
+      : consent_level_(consent_level) {}
 
   // Returns whether a GoogleSigninSucceeded event has happened.
   bool DidSignIn() { return signed_in_; }
@@ -88,7 +89,7 @@
 
   void OnPrimaryAccountChanged(
       const signin::PrimaryAccountChangeEvent& event) override {
-    if (event.GetEventTypeFor(signin::ConsentLevel::kSignin) !=
+    if (event.GetEventTypeFor(consent_level_) !=
         signin::PrimaryAccountChangeEvent::Type::kSet) {
       return;
     }
@@ -108,6 +109,7 @@
   }
 
  private:
+  const signin::ConsentLevel consent_level_;
   // Bool to mark an observed event as seen prior to calling Wait(), used to
   // prevent the observer from blocking.
   bool seen_ = false;
@@ -444,7 +446,7 @@
 #if BUILDFLAG(IS_CHROMEOS)
   NOTREACHED();
 #else
-  SignInObserver signin_observer;
+  SignInObserver signin_observer(consent_level);
   base::ScopedObservation<signin::IdentityManager,
                           signin::IdentityManager::Observer>
       scoped_signin_observation(&signin_observer);
diff --git a/chrome/browser/win/installer_downloader/installer_downloader_controller.cc b/chrome/browser/win/installer_downloader/installer_downloader_controller.cc
index 3684e65..e60b688 100644
--- a/chrome/browser/win/installer_downloader/installer_downloader_controller.cc
+++ b/chrome/browser/win/installer_downloader/installer_downloader_controller.cc
@@ -91,9 +91,11 @@
   // InstallerDownloaderController will be alive at any point during the browser
   // runtime.
   show_infobar_callback_.Run(
-      contents, base::BindRepeating(
-                    &InstallerDownloaderController::OnDownloadRequestAccepted,
-                    base::Unretained(this)));
+      contents,
+      base::BindOnce(&InstallerDownloaderController::OnDownloadRequestAccepted,
+                     base::Unretained(this)),
+      base::BindOnce(&InstallerDownloaderController::OnInfoBarDismissed,
+                     base::Unretained(this)));
 }
 
 void InstallerDownloaderController::OnDownloadRequestAccepted() {
@@ -114,4 +116,9 @@
   get_active_web_contents_callback_ = std::move(callback);
 }
 
+void InstallerDownloaderController::OnInfoBarDismissed() {
+  // TODO(crbug.com/417709084):Dismisses all installer Downloader infobars
+  // since this infobar is global.
+}
+
 }  // namespace installer_downloader
diff --git a/chrome/browser/win/installer_downloader/installer_downloader_controller.h b/chrome/browser/win/installer_downloader/installer_downloader_controller.h
index 1a33721..9464b3e 100644
--- a/chrome/browser/win/installer_downloader/installer_downloader_controller.h
+++ b/chrome/browser/win/installer_downloader/installer_downloader_controller.h
@@ -44,7 +44,8 @@
   // TODO(https://crbug.com/417709084): Make the infobar global to the browser.
   using ShowInfobarCallback =
       base::RepeatingCallback<void(content::WebContents* web_contents,
-                                   base::RepeatingClosure on_accept)>;
+                                   base::OnceClosure on_accept,
+                                   base::OnceClosure on_dismiss)>;
 
   using GetActiveWebContentsCallback =
       base::RepeatingCallback<content::WebContents*()>;
@@ -69,6 +70,9 @@
   // infobar.
   void OnDownloadRequestAccepted();
 
+  // Called when the user dismisses the installer download infobar.
+  void OnInfoBarDismissed();
+
   void SetActiveWebContentsCallbackForTesting(
       GetActiveWebContentsCallback callback);
 
diff --git a/chrome/browser/win/installer_downloader/installer_downloader_controller_unittest.cc b/chrome/browser/win/installer_downloader/installer_downloader_controller_unittest.cc
index 09e10a15..be1de34 100644
--- a/chrome/browser/win/installer_downloader/installer_downloader_controller_unittest.cc
+++ b/chrome/browser/win/installer_downloader/installer_downloader_controller_unittest.cc
@@ -97,7 +97,7 @@
   EXPECT_CALL(*mock_model_, CheckEligibility(_))
       .WillOnce(base::test::RunOnceCallback<0>(
           std::optional<base::FilePath>(base::FilePath(L"C:\\foo"))));
-  EXPECT_CALL(show_infobar_callback_, Run(_, _)).Times(1);
+  EXPECT_CALL(show_infobar_callback_, Run(_, _, _)).Times(1);
 
   controller_->MaybeShowInfoBar();
 }
diff --git a/chrome/browser/win/installer_downloader/installer_downloader_infobar_delegate.cc b/chrome/browser/win/installer_downloader/installer_downloader_infobar_delegate.cc
index e3b15a09..1faa8ca 100644
--- a/chrome/browser/win/installer_downloader/installer_downloader_infobar_delegate.cc
+++ b/chrome/browser/win/installer_downloader/installer_downloader_infobar_delegate.cc
@@ -29,22 +29,23 @@
 }  // namespace
 
 // static
-void InstallerDownloaderInfoBarDelegate::Show(
-    content::WebContents* contents,
-    base::RepeatingCallback<void()> accept_cb) {
+void InstallerDownloaderInfoBarDelegate::Show(content::WebContents* contents,
+                                              base::OnceClosure accept_cb,
+                                              base::OnceClosure close_cb) {
   infobars::ContentInfoBarManager* infobar_manager =
       infobars::ContentInfoBarManager::FromWebContents(contents);
 
   std::unique_ptr<InstallerDownloaderInfoBarDelegate> delegate =
-      std::make_unique<InstallerDownloaderInfoBarDelegate>(
-          std::move(accept_cb));
+      std::make_unique<InstallerDownloaderInfoBarDelegate>(std::move(accept_cb),
+                                                           std::move(close_cb));
   infobar_manager->AddInfoBar(
       std::make_unique<ConfirmInfoBar>(std::move(delegate)));
 }
 
 InstallerDownloaderInfoBarDelegate::InstallerDownloaderInfoBarDelegate(
-    base::RepeatingCallback<void()> accept_cb)
-    : accept_cb_(std::move(accept_cb)) {}
+    base::OnceClosure accept_cb,
+    base::OnceClosure close_cb)
+    : accept_cb_(std::move(accept_cb)), close_cb_(std::move(close_cb)) {}
 
 InstallerDownloaderInfoBarDelegate::~InstallerDownloaderInfoBarDelegate() =
     default;
@@ -66,6 +67,10 @@
   return false;
 }
 
+void InstallerDownloaderInfoBarDelegate::InfoBarDismissed() {
+  std::move(close_cb_).Run();
+}
+
 std::u16string InstallerDownloaderInfoBarDelegate::GetMessageText() const {
   return l10n_util::GetStringUTF16(IDS_INSTALLER_DOWNLOADER_DISCLAIMER);
 }
diff --git a/chrome/browser/win/installer_downloader/installer_downloader_infobar_delegate.h b/chrome/browser/win/installer_downloader/installer_downloader_infobar_delegate.h
index 94610b1..ff90c3e 100644
--- a/chrome/browser/win/installer_downloader/installer_downloader_infobar_delegate.h
+++ b/chrome/browser/win/installer_downloader/installer_downloader_infobar_delegate.h
@@ -23,19 +23,21 @@
   // `infobar_manager`. The `infobar_manager` will own the returned infobar.
   // `accept_cb` is called when the user accepts the infobar.
   static void Show(content::WebContents* contents,
-                   base::RepeatingCallback<void()> accept_cb);
+                   base::OnceClosure accept_cb,
+                   base::OnceClosure close_cb);
 
   InstallerDownloaderInfoBarDelegate& operator=(
       const InstallerDownloaderInfoBarDelegate&) = delete;
 
-  explicit InstallerDownloaderInfoBarDelegate(
-      base::RepeatingCallback<void()> accept_cb);
+  explicit InstallerDownloaderInfoBarDelegate(base::OnceClosure accept_cb,
+                                              base::OnceClosure close_cb);
   ~InstallerDownloaderInfoBarDelegate() override;
 
   // ConfirmInfoBarDelegate:
   infobars::InfoBarDelegate::InfoBarIdentifier GetIdentifier() const override;
   const gfx::VectorIcon& GetVectorIcon() const override;
   bool ShouldExpire(const NavigationDetails& details) const override;
+  void InfoBarDismissed() override;
   std::u16string GetMessageText() const override;
   std::u16string GetLinkText() const override;
   int GetButtons() const override;
@@ -43,7 +45,8 @@
   bool LinkClicked(WindowOpenDisposition disposition) override;
 
  private:
-  base::RepeatingCallback<void()> accept_cb_;
+  base::OnceClosure accept_cb_;
+  base::OnceClosure close_cb_;
 };
 
 }  // namespace installer_downloader
diff --git a/chrome/browser/win/installer_downloader/installer_downloader_infobar_delegate_unittest.cc b/chrome/browser/win/installer_downloader/installer_downloader_infobar_delegate_unittest.cc
index de75a39..fa49fa0f 100644
--- a/chrome/browser/win/installer_downloader/installer_downloader_infobar_delegate_unittest.cc
+++ b/chrome/browser/win/installer_downloader/installer_downloader_infobar_delegate_unittest.cc
@@ -7,7 +7,9 @@
 #include <memory>
 #include <string>
 
+#include "base/functional/callback.h"
 #include "base/functional/callback_helpers.h"
+#include "base/test/mock_callback.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "components/infobars/content/content_infobar_manager.h"
 #include "components/infobars/core/infobar.h"
@@ -32,8 +34,10 @@
   }
   std::unique_ptr<InstallerDownloaderInfoBarDelegate> CreateDelegate() {
     return std::make_unique<InstallerDownloaderInfoBarDelegate>(
-        base::DoNothing());
+        mock_accept_cb_.Get(), mock_cancel_cb_.Get());
   }
+  base::MockCallback<base::OnceClosure> mock_accept_cb_;
+  base::MockCallback<base::OnceClosure> mock_cancel_cb_;
 };
 
 TEST_F(InstallerDownloaderInfoBarDelegateTest, CheckInfoBarProperties) {
@@ -78,5 +82,13 @@
             added_infobar_ptr->delegate()->GetIdentifier());
 }
 
+// TODO(crbug.com/412697757): Add views test for clicking cancel/accept on
+// infobar.
+TEST_F(InstallerDownloaderInfoBarDelegateTest, CancelClicked) {
+  auto delegate = CreateDelegate();
+  EXPECT_CALL(mock_cancel_cb_, Run).Times(1);
+  delegate->InfoBarDismissed();
+}
+
 }  // namespace
 }  // namespace installer_downloader
diff --git a/chrome/build/android-arm32.pgo.txt b/chrome/build/android-arm32.pgo.txt
index 1384592..5946352 100644
--- a/chrome/build/android-arm32.pgo.txt
+++ b/chrome/build/android-arm32.pgo.txt
@@ -1 +1 @@
-chrome-android32-main-1747742334-6dd303cdec98ae41409c007570c55985f2fc75ea-3313d9b6b84ff455dbf0edfee0bb15bbc23b30ba.profdata
+chrome-android32-main-1747763949-862ba35743570b1388e0908419acc48660817e39-a2a9986393624ba3e4c621acfbabca8a5ff76866.profdata
diff --git a/chrome/build/android-arm64.pgo.txt b/chrome/build/android-arm64.pgo.txt
index be445cb..13b8ece 100644
--- a/chrome/build/android-arm64.pgo.txt
+++ b/chrome/build/android-arm64.pgo.txt
@@ -1 +1 @@
-chrome-android64-main-1747744749-23bc3f96a17dd747a19326c8b3b4e872bb88498b-6eb39ab0ce8e71c966afab4da00d6ac003e5759c.profdata
+chrome-android64-main-1747768602-c3a25191590271747e5a4a657470637614e1b175-2433064901720dc6739ae4bf29a4d5281397043b.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 88df841..272a041 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1747749560-14134e82a99bc9728da3c39e508d86d73e5a72c5-bc470a9b72af690f4ac96c143326d6c452eb1fb9.profdata
+chrome-mac-arm-main-1747771183-7e8480ac405ebb979c92ef1626346d542786c7e8-16edc46f3f4ef93b1e06d871bd371a9a75b292ee.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index d1a8803..28d5b316 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1747742334-c36f6234cea2fe94ce950d60fa5af1623dd65de1-3313d9b6b84ff455dbf0edfee0bb15bbc23b30ba.profdata
+chrome-mac-main-1747763949-1ea80dd1344988e541bc7c34d8680f7fc1569f93-a2a9986393624ba3e4c621acfbabca8a5ff76866.profdata
diff --git a/chrome/build/win-arm64.pgo.txt b/chrome/build/win-arm64.pgo.txt
index e85ab5a..728f274 100644
--- a/chrome/build/win-arm64.pgo.txt
+++ b/chrome/build/win-arm64.pgo.txt
@@ -1 +1 @@
-chrome-win-arm64-main-1747742334-fa4db5913d22d998947a09568a7ad6a8f1ee3d6a-3313d9b6b84ff455dbf0edfee0bb15bbc23b30ba.profdata
+chrome-win-arm64-main-1747763949-a4e4fee37fd4402eb194cb8df2437c3d90fca234-a2a9986393624ba3e4c621acfbabca8a5ff76866.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 084986c..904dd88 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1747719893-2403c37ee947d167f7e46fc40ffabe576779ba87-2f97115fc991c1e755b76c32e12c2c3f10e7bd7b.profdata
+chrome-win32-main-1747742334-7f959dbfd853b8e8bf8add55915a869290dc042f-3313d9b6b84ff455dbf0edfee0bb15bbc23b30ba.profdata
diff --git a/chrome/common/BUILD.gn b/chrome/common/BUILD.gn
index 43ad4598..7c4b57024 100644
--- a/chrome/common/BUILD.gn
+++ b/chrome/common/BUILD.gn
@@ -573,6 +573,7 @@
     "//components/signin/public/base:signin_buildflags",
     "//content/public/common:buildflags",
     "//content/public/common:result_codes",
+    "//pdf:buildflags",
     "//rlz/buildflags",
     "//third_party/widevine/cdm:buildflags",
   ]
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index 25380eb..ccdc5b3 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -19,6 +19,7 @@
 #include "components/signin/public/base/signin_buildflags.h"
 #include "content/public/common/buildflags.h"
 #include "extensions/buildflags/buildflags.h"
+#include "pdf/buildflags.h"
 #include "ppapi/buildflags/buildflags.h"
 #include "printing/buildflags/buildflags.h"
 #include "rlz/buildflags/buildflags.h"
@@ -3936,11 +3937,13 @@
     "fetch_keepalive_duration_on_shutdown";
 #endif
 
-#if BUILDFLAG(IS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(ENABLE_PDF_INK2)
 // Boolean pref to control whether to enable annotation mode in the PDF viewer
 // or not.
 inline constexpr char kPdfAnnotationsEnabled[] = "pdf.enable_annotations";
+#endif
 
+#if BUILDFLAG(IS_CHROMEOS)
 // Boolean pref to control whether to enable Lens integration with media app
 inline constexpr char kMediaAppLensEnabled[] = "media_app.enable_lens";
 #endif
diff --git a/chrome/common/profiler/thread_profiler_platform_configuration.cc b/chrome/common/profiler/thread_profiler_platform_configuration.cc
index e59b9f9..bd85a7d 100644
--- a/chrome/common/profiler/thread_profiler_platform_configuration.cc
+++ b/chrome/common/profiler/thread_profiler_platform_configuration.cc
@@ -6,6 +6,7 @@
 
 #include "base/command_line.h"
 #include "base/containers/flat_map.h"
+#include "base/feature_list.h"
 #include "base/functional/callback.h"
 #include "base/notreached.h"
 #include "base/profiler/stack_sampling_profiler.h"
@@ -14,6 +15,10 @@
 #include "chrome/common/profiler/process_type.h"
 #include "components/sampling_profiler/process_type.h"
 
+BASE_FEATURE(kSamplingProfilerOnWorkerThreads,
+             "SamplingProfilerOnWorkerThreads",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 namespace {
 
 // The default configuration to use in the absence of special circumstances on a
@@ -119,10 +124,8 @@
     sampling_profiler::ProfilerProcessType process,
     sampling_profiler::ProfilerThreadType thread,
     std::optional<version_info::Channel> release_channel) const {
-  // TODO(crbug.com/40226611): Remove exception once ThreadPoolWorker profile
-  // sampling is enabled for thread pool worker.
   if (thread == sampling_profiler::ProfilerThreadType::kThreadPoolWorker) {
-    return false;
+    return base::FeatureList::IsEnabled(kSamplingProfilerOnWorkerThreads);
   }
   // Enable for all supported threads.
   return true;
@@ -290,14 +293,13 @@
     sampling_profiler::ProfilerProcessType process,
     sampling_profiler::ProfilerThreadType thread,
     std::optional<version_info::Channel> release_channel) const {
-  if (!release_channel.has_value() || browser_test_mode_enabled()) {
-    return true;
+  if (!DefaultPlatformConfiguration::IsEnabledForThread(process, thread,
+                                                        release_channel)) {
+    return false;
   }
 
-  // TODO(crbug.com/40226611): Remove exception once ThreadPoolWorker profile
-  // sampling is enabled for thread pool worker.
-  if (thread == sampling_profiler::ProfilerThreadType::kThreadPoolWorker) {
-    return false;
+  if (!release_channel.has_value() || browser_test_mode_enabled()) {
+    return true;
   }
 
   switch (*release_channel) {
diff --git a/chrome/common/profiler/thread_profiler_platform_configuration.h b/chrome/common/profiler/thread_profiler_platform_configuration.h
index 6a076b5..60f36a7 100644
--- a/chrome/common/profiler/thread_profiler_platform_configuration.h
+++ b/chrome/common/profiler/thread_profiler_platform_configuration.h
@@ -8,10 +8,18 @@
 #include <memory>
 #include <optional>
 
+#include "base/feature_list.h"
 #include "base/functional/callback.h"
 #include "components/sampling_profiler/process_type.h"
 #include "components/version_info/channel.h"
 
+// If enabled, ThreadProfilerPlatformConfiguration::IsEnabledForThread() will
+// return true for ThreadPoolWorker threads. This can use a normal feature
+// config instead of being part of the RelativePopulations experiment group
+// because the feature is only checked after the ThreadPool is created, at which
+// point field trials have been set up.
+BASE_DECLARE_FEATURE(kSamplingProfilerOnWorkerThreads);
+
 // Encapsulates the platform-specific configuration for the ThreadProfiler.
 //
 // The interface functions this class make a distinction between 'supported' and
diff --git a/chrome/common/profiler/thread_profiler_platform_configuration_unittest.cc b/chrome/common/profiler/thread_profiler_platform_configuration_unittest.cc
index bde342d9..8b31b30 100644
--- a/chrome/common/profiler/thread_profiler_platform_configuration_unittest.cc
+++ b/chrome/common/profiler/thread_profiler_platform_configuration_unittest.cc
@@ -4,11 +4,15 @@
 
 #include "chrome/common/profiler/thread_profiler_platform_configuration.h"
 
+#include <ostream>
+#include <tuple>
 #include <utility>
 
+#include "base/containers/enum_set.h"
 #include "base/profiler/profiler_buildflags.h"
 #include "base/test/bind.h"
 #include "base/test/gtest_util.h"
+#include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
 #include "components/sampling_profiler/process_type.h"
 #include "components/version_info/version_info.h"
@@ -19,19 +23,16 @@
     (BUILDFLAG(IS_ANDROID) && defined(ARCH_CPU_ARM64)) ||                   \
     (BUILDFLAG(IS_CHROMEOS) &&                                              \
      (defined(ARCH_CPU_X86_64) || defined(ARCH_CPU_ARM64)))
-#define THREAD_PROFILER_SUPPORTED_ON_PLATFORM true
+constexpr bool kThreadProfilerSupportedOnPlatform = true;
 #else
-#define THREAD_PROFILER_SUPPORTED_ON_PLATFORM false
-#endif
-
-#if THREAD_PROFILER_SUPPORTED_ON_PLATFORM
-#define MAYBE_PLATFORM_CONFIG_TEST_F(suite, test) TEST_F(suite, test)
-#else
-#define MAYBE_PLATFORM_CONFIG_TEST_F(suite, test) TEST_F(suite, DISABLED_##test)
+constexpr bool kThreadProfilerSupportedOnPlatform = false;
 #endif
 
 namespace {
 
+using RelativePopulations =
+    ThreadProfilerPlatformConfiguration::RelativePopulations;
+
 class ThreadProfilerPlatformConfigurationTest : public ::testing::Test {
  public:
   // The browser_test_mode_enabled=true scenario is already covered by the
@@ -48,166 +49,177 @@
   const std::unique_ptr<ThreadProfilerPlatformConfiguration> config_;
 };
 
+using ThreadProfilerPlatformConfigurationDeathTest =
+    ThreadProfilerPlatformConfigurationTest;
+
+class ThreadProfilerPlatformConfigurationThreadTest
+    : public ::testing::TestWithParam<std::tuple<bool, bool>> {
+ public:
+  ThreadProfilerPlatformConfigurationThreadTest() {
+    std::tie(enable_worker_threads_, enable_on_dev_channel_) = GetParam();
+    scoped_feature_list_.InitWithFeatureState(kSamplingProfilerOnWorkerThreads,
+                                              enable_worker_threads_);
+    config_ = ThreadProfilerPlatformConfiguration::Create(
+        /*browser_test_mode_enabled=*/false,
+        /*is_enabled_on_dev_callback=*/base::BindLambdaForTesting(
+            [this](double probability) { return enable_on_dev_channel_; }));
+  }
+
+  bool enable_worker_threads() const { return enable_worker_threads_; }
+  bool enable_on_dev_channel() const { return enable_on_dev_channel_; }
+
+  ThreadProfilerPlatformConfiguration* config() const { return config_.get(); }
+
+ private:
+  bool enable_worker_threads_;
+  bool enable_on_dev_channel_;
+  base::test::ScopedFeatureList scoped_feature_list_;
+  std::unique_ptr<ThreadProfilerPlatformConfiguration> config_;
+};
+
+INSTANTIATE_TEST_SUITE_P(
+    All,
+    ThreadProfilerPlatformConfigurationThreadTest,
+    ::testing::Combine(
+        ::testing::Bool(),
+#if BUILDFLAG(IS_ANDROID)
+        // AndroidPlatformConfiguration::IsEnabledForThread() checks the
+        // channel, so test with dev channel both enabled and disabled.
+        ::testing::Bool()
+#else
+        // DefaultPlatformConfiguration::IsEnabledForThread() doesn't check the
+        // channel, so no need to test with dev channel disabled.
+        ::testing::Values(true)
+#endif
+            ));
+
 }  // namespace
 
 // Glue functions to make RelativePopulations work with googletest.
-std::ostream& operator<<(
-    std::ostream& strm,
-    const ThreadProfilerPlatformConfiguration::RelativePopulations&
-        populations) {
+std::ostream& operator<<(std::ostream& strm,
+                         const RelativePopulations& populations) {
   return strm << "{" << populations.enabled << ", " << populations.experiment
               << "}";
 }
 
-bool operator==(
-    const ThreadProfilerPlatformConfiguration::RelativePopulations& a,
-    const ThreadProfilerPlatformConfiguration::RelativePopulations& b) {
+bool operator==(const RelativePopulations& a, const RelativePopulations& b) {
   return a.enabled == b.enabled && a.experiment == b.experiment;
 }
 
 TEST_F(ThreadProfilerPlatformConfigurationTest, IsSupported) {
-#if !THREAD_PROFILER_SUPPORTED_ON_PLATFORM
-  EXPECT_FALSE(config()->IsSupported(version_info::Channel::UNKNOWN));
-  EXPECT_FALSE(config()->IsSupported(version_info::Channel::CANARY));
-  EXPECT_FALSE(config()->IsSupported(version_info::Channel::DEV));
-  EXPECT_FALSE(config()->IsSupported(version_info::Channel::BETA));
-  EXPECT_FALSE(config()->IsSupported(version_info::Channel::STABLE));
-
-  EXPECT_FALSE(config()->IsSupported(std::nullopt));
+#if BUILDFLAG(IS_ANDROID) && !defined(ARCH_CPU_ARM64)
+  constexpr bool kIsSupportedOnStable = false;
 #else
-  EXPECT_FALSE(config()->IsSupported(version_info::Channel::UNKNOWN));
-  EXPECT_TRUE(config()->IsSupported(version_info::Channel::CANARY));
-  EXPECT_TRUE(config()->IsSupported(version_info::Channel::DEV));
-  EXPECT_TRUE(config()->IsSupported(version_info::Channel::BETA));
-#if BUILDFLAG(IS_ANDROID)
-#if defined(ARCH_CPU_ARM64)
-  EXPECT_TRUE(config()->IsSupported(version_info::Channel::STABLE));
-#else   // defined(ARCH_CPU_ARM64)
-  EXPECT_FALSE(config()->IsSupported(version_info::Channel::STABLE));
-#endif  // defined(ARCH_CPU_ARM64)
-#else   // BUILDFLAG(IS_ANDROID)
-  EXPECT_TRUE(config()->IsSupported(version_info::Channel::STABLE));
-#endif  // BUILDFLAG(IS_ANDROID)
-
-  EXPECT_TRUE(config()->IsSupported(std::nullopt));
+  constexpr bool kIsSupportedOnStable = true;
 #endif
+  EXPECT_FALSE(config()->IsSupported(version_info::Channel::UNKNOWN));
+  EXPECT_EQ(config()->IsSupported(version_info::Channel::CANARY),
+            kThreadProfilerSupportedOnPlatform);
+  EXPECT_EQ(config()->IsSupported(version_info::Channel::DEV),
+            kThreadProfilerSupportedOnPlatform);
+  EXPECT_EQ(config()->IsSupported(version_info::Channel::BETA),
+            kThreadProfilerSupportedOnPlatform);
+  EXPECT_EQ(config()->IsSupported(version_info::Channel::STABLE),
+            kThreadProfilerSupportedOnPlatform && kIsSupportedOnStable);
+  EXPECT_EQ(config()->IsSupported(std::nullopt),
+            kThreadProfilerSupportedOnPlatform);
 }
 
-MAYBE_PLATFORM_CONFIG_TEST_F(ThreadProfilerPlatformConfigurationTest,
-                             GetEnableRates) {
-  using RelativePopulations =
-      ThreadProfilerPlatformConfiguration::RelativePopulations;
+TEST_F(ThreadProfilerPlatformConfigurationDeathTest, GetEnableRates) {
 #if BUILDFLAG(IS_ANDROID)
-  EXPECT_EQ((RelativePopulations{0.0, 0.0, 100.0}),
-            config()->GetEnableRates(version_info::Channel::CANARY));
-  EXPECT_EQ((RelativePopulations{0.0, 0.0, 100.0}),
-            config()->GetEnableRates(version_info::Channel::DEV));
-  EXPECT_EQ((RelativePopulations{0.0, 0.0, 100.0}),
-            config()->GetEnableRates(version_info::Channel::BETA));
+  constexpr double kCanaryDevExperimentRate = 100.0;
+  constexpr double kBetaExperimentRate = 100.0;
 #if defined(ARCH_CPU_ARM64)
-  EXPECT_EQ((RelativePopulations{100.0 - 0.0001, 0.0, 0.0001}),
-            config()->GetEnableRates(version_info::Channel::STABLE));
+  constexpr double kStableExperimentRate = 0.0001;
 #else
-  EXPECT_EQ((RelativePopulations{100.0, 0.0, 0.0}),
-            config()->GetEnableRates(version_info::Channel::STABLE));
+  constexpr double kStableExperimentRate = 0.0;
 #endif
-  // Note: death tests aren't supported on Android. Otherwise this test would
-  // check that the other inputs result in CHECKs.
-#else
+#else   // !BUILDFLAG(IS_ANDROID)
+  constexpr double kCanaryDevExperimentRate = 20.0;
+  constexpr double kBetaExperimentRate = 10.0;
+  constexpr double kStableExperimentRate = 0.006;
+#endif  // !BUILDFLAG(IS_ANDROID)
+
   EXPECT_CHECK_DEATH(config()->GetEnableRates(version_info::Channel::UNKNOWN));
-  EXPECT_EQ((RelativePopulations{0.0, 80.0, 20.0}),
+
+  // 100% enabled on Canary/Dev, except for experiments.
+  EXPECT_EQ((RelativePopulations{.disabled = 0.0,
+                                 .enabled = 100.0 - kCanaryDevExperimentRate,
+                                 .experiment = kCanaryDevExperimentRate}),
             config()->GetEnableRates(version_info::Channel::CANARY));
-  EXPECT_EQ((RelativePopulations{0.0, 80.0, 20.0}),
+  EXPECT_EQ((RelativePopulations{.disabled = 0.0,
+                                 .enabled = 100.0 - kCanaryDevExperimentRate,
+                                 .experiment = kCanaryDevExperimentRate}),
             config()->GetEnableRates(version_info::Channel::DEV));
-  EXPECT_EQ((RelativePopulations{90.0, 0.0, 10.0}),
+  // 100% disabled on Beta/Stable, except for experiments.
+  EXPECT_EQ((RelativePopulations{.disabled = 100.0 - kBetaExperimentRate,
+                                 .enabled = 0.0,
+                                 .experiment = kBetaExperimentRate}),
             config()->GetEnableRates(version_info::Channel::BETA));
-  EXPECT_EQ((RelativePopulations{100.0 - 0.006, 0.0, 0.006}),
+  EXPECT_EQ((RelativePopulations{.disabled = 100.0 - kStableExperimentRate,
+                                 .enabled = 0.0,
+                                 .experiment = kStableExperimentRate}),
             config()->GetEnableRates(version_info::Channel::STABLE));
 
-  EXPECT_EQ((RelativePopulations{0.0, 100.0, 0.0}),
+  EXPECT_EQ((RelativePopulations{
+                .disabled = 0.0, .enabled = 100.0, .experiment = 0.0}),
             config()->GetEnableRates(std::nullopt));
-#endif
 }
 
-MAYBE_PLATFORM_CONFIG_TEST_F(ThreadProfilerPlatformConfigurationTest,
-                             GetChildProcessPerExecutionEnableFraction) {
+TEST_F(ThreadProfilerPlatformConfigurationTest,
+       GetChildProcessPerExecutionEnableFraction) {
+#if BUILDFLAG(IS_ANDROID)
+  // Android child processes that match ChooseEnabledProcess() should be
+  // profiled unconditionally.
+  constexpr bool kAlwaysEnable = true;
+#else
+  constexpr bool kAlwaysEnable = false;
+#endif
+
   EXPECT_EQ(1.0, config()->GetChildProcessPerExecutionEnableFraction(
                      sampling_profiler::ProfilerProcessType::kGpu));
   EXPECT_EQ(1.0, config()->GetChildProcessPerExecutionEnableFraction(
                      sampling_profiler::ProfilerProcessType::kNetworkService));
-
-#if BUILDFLAG(IS_ANDROID)
-  // Android child processes that match ChooseEnabledProcess() should be
-  // profiled unconditionally.
-  EXPECT_EQ(1.0, config()->GetChildProcessPerExecutionEnableFraction(
-                     sampling_profiler::ProfilerProcessType::kRenderer));
-  EXPECT_EQ(1.0, config()->GetChildProcessPerExecutionEnableFraction(
-                     sampling_profiler::ProfilerProcessType::kUnknown));
-#else
-  EXPECT_EQ(0.2, config()->GetChildProcessPerExecutionEnableFraction(
-                     sampling_profiler::ProfilerProcessType::kRenderer));
-  EXPECT_EQ(0.0, config()->GetChildProcessPerExecutionEnableFraction(
-                     sampling_profiler::ProfilerProcessType::kUnknown));
-#endif
+  EXPECT_EQ(kAlwaysEnable ? 1.0 : 0.2,
+            config()->GetChildProcessPerExecutionEnableFraction(
+                sampling_profiler::ProfilerProcessType::kRenderer));
+  EXPECT_EQ(kAlwaysEnable ? 1.0 : 0.0,
+            config()->GetChildProcessPerExecutionEnableFraction(
+                sampling_profiler::ProfilerProcessType::kUnknown));
 }
 
-MAYBE_PLATFORM_CONFIG_TEST_F(ThreadProfilerPlatformConfigurationTest,
-                             IsEnabledForThread) {
+TEST_P(ThreadProfilerPlatformConfigurationThreadTest, IsEnabledForThread) {
   // Profiling should be enabled without restriction across all threads,
   // assuming it is enabled for corresponding process. Not all these
   // combinations actually make sense or are implemented in the code, but
   // iterating over all combinations is the simplest way to test.
-  for (int i = 0;
-       i <= static_cast<int>(sampling_profiler::ProfilerProcessType::kMax);
-       ++i) {
-    const auto process = static_cast<sampling_profiler::ProfilerProcessType>(i);
-    for (int j = 0;
-         j <= static_cast<int>(sampling_profiler::ProfilerThreadType::kMax);
-         ++j) {
-      const auto thread = static_cast<sampling_profiler::ProfilerThreadType>(j);
-      // TODO(crbug.com/40226611): Remove exception once ThreadPoolWorker
-      // profile sampling is enabled.
-      if (thread == sampling_profiler::ProfilerThreadType::kThreadPoolWorker) {
-        // Skip checking kThreadPoolWorker threads.
-        continue;
-      }
-
-      EXPECT_TRUE(config()->IsEnabledForThread(process, thread,
-                                               version_info::Channel::CANARY));
-
-#if BUILDFLAG(IS_ANDROID)
-      auto android_config1 = ThreadProfilerPlatformConfiguration::Create(
-          /* browser_test_mode_enabled=*/false,
-          base::BindLambdaForTesting([](double probability) { return true; }));
-      EXPECT_TRUE(android_config1->IsEnabledForThread(
-          process, thread, version_info::Channel::DEV));
-      auto android_config2 = ThreadProfilerPlatformConfiguration::Create(
-          /* browser_test_mode_enabled=*/false,
-          base::BindLambdaForTesting([](double probability) { return false; }));
-      EXPECT_FALSE(android_config2->IsEnabledForThread(
-          process, thread, version_info::Channel::DEV));
-#else
-      EXPECT_TRUE(config()->IsEnabledForThread(process, thread,
-                                               version_info::Channel::DEV));
+  using ProcessTypes =
+      base::EnumSet<sampling_profiler::ProfilerProcessType,
+                    sampling_profiler::ProfilerProcessType::kUnknown,
+                    sampling_profiler::ProfilerProcessType::kMax>;
+  using ThreadTypes =
+      base::EnumSet<sampling_profiler::ProfilerThreadType,
+                    sampling_profiler::ProfilerThreadType::kUnknown,
+                    sampling_profiler::ProfilerThreadType::kMax>;
+  for (const auto process : ProcessTypes::All()) {
+    for (const auto thread : ThreadTypes::All()) {
+      SCOPED_TRACE(::testing::Message()
+                   << "process type " << static_cast<int>(process)
+                   << ", thread type " << static_cast<int>(thread));
+      bool thread_type_enabled =
+          thread == sampling_profiler::ProfilerThreadType::kThreadPoolWorker
+              ? enable_worker_threads()
+              : true;
+      EXPECT_EQ(config()->IsEnabledForThread(process, thread,
+                                             version_info::Channel::CANARY),
+                thread_type_enabled);
+#if !BUILDFLAG(IS_ANDROID)
+      // Dev channel only has special handling on Android.
+      ASSERT_TRUE(enable_on_dev_channel());
 #endif
+      EXPECT_EQ(config()->IsEnabledForThread(process, thread,
+                                             version_info::Channel::DEV),
+                thread_type_enabled && enable_on_dev_channel());
     }
   }
 }
-
-// TODO(crbug.com/40226611): Remove test once ThreadPoolWorker profile sampling
-// is enabled.
-MAYBE_PLATFORM_CONFIG_TEST_F(ThreadProfilerPlatformConfigurationTest,
-                             IsDisabledForThreadPoolWorkerThread) {
-  for (int i = 0;
-       i <= static_cast<int>(sampling_profiler::ProfilerProcessType::kMax);
-       ++i) {
-    const auto process = static_cast<sampling_profiler::ProfilerProcessType>(i);
-    const auto thread =
-        sampling_profiler::ProfilerThreadType::kThreadPoolWorker;
-
-    EXPECT_FALSE(config()->IsEnabledForThread(process, thread,
-                                              version_info::Channel::CANARY));
-    EXPECT_FALSE(config()->IsEnabledForThread(process, thread,
-                                              version_info::Channel::DEV));
-  }
-}
diff --git a/chrome/common/url_constants.h b/chrome/common/url_constants.h
index d941805..69ee2726 100644
--- a/chrome/common/url_constants.h
+++ b/chrome/common/url_constants.h
@@ -588,11 +588,6 @@
 
 // The URL for the Settings page to enable history search.
 inline constexpr char16_t kHistorySearchSettingURL[] =
-    u"chrome://settings/historySearch";
-
-// The URL for the Settings page to enable history search when
-// AiSettingsPageRefresh flag is enabled.
-inline constexpr char16_t kHistorySearchV2SettingURL[] =
     u"chrome://settings/ai/historySearch";
 
 // The URL for the "Learn more" page for Wallpaper Search.
diff --git a/chrome/common/webui_url_constants.h b/chrome/common/webui_url_constants.h
index 53a6bba..ba85be1 100644
--- a/chrome/common/webui_url_constants.h
+++ b/chrome/common/webui_url_constants.h
@@ -636,7 +636,6 @@
 inline constexpr char kLanguageOptionsSubPage[] = "languages";
 inline constexpr char kLanguagesSubPage[] = "languages/details";
 inline constexpr char kManageProfileSubPage[] = "manageProfile";
-inline constexpr char kOfferWritingHelpSubpage[] = "content/offerWritingHelp";
 inline constexpr char kAiHelpMeWriteSubpage[] = "ai/helpMeWrite";
 inline constexpr char kOnDeviceSiteDataSubpage[] = "content/siteData";
 inline constexpr char kOnStartupSubPage[] = "onStartup";
diff --git a/chrome/installer/mac/internal b/chrome/installer/mac/internal
index f6030c4..be25bba 160000
--- a/chrome/installer/mac/internal
+++ b/chrome/installer/mac/internal
@@ -1 +1 @@
-Subproject commit f6030c49cc22768ce47357e5a35e872cec925a65
+Subproject commit be25bbacee6f69ac6f42e248354047cd048d66cf
diff --git a/chrome/installer/setup/launch_chrome.cc b/chrome/installer/setup/launch_chrome.cc
index d02bb4d0..52dfb22 100644
--- a/chrome/installer/setup/launch_chrome.cc
+++ b/chrome/installer/setup/launch_chrome.cc
@@ -9,6 +9,7 @@
 #include "base/logging.h"
 #include "base/process/launch.h"
 #include "base/process/process.h"
+#include "base/win/elevation_util.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/installer/util/util_constants.h"
 
@@ -39,7 +40,7 @@
   base::CommandLine cmd(application_path.Append(kChromeExe));
   cmd.AppendArguments(options, false);
 
-  base::Process chrome_handle = base::LaunchProcess(cmd, base::LaunchOptions());
+  base::Process chrome_handle = base::win::RunDeElevated(cmd);
   if (!chrome_handle.IsValid()) {
     PLOG(ERROR) << "Failed to launch: " << cmd.GetCommandLineString();
     return false;
diff --git a/chrome/renderer/accessibility/read_anything/read_anything_app_controller.cc b/chrome/renderer/accessibility/read_anything/read_anything_app_controller.cc
index 8326008..7375d5c7 100644
--- a/chrome/renderer/accessibility/read_anything/read_anything_app_controller.cc
+++ b/chrome/renderer/accessibility/read_anything/read_anything_app_controller.cc
@@ -1034,8 +1034,6 @@
       .SetMethod("getCurrentTextEndIndex",
                  &ReadAnythingAppController::GetCurrentTextEndIndex)
       .SetMethod("getCurrentText", &ReadAnythingAppController::GetCurrentText)
-      .SetMethod("preprocessTextForSpeech",
-                 &ReadAnythingAppController::PreprocessTextForSpeech)
       .SetMethod("shouldShowUi", &ReadAnythingAppController::ShouldShowUI)
       .SetMethod("onSpeechPlayingStateChanged",
                  &ReadAnythingAppController::OnSpeechPlayingStateChanged)
@@ -1802,6 +1800,9 @@
     const ui::AXNodeID& starting_node_id) {
   ui::AXNode* ax_node = model_.GetAXNode(starting_node_id);
   read_aloud_model_.InitAXPositionWithNode(ax_node, model_.active_tree_id());
+  // TODO: crbug.com/411198154: This should only be called if the ax position
+  // is not already initialized.
+  PreprocessTextForSpeech();
 }
 
 std::vector<ui::AXNodeID> ReadAnythingAppController::GetCurrentText() {
diff --git a/chrome/renderer/accessibility/read_anything/read_anything_app_controller_browsertest.cc b/chrome/renderer/accessibility/read_anything/read_anything_app_controller_browsertest.cc
index 3ddbe20a..e255856 100644
--- a/chrome/renderer/accessibility/read_anything/read_anything_app_controller_browsertest.cc
+++ b/chrome/renderer/accessibility/read_anything/read_anything_app_controller_browsertest.cc
@@ -1999,6 +1999,37 @@
   Mock::VerifyAndClearExpectations(distiller_);
 }
 
+TEST_F(ReadAnythingAppControllerTest,
+       InitAXPositionWithNode_PreprocessesTextForSpeech) {
+  // Text indices:             0123456789012345678901234567890
+  std::u16string sentence1 = u"Never feel heavy ";
+  std::u16string sentence2 = u"or earthbound, ";
+  std::u16string sentence3 = u"no worries or doubts interfere.";
+
+  static constexpr ui::AXNodeID kId1 = 2;
+  static constexpr ui::AXNodeID kId2 = 3;
+  static constexpr ui::AXNodeID kId3 = 4;
+  ui::AXNodeData static_text1 = test::TextNode(kId1, sentence1);
+  ui::AXNodeData static_text2 = test::TextNode(kId2, sentence2);
+  ui::AXNodeData static_text3 = test::TextNode(kId3, sentence3);
+
+  EXPECT_THAT(read_aloud_model().GetHighlightForCurrentSegmentIndex(1, false),
+              IsEmpty());
+
+  InitializeWithAndProcessNodes({std::move(static_text1),
+                                 std::move(static_text2),
+                                 std::move(static_text3)});
+
+  // After initializing, GetHighlightForCurrentSegmentIndex should return
+  // highlights, since this means text was preprocessed.
+  EXPECT_EQ(
+      read_aloud_model().GetHighlightForCurrentSegmentIndex(1, false).size(),
+      1u);
+
+  std::vector<ui::AXNodeID> node_ids = controller().GetCurrentText();
+  EXPECT_EQ(node_ids.size(), 3u);
+}
+
 TEST_F(ReadAnythingAppControllerTest, ScrollToTargetNode_ScrollsIfGoogleDocs) {
   ui::AXNodeData root;
   ui::AXNodeData node;
diff --git a/chrome/renderer/actor/click_tool.cc b/chrome/renderer/actor/click_tool.cc
index 3cd8385b..5229023d 100644
--- a/chrome/renderer/actor/click_tool.cc
+++ b/chrome/renderer/actor/click_tool.cc
@@ -23,67 +23,24 @@
 #include "third_party/blink/public/web/web_frame_widget.h"
 #include "third_party/blink/public/web/web_local_frame.h"
 #include "third_party/blink/public/web/web_node.h"
-#include "ui/events/base_event_utils.h"
 #include "ui/gfx/geometry/point_f.h"
 #include "ui/latency/latency_info.h"
 
-namespace {
-constexpr base::TimeDelta kClickDelay = base::Milliseconds(50);
-}
-
 namespace actor {
 
-using blink::WebCoalescedInputEvent;
-using blink::WebFormControlElement;
-using blink::WebInputEvent;
-using blink::WebInputEventResult;
-using blink::WebMouseEvent;
-using blink::WebNode;
+using ::blink::WebCoalescedInputEvent;
+using ::blink::WebFormControlElement;
+using ::blink::WebFrameWidget;
+using ::blink::WebInputEvent;
+using ::blink::WebInputEventResult;
+using ::blink::WebMouseEvent;
+using ::blink::WebNode;
 
 ClickTool::ClickTool(mojom::ClickActionPtr action, content::RenderFrame& frame)
     : frame_(frame), action_(std::move(action)) {}
 
 ClickTool::~ClickTool() = default;
 
-WebMouseEvent ClickTool::CreateClickMouseEvent(mojom::ClickAction::Type type,
-                                               mojom::ClickAction::Count count,
-                                               WebInputEvent::Type event_type,
-                                               const gfx::PointF& click_point) {
-  WebMouseEvent mouse_event(event_type, WebInputEvent::kNoModifiers,
-                            ui::EventTimeForNow());
-
-  switch (type) {
-    case mojom::ClickAction::Type::kLeft: {
-      mouse_event.button = WebMouseEvent::Button::kLeft;
-      break;
-    }
-    case mojom::ClickAction::Type::kRight: {
-      mouse_event.button = WebMouseEvent::Button::kRight;
-      break;
-    }
-  }
-
-  switch (count) {
-    case mojom::ClickAction::Count::kSingle: {
-      mouse_event.click_count = 1;
-      break;
-    }
-    case mojom::ClickAction::Count::kDouble: {
-      mouse_event.click_count = 2;
-      break;
-    }
-  }
-
-  mouse_event.SetPositionInWidget(click_point);
-
-  // TODO(crbug.com/402082828): Find a way to set screen position.
-  //   const gfx::Rect offset =
-  //     render_frame_host_->GetRenderWidgetHost()->GetView()->GetViewBounds();
-  //   mouse_event_.SetPositionInScreen(point.x() + offset.x(),
-  //                                    point.y() + offset.y());
-  return mouse_event;
-}
-
 void ClickTool::Execute(ToolFinishedCallback callback) {
   std::optional<gfx::PointF> click_point = ValidateAndGetClickPoint();
   if (!click_point) {
@@ -92,37 +49,33 @@
     return;
   }
 
-  // Create and send MouseDown event
-  WebMouseEvent mouse_down = CreateClickMouseEvent(
-      action_->type, action_->count, WebInputEvent::Type::kMouseDown,
-      click_point.value());
-  WebMouseEvent mouse_up = mouse_down;
-  WebInputEventResult result =
-      frame_->GetWebFrame()->FrameWidget()->HandleInputEvent(
-          WebCoalescedInputEvent(mouse_down, ui::LatencyInfo()));
-
-  if (result == WebInputEventResult::kHandledSuppressed) {
-    std::move(callback).Run(
-        MakeResult(mojom::ActionResultCode::kClickSuppressed));
-    return;
+  WebMouseEvent::Button button;
+  switch (action_->type) {
+    case mojom::ClickAction::Type::kLeft: {
+      button = WebMouseEvent::Button::kLeft;
+      break;
+    }
+    case mojom::ClickAction::Type::kRight: {
+      button = WebMouseEvent::Button::kRight;
+      break;
+    }
+  }
+  int click_count;
+  switch (action_->count) {
+    case mojom::ClickAction::Count::kSingle: {
+      click_count = 1;
+      break;
+    }
+    case mojom::ClickAction::Count::kDouble: {
+      click_count = 2;
+      break;
+    }
   }
 
-  mouse_up.SetType(WebInputEvent::Type::kMouseUp);
-  mouse_up.SetTimeStamp(mouse_down.TimeStamp() + kClickDelay);
-
-  // TODO(crbug.com/402082828): Delay the mouse up to simulate natural click
-  // after ToolExecutor lifetime update.
-
-  result = frame_->GetWebFrame()->FrameWidget()->HandleInputEvent(
-      WebCoalescedInputEvent(std::move(mouse_up), ui::LatencyInfo()));
-
-  if (result == WebInputEventResult::kHandledSuppressed) {
-    std::move(callback).Run(
-        MakeResult(mojom::ActionResultCode::kClickSuppressed));
-    return;
-  }
-
-  std::move(callback).Run(MakeOkResult());
+  mojom::ActionResultPtr result =
+      CreateAndDispatchClick(button, click_count, click_point.value(),
+                             frame_->GetWebFrame()->FrameWidget());
+  std::move(callback).Run(std::move(result));
 }
 
 std::string ClickTool::DebugString() const {
diff --git a/chrome/renderer/actor/click_tool.h b/chrome/renderer/actor/click_tool.h
index 13047c2..aa6d3f2 100644
--- a/chrome/renderer/actor/click_tool.h
+++ b/chrome/renderer/actor/click_tool.h
@@ -10,7 +10,6 @@
 #include "base/memory/raw_ref.h"
 #include "chrome/common/actor.mojom.h"
 #include "chrome/renderer/actor/tool_base.h"
-#include "third_party/blink/public/common/input/web_input_event.h"
 
 namespace blink {
 class WebMouseEvent;
@@ -39,12 +38,6 @@
  private:
   std::optional<gfx::PointF> ValidateAndGetClickPoint() const;
 
-  blink::WebMouseEvent CreateClickMouseEvent(
-      const mojom::ClickAction::Type type,
-      const mojom::ClickAction::Count count,
-      blink::WebInputEvent::Type event_type,
-      const gfx::PointF& click_point);
-
   void SendMouseUp(blink::WebMouseEvent mouse_event,
                    ToolFinishedCallback callback);
 
diff --git a/chrome/renderer/actor/tool_utils.cc b/chrome/renderer/actor/tool_utils.cc
index c20f03e..3bcade9 100644
--- a/chrome/renderer/actor/tool_utils.cc
+++ b/chrome/renderer/actor/tool_utils.cc
@@ -7,17 +7,32 @@
 #include <sstream>
 
 #include "chrome/common/actor.mojom.h"
+#include "chrome/common/actor/action_result.h"
 #include "content/public/renderer/render_frame.h"
+#include "third_party/blink/public/common/input/web_coalesced_input_event.h"
 #include "third_party/blink/public/web/web_element.h"
 #include "third_party/blink/public/web/web_frame_widget.h"
 #include "third_party/blink/public/web/web_local_frame.h"
 #include "third_party/blink/public/web/web_node.h"
+#include "ui/events/base_event_utils.h"
 #include "ui/gfx/geometry/point_conversions.h"
 #include "ui/gfx/geometry/point_f.h"
 #include "ui/gfx/geometry/rect.h"
+#include "ui/latency/latency_info.h"
+
+namespace {
+constexpr base::TimeDelta kClickDelay = base::Milliseconds(50);
+}
 
 namespace actor {
 
+using ::blink::WebCoalescedInputEvent;
+using ::blink::WebFrameWidget;
+using ::blink::WebInputEvent;
+using ::blink::WebInputEventResult;
+using ::blink::WebMouseEvent;
+using ::blink::WebNode;
+
 std::optional<gfx::PointF> InteractionPointFromWebNode(
     const blink::WebNode& node) {
   blink::WebElement element = node.DynamicTo<blink::WebElement>();
@@ -80,4 +95,43 @@
   return ss.str();
 }
 
+mojom::ActionResultPtr CreateAndDispatchClick(WebMouseEvent::Button button,
+                                              int count,
+                                              const gfx::PointF& click_point,
+                                              WebFrameWidget* widget) {
+  WebMouseEvent mouse_down(WebInputEvent::Type::kMouseDown,
+                           WebInputEvent::kNoModifiers, ui::EventTimeForNow());
+  mouse_down.button = button;
+  mouse_down.click_count = count;
+  mouse_down.SetPositionInWidget(click_point);
+  // TODO(crbug.com/402082828): Find a way to set screen position.
+  //   const gfx::Rect offset =
+  //     render_frame_host_->GetRenderWidgetHost()->GetView()->GetViewBounds();
+  //   mouse_event_.SetPositionInScreen(point.x() + offset.x(),
+  //                                    point.y() + offset.y());
+
+  WebMouseEvent mouse_up = mouse_down;
+  WebInputEventResult result = widget->HandleInputEvent(
+      WebCoalescedInputEvent(mouse_down, ui::LatencyInfo()));
+
+  if (result == WebInputEventResult::kHandledSuppressed) {
+    return MakeResult(mojom::ActionResultCode::kClickSuppressed);
+  }
+
+  mouse_up.SetType(WebInputEvent::Type::kMouseUp);
+  mouse_up.SetTimeStamp(mouse_down.TimeStamp() + kClickDelay);
+
+  // TODO(crbug.com/402082828): Delay the mouse up to simulate natural click
+  // after ToolExecutor lifetime update.
+
+  result = widget->HandleInputEvent(
+      WebCoalescedInputEvent(std::move(mouse_up), ui::LatencyInfo()));
+
+  if (result == WebInputEventResult::kHandledSuppressed) {
+    return MakeResult(mojom::ActionResultCode::kClickSuppressed);
+  }
+
+  return MakeOkResult();
+}
+
 }  // namespace actor
diff --git a/chrome/renderer/actor/tool_utils.h b/chrome/renderer/actor/tool_utils.h
index 1eb0e715..2b7edb05 100644
--- a/chrome/renderer/actor/tool_utils.h
+++ b/chrome/renderer/actor/tool_utils.h
@@ -10,9 +10,12 @@
 #include <string>
 
 #include "chrome/common/actor.mojom-forward.h"
+#include "third_party/blink/public/common/input/web_input_event.h"
+#include "third_party/blink/public/common/input/web_mouse_event.h"
 
 namespace blink {
 class WebNode;
+class WebFrameWidget;
 }  // namespace blink
 
 namespace content {
@@ -43,6 +46,14 @@
 
 std::string ToDebugString(const mojom::ToolTargetPtr& target);
 
+// Create and dispatch the mouse down event and corresponding mouse up, click
+// event to the widget.
+mojom::ActionResultPtr CreateAndDispatchClick(
+    blink::WebMouseEvent::Button button,
+    int count,
+    const gfx::PointF& click_point,
+    blink::WebFrameWidget* widget);
+
 }  // namespace actor
 
 #endif  // CHROME_RENDERER_ACTOR_TOOL_UTILS_H_
diff --git a/chrome/renderer/actor/type_tool.cc b/chrome/renderer/actor/type_tool.cc
index 9956f77..ecf12c1 100644
--- a/chrome/renderer/actor/type_tool.cc
+++ b/chrome/renderer/actor/type_tool.cc
@@ -14,6 +14,7 @@
 #include "chrome/common/actor.mojom-shared.h"
 #include "chrome/common/actor/action_result.h"
 #include "chrome/common/actor/actor_logging.h"
+#include "chrome/renderer/actor/click_tool.h"
 #include "chrome/renderer/actor/tool_utils.h"
 #include "content/public/renderer/render_frame.h"
 #include "third_party/abseil-cpp/absl/strings/str_format.h"
@@ -23,6 +24,7 @@
 #include "third_party/blink/public/platform/web_input_event_result.h"
 #include "third_party/blink/public/web/web_element.h"
 #include "third_party/blink/public/web/web_frame_widget.h"
+#include "third_party/blink/public/web/web_hit_test_result.h"
 #include "third_party/blink/public/web/web_input_element.h"
 #include "third_party/blink/public/web/web_local_frame.h"
 #include "third_party/blink/public/web/web_node.h"
@@ -39,6 +41,7 @@
 using ::blink::WebInputEventResult;
 using ::blink::WebKeyboardEvent;
 using ::blink::WebLocalFrame;
+using ::blink::WebMouseEvent;
 using ::blink::WebNode;
 using ::blink::WebString;
 
@@ -100,10 +103,15 @@
   return *key_info_map;
 }
 
-bool PrepareTargetForMode(WebLocalFrame& frame, mojom::TypeAction::Mode mode) {
-  // TODO(crbug.com/409570203): Use DELETE_EXISTING regardless of `mode` but
-  // we'll have to implement the different insertion modes.
-  frame.ExecuteCommand(WebString::FromUTF8("SelectAll"));
+bool PrepareTargetForMode(WebLocalFrame& frame,
+                          mojom::TypeAction::Mode mode,
+                          bool is_target_editable) {
+  // Skip prepration if target is not editable.
+  if (is_target_editable) {
+    // TODO(crbug.com/409570203): Use DELETE_EXISTING regardless of `mode` but
+    // we'll have to implement the different insertion modes.
+    frame.ExecuteCommand(WebString::FromUTF8("SelectAll"));
+  }
   return true;
 }
 
@@ -246,58 +254,80 @@
   mojom::ToolTargetPtr& target = action_->target;
   CHECK(target);
 
+  bool is_target_editable;
+
   if (target->is_coordinate()) {
-    NOTIMPLEMENTED() << "Coordinate-based target not yet supported.";
-    std::move(callback).Run(MakeErrorResult());
-    return;
-  }
-  int32_t dom_node_id = target->get_dom_node_id();
-
-  WebNode node = GetNodeFromId(frame_.get(), dom_node_id);
-  if (node.IsNull()) {
-    ACTOR_LOG() << "Cannot find dom node with id " << dom_node_id;
-    std::move(callback).Run(MakeErrorResult());
-    return;
-  }
-
-  // Validate Node is an editable element
-  // TODO(crbug.com/414398425): This seems too restrictive for non-input cases.
-  if (!node.IsElementNode()) {
-    ACTOR_LOG() << "Target node " << node << " is not an element.";
-    std::move(callback).Run(MakeErrorResult());
-    return;
-  }
-  WebElement element = node.To<WebElement>();
-  if (!element.IsEditable()) {
-    ACTOR_LOG() << "Target element " << element << " is not editable.";
-    std::move(callback).Run(MakeErrorResult());
-    return;
-  }
-
-  // Check and set focus if needed.
-  if (!IsNodeFocused(frame_.get(), node)) {
-    if (element.IsFocusable()) {
-      element.Focus();
-    } else {
-      ACTOR_LOG() << "Target element " << element
-                  << " is not focusable for typing.";
+    // Injecting a click first at the coordinate.
+    gfx::PointF click_point(target->get_coordinate());
+    if (!IsPointWithinViewport(click_point, frame_.get())) {
+      std::move(callback).Run(
+          MakeResult(mojom::ActionResultCode::kClickInvalidPoint));
+      return;
+    }
+    mojom::ActionResultPtr result = CreateAndDispatchClick(
+        blink::WebMouseEvent::Button::kLeft, 1, click_point,
+        frame_->GetWebFrame()->FrameWidget());
+    // Cancel rest of typing if initial click failed.
+    if (!IsOk(*result)) {
+      std::move(callback).Run(std::move(result));
+      return;
+    }
+    blink::WebHitTestResult htresult =
+        frame_->GetWebFrame()->FrameWidget()->HitTestResultAt(click_point);
+    // Only prepare target if the hit test result indicates the node is
+    // editable.
+    is_target_editable = htresult.IsContentEditable();
+  } else {
+    int32_t dom_node_id = target->get_dom_node_id();
+    WebNode node = GetNodeFromId(frame_.get(), dom_node_id);
+    if (node.IsNull()) {
+      ACTOR_LOG() << "Cannot find dom node with id " << dom_node_id;
       std::move(callback).Run(MakeErrorResult());
       return;
     }
+
+    // Validate Node is an editable element
+    // TODO(crbug.com/414398425): This seems too restrictive for non-input
+    // cases.
+    if (!node.IsElementNode()) {
+      ACTOR_LOG() << "Target node " << node << " is not an element.";
+      std::move(callback).Run(MakeErrorResult());
+      return;
+    }
+    WebElement element = node.To<WebElement>();
+    if (!element.IsEditable()) {
+      ACTOR_LOG() << "Target element " << element << " is not editable.";
+      std::move(callback).Run(MakeErrorResult());
+      return;
+    }
+
+    // Check and set focus if needed.
+    if (!IsNodeFocused(frame_.get(), node)) {
+      if (element.IsFocusable()) {
+        element.Focus();
+      } else {
+        ACTOR_LOG() << "Target element " << element
+                    << " is not focusable for typing.";
+        std::move(callback).Run(MakeErrorResult());
+        return;
+      }
+    }
+    is_target_editable = true;
   }
 
-  if (!PrepareTargetForMode(*frame_->GetWebFrame(), action_->mode)) {
+  if (!PrepareTargetForMode(*frame_->GetWebFrame(), action_->mode,
+                            is_target_editable)) {
     ACTOR_LOG() << "Failed to prepare target element based on mode: "
                 << action_->mode;
     std::move(callback).Run(MakeErrorResult());
     return;
   }
 
-  // Note: Focus and preparing the target performs actions which lead to script
-  // execution so `node` may no longer be focused (it or its frame could be
-  // disconnected). However, sites sometimes do unexpected things to work around
-  // issues so to keep those working we proceed to key dispatch without checking
-  // this.
+  // Note: Focus and preparing the target performs actions which lead to
+  // script execution so `node` may no longer be focused (it or its frame
+  // could be disconnected). However, sites sometimes do unexpected things to
+  // work around issues so to keep those working we proceed to key dispatch
+  // without checking this.
 
   if (!base::IsStringASCII(action_->text)) {
     // TODO(crbug.com/409032824): Add support beyond ASCII.
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 52343b01..e6566c5 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -15,7 +15,6 @@
 import("//chrome/browser/downgrade/buildflags.gni")
 import("//chrome/browser/page_load_metrics/integration_tests/jsdeps.gni")
 import("//chrome/browser/page_load_metrics/integration_tests/sources.gni")
-import("//chrome/browser/sync/test/integration/sync_integration_tests.gni")
 import("//chrome/common/features.gni")
 import("//chrome/services/speech/buildflags/buildflags.gni")
 import("//chrome/test/include_js_tests.gni")
@@ -2003,19 +2002,15 @@
   }
 
   test("android_sync_integration_tests") {
-    configs += [ "//build/config:precompiled_headers" ]
-    defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
     use_default_launcher = false
     android_manifest =
         "${target_gen_dir}/android_browsertests_manifest/AndroidManifest.xml"
     android_manifest_dep = ":android_browsertests_manifest"
 
-    deps = android_sync_integration_tests_deps + [
-             "//chrome/browser/sync",
-             "//chrome/browser/ui:ui_features",
-           ]
-    sources = sync_integration_tests_sources
-    data = android_sync_integration_tests_data
+    deps = [
+      "//chrome/browser/sync/test/integration:sync_integration_tests_impl",
+      "//chrome/browser/sync/test/integration:sync_integration_tests_java",
+    ]
   }
 
   static_library("test_support_ui_android") {
@@ -2324,6 +2319,7 @@
       "//chrome/browser/ui/safety_hub",
       "//chrome/browser/ui/safety_hub:browser_tests",
       "//chrome/browser/ui/safety_hub:test_support",
+      "//chrome/browser/ui/search:browser_tests",
       "//chrome/browser/ui/search_engines:browser_tests",
       "//chrome/browser/ui/signin",
       "//chrome/browser/ui/signin:browser_tests",
@@ -3412,10 +3408,6 @@
       "../browser/ui/profiles/profile_error_browsertest.cc",
       "../browser/ui/promos/ios_promos_utils_browsertest.cc",
       "../browser/ui/renderer_event_injection_browsertest.cc",
-      "../browser/ui/search/instant_test_base.cc",
-      "../browser/ui/search/instant_test_base.h",
-      "../browser/ui/search/new_tab_page_navigation_throttle_browsertest.cc",
-      "../browser/ui/search/third_party_ntp_browsertest.cc",
       "../browser/ui/startup/startup_browser_creator_browsertest.cc",
       "../browser/ui/sync/one_click_signin_links_delegate_impl_browsertest.cc",
       "../browser/ui/tab_modal_confirm_dialog_browsertest.cc",
@@ -3708,6 +3700,7 @@
       "//chrome/browser/apps/link_capturing:app_service_browser_tests",
       "//chrome/browser/contextual_cueing",
       "//chrome/browser/contextual_cueing:impl",
+      "//chrome/browser/ui/search",
       "//chrome/browser/ui/web_applications:app_service_browser_tests",
       "//chrome/browser/web_applications:app_service_browser_tests",
       "//components/live_caption:live_translate",
@@ -4994,6 +4987,7 @@
         "../browser/apps/app_service/metrics/website_metrics_browsertest.cc",
         "../browser/apps/app_service/notifications_browsertest.cc",
         "../browser/apps/app_service/publishers/chrome_app_deprecation_browsertest.cc",
+        "../browser/apps/app_service/publishers/chrome_app_deprecation_kiosk_browsertest.cc",
         "../browser/apps/app_service/publishers/extension_apps_chromeos_browsertest.cc",
         "../browser/apps/app_service/webapk/webapk_policy_browsertest.cc",
         "../browser/apps/platform_apps/app_window_interactive_uitest_base.cc",
@@ -7885,9 +7879,6 @@
       "../browser/ui/performance_controls/tab_resource_usage_tab_helper_unittest.cc",
       "../browser/ui/profiles/profile_view_utils_unittest.cc",
       "../browser/ui/recently_audible_helper_unittest.cc",
-      "../browser/ui/search/ntp_user_data_logger_unittest.cc",
-      "../browser/ui/search/search_ipc_router_policy_unittest.cc",
-      "../browser/ui/search/search_ipc_router_unittest.cc",
       "../browser/ui/singleton_tabs_unittest.cc",
       "../browser/ui/startup/launch_mode_recorder_unittest.cc",
       "../browser/ui/sync/profile_signin_confirmation_helper_unittest.cc",
@@ -8081,12 +8072,14 @@
       "//chrome/browser/task_manager/common",
       "//chrome/browser/ui/bookmarks:unit_tests",
       "//chrome/browser/ui/safety_hub",
+      "//chrome/browser/ui/search:unit_tests",
       "//chrome/browser/ui/startup:startup_tab",
       "//chrome/browser/ui/tabs:tab_group",
       "//chrome/browser/ui/tabs:tab_model",
       "//chrome/browser/ui/tabs:tab_strip",
       "//chrome/browser/ui/tabs:test_support",
       "//chrome/browser/ui/tabs:unit_tests",
+      "//chrome/browser/ui/views/new_tab_footer:unit_tests",
       "//chrome/browser/ui/views/page_action:unit_tests",
       "//chrome/browser/ui/views/webid:test_support",
       "//chrome/browser/ui/webui:webui_util",
@@ -10911,10 +10904,6 @@
       "../browser/ui/fullscreen_keyboard_browsertest_base.cc",
       "../browser/ui/fullscreen_keyboard_browsertest_base.h",
       "../browser/ui/keyboard_lock_interactive_browsertest.cc",
-      "../browser/ui/search/instant_extended_interactive_uitest.cc",
-      "../browser/ui/search/instant_test_base.cc",
-      "../browser/ui/search/instant_test_base.h",
-      "../browser/ui/search/third_party_ntp_uitest.cc",
       "../browser/ui/startup/invalid_user_data_dir_interactive_uitest.cc",
       "../browser/ui/startup/startup_browser_creator_interactive_uitest.cc",
       "../browser/ui/tabs/organization/tab_declutter_controller_interactive_uitest.cc",
@@ -11074,6 +11063,7 @@
       "//chrome/browser/ui/page_action:icon_type",
       "//chrome/browser/ui/plus_addresses:interactive_ui_tests",
       "//chrome/browser/ui/privacy_sandbox",
+      "//chrome/browser/ui/search:interactive_ui_tests",
       "//chrome/browser/ui/startup:startup_tab",
       "//chrome/browser/ui/tab_contents",
       "//chrome/browser/ui/tabs:tab_strip",
@@ -12136,170 +12126,8 @@
 if (!is_android) {
   test("sync_integration_tests") {
     use_xvfb = use_xvfb_in_this_config
-
-    sources = sync_integration_tests_sources
-
-    data = [
-      "//chrome/test/data/password/",
-      "//chrome/test/data/sync/",
-      "//chrome/test/data/webapps_integration/",
-      "//chrome/test/data/web_apps/",
-      "//chrome/test/data/banners/",
-      "//net/tools/testserver/",
-      "//third_party/pywebsocket3/src/mod_pywebsocket/",
-    ]
-
-    # TODO(phajdan.jr): Only temporary, to make transition easier.
-    defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
-
-    deps = [
-      ":browser_tests_runner",
-      ":sync_integration_test_support",
-      ":test_support",
-      ":test_support_ui",
-      "//chrome:packed_resources",
-      "//chrome:resources",
-      "//chrome:strings",
-      "//chrome/app:chrome_dll_resources",
-      "//chrome/app:command_ids",
-      "//chrome/browser/affiliations",
-      "//chrome/browser/apps/link_capturing",
-      "//chrome/browser/autofill",
-      "//chrome/browser/browsing_data:constants",
-      "//chrome/browser/metrics/desktop_session_duration",
-      "//chrome/browser/plus_addresses",
-      "//chrome/browser/prefs",
-      "//chrome/browser/reading_list",
-      "//chrome/browser/search_engines",
-      "//chrome/browser/sync",
-      "//chrome/browser/themes",
-      "//chrome/browser/ui:browser_navigator_params_headers",
-      "//chrome/browser/ui:ui_features",
-      "//chrome/browser/web_applications:features",
-      "//chrome/browser/web_applications:web_applications_test_support",
-      "//chrome/common",
-      "//chrome/renderer",
-      "//components/app_constants",
-      "//components/bookmarks/browser",
-      "//components/bookmarks/test",
-      "//components/browser_sync",
-      "//components/commerce/core:feature_list",
-      "//components/consent_auditor",
-      "//components/data_sharing/public",
-      "//components/favicon/core",
-      "//components/history/content/browser",
-      "//components/history/core/common",
-      "//components/password_manager/core/browser/sharing",
-      "//components/plus_addresses",
-      "//components/plus_addresses:test_support",
-      "//components/plus_addresses/settings",
-      "//components/plus_addresses/settings:test_support",
-      "//components/plus_addresses/webdata",
-      "//components/power_bookmarks/common:test_support",
-      "//components/power_bookmarks/core",
-      "//components/reading_list/core",
-      "//components/reading_list/core:test_support",
-      "//components/saved_tab_groups/internal:tab_group_sync_bridge",
-      "//components/saved_tab_groups/test_support",
-      "//components/search_engines",
-      "//components/send_tab_to_self",
-      "//components/spellcheck/common",
-      "//components/sync",
-      "//components/sync:test_support",
-      "//components/sync_bookmarks",
-      "//components/sync_device_info:test_support",
-      "//components/sync_preferences:common_syncable_prefs_database",
-      "//components/trusted_vault",
-      "//components/trusted_vault:test_support",
-      "//components/undo",
-      "//components/version_info",
-      "//components/webapps/browser",
-      "//components/webapps/common",
-      "//components/webauthn/core/browser",
-      "//components/webauthn/core/browser:passkey_model",
-      "//testing/gmock",
-      "//testing/gtest",
-      "//third_party/blink/public:blink",
-      "//third_party/icu",
-      "//third_party/leveldatabase",
-    ]
-
-    data_deps = [
-      "//testing:test_scripts_shared",
-      "//testing/buildbot/filters:sync_integration_tests_filters",
-      "//third_party/angle:includes",
-    ]
-
-    if (is_linux || is_chromeos || is_win) {
-      data_deps += [ "//chrome:packed_resources" ]
-    }
-
-    if (is_mac) {
-      # Dictionary sync is disabled on Mac.
-      sources -= [
-        "//chrome/browser/sync/test/integration/single_client_dictionary_sync_test.cc",
-        "//chrome/browser/sync/test/integration/two_client_dictionary_sync_test.cc",
-      ]
-
-      data_deps += [
-        "//chrome",
-        "//chrome:chrome_framework",
-      ]
-    }
-    if (is_win) {
-      deps += [
-        "//chrome:other_version",
-        "//third_party/wtl",
-        "//ui/resources",
-      ]
-    }
-    if (is_chromeos) {
-      sources += [
-        "//chrome/browser/sync/test/integration/single_client_app_list_sync_test.cc",
-        "//chrome/browser/sync/test/integration/single_client_arc_package_sync_test.cc",
-        "//chrome/browser/sync/test/integration/single_client_cookies_sync_test.cc",
-        "//chrome/browser/sync/test/integration/single_client_os_preferences_sync_test.cc",
-        "//chrome/browser/sync/test/integration/single_client_printers_sync_test.cc",
-        "//chrome/browser/sync/test/integration/single_client_wifi_configuration_sync_test.cc",
-        "//chrome/browser/sync/test/integration/single_client_workspace_desk_sync_test.cc",
-        "//chrome/browser/sync/test/integration/two_client_app_list_sync_test.cc",
-        "//chrome/browser/sync/test/integration/two_client_arc_package_sync_test.cc",
-        "//chrome/browser/sync/test/integration/two_client_os_preferences_sync_test.cc",
-        "//chrome/browser/sync/test/integration/two_client_printers_sync_test.cc",
-        "//chrome/browser/sync/test/integration/two_client_workspace_desk_sync_test.cc",
-      ]
-      deps += [
-        "//chrome/browser/ash/app_list",
-        "//chrome/browser/ash/app_list/arc",
-        "//chrome/browser/ash/app_list/test:test_support",
-        "//chrome/browser/ash/crosapi",
-        "//chrome/browser/ash/floating_sso",
-        "//chrome/browser/ash/floating_sso:test_support",
-        "//chrome/browser/ash/printing",
-        "//chrome/browser/ash/sync",
-        "//chrome/browser/ash/system_web_apps/test_support",
-        "//chromeos/ash/components/browser_context_helper",
-        "//chromeos/ash/components/dbus/shill",
-        "//chromeos/ash/components/sync_wifi",
-        "//chromeos/ash/components/sync_wifi:test_support",
-        "//chromeos/printing",
-        "//chromeos/services/network_config/public/cpp",
-        "//components/desks_storage",
-      ]
-    }
-
-    if (toolkit_views) {
-      deps += [ "//ui/views" ]
-    }
-    if (enable_printing) {
-      deps += [ "//printing" ]
-    }
-    if (enable_glic) {
-      deps += [
-        "//chrome/browser/glic",
-        "//chrome/browser/glic/test_support",
-      ]
-    }
+    deps =
+        [ "//chrome/browser/sync/test/integration:sync_integration_tests_impl" ]
   }
 
   test("sync_performance_tests") {
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 e31287a3..7f57aa75 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
@@ -24,6 +24,7 @@
 import org.chromium.chrome.test.transit.page.NativePageCondition;
 import org.chromium.chrome.test.transit.page.PageStation;
 import org.chromium.components.embedder_support.util.UrlConstants;
+import org.chromium.components.omnibox.OmniboxFeatures;
 
 import java.util.List;
 
@@ -43,7 +44,8 @@
         declareElementFactory(
                 mActivityElement,
                 delayedElements -> {
-                    if (mActivityElement.get().isTablet()) {
+                    if (mActivityElement.get().isTablet()
+                            || OmniboxFeatures.sOmniboxMobileParityUpdate.isEnabled()) {
                         urlBarElement = delayedElements.declareView(URL_BAR);
                     } else {
                         delayedElements.declareNoView(URL_BAR);
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/omnibox/OmniboxFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/omnibox/OmniboxFacility.java
index 4c3e3152..5e1ce03 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/omnibox/OmniboxFacility.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/omnibox/OmniboxFacility.java
@@ -60,7 +60,10 @@
                     declareView(
                             ACTION_CONTAINER,
                             ViewElement.newOptions().unscoped().displayingAtLeast(50).build());
-            micButtonElement = declareView(MIC_BUTTON, ViewElement.displayingAtLeastOption(50));
+            micButtonElement =
+                    declareView(
+                            MIC_BUTTON,
+                            ViewElement.newOptions().unscoped().displayingAtLeast(50).build());
             declareNoView(DELETE_BUTTON);
         } else {
             if (mHostStation.getActivity().isTablet()) {
diff --git a/chrome/test/data/actor/type_input_coordinate.html b/chrome/test/data/actor/type_input_coordinate.html
new file mode 100644
index 0000000..2e1bda4
--- /dev/null
+++ b/chrome/test/data/actor/type_input_coordinate.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta name="viewport" content="width=device-width, minimum-scale=1.0">
+    <title>Page with text input for testing coordinate based target</title>
+    <style>
+      div {
+        margin-top: 8px;
+        background-color: #ddd;
+        border: 1px solid #000;
+        display: block;
+        width: 50px;
+        height: 50px;
+      }
+    </style>
+  </head>
+  <body>
+    <!-- method=dialog prevents the form from navigating the page -->
+    <form id="form" method="dialog">
+      <input class="input-event-test" type="text" name="input" id="input">
+      <input type="submit" id="submit">
+    </form>
+    <div id="editableDiv" contenteditable="true" class="input-event-test"></div>
+    <!-- tabindex="0" makes the div focusable -->
+    <div id="focusableDiv" tabindex="0" class="input-event-test"></div>
+    <div id="unfocusableDiv" class="input-event-test"></div>
+    <script>
+      let input_event_log = [];
+      let log_handler = e => {
+        if (e.type === 'mousedown' || e.type === 'mouseup' || e.type === 'click') {
+          input_event_log.push(`${e.type}(${e.clientX},${e.clientY})`);
+        } else {
+          input_event_log.push(`${e.type}`);
+        }
+      }
+      document.querySelectorAll(".input-event-test").forEach((node)=>{
+        node.addEventListener('change', log_handler);
+        node.addEventListener('input', log_handler);
+        node.addEventListener('keydown', log_handler);
+        node.addEventListener('keyup', log_handler);
+        node.addEventListener('click', log_handler);
+        node.addEventListener('mousedown', log_handler);
+        node.addEventListener('mouseup', log_handler);
+      });
+      document.getElementById('submit').addEventListener('click', log_handler);
+      let body_input_event_log = [];
+      let body_log_handler = e => {
+        body_input_event_log.push(`${e.type}`);
+      }
+      document.body.addEventListener('keydown', body_log_handler);
+      document.body.addEventListener('keyup', body_log_handler);
+    </script>
+  </body>
+</html>
diff --git a/chrome/test/data/media/picture-in-picture/document-pip.html b/chrome/test/data/media/picture-in-picture/document-pip.html
index 1ba7b1a..0c859715 100644
--- a/chrome/test/data/media/picture-in-picture/document-pip.html
+++ b/chrome/test/data/media/picture-in-picture/document-pip.html
@@ -39,4 +39,17 @@
     return true;
 }
 
+function getPipWindowPageTitle() {
+  return pipWindow.document.title;
+}
+
+function setPipWindowPageTitle(title) {
+  pipWindow.document.title = title;
+  return true;
+}
+
+function getWindowPageTitle() {
+  return document.title;
+}
+
 </script>
diff --git a/chrome/test/data/pdf/ink2_text_box_test.ts b/chrome/test/data/pdf/ink2_text_box_test.ts
index d6b8466..70b9cb4 100644
--- a/chrome/test/data/pdf/ink2_text_box_test.ts
+++ b/chrome/test/data/pdf/ink2_text_box_test.ts
@@ -4,7 +4,8 @@
 
 import {hexToColor, Ink2Manager, PluginController, PluginControllerEventType, TEXT_COLORS, TextAlignment, TextBoxState, TextStyle, TextTypeface} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
 import type {TextAnnotation} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
-import {isVisible, microtasksFinished} from 'chrome://webui-test/test_util.js';
+import {keyDownOn, keyUpOn} from 'chrome://webui-test/keyboard_mock_interactions.js';
+import {eventToPromise, isVisible, microtasksFinished} from 'chrome://webui-test/test_util.js';
 
 import {assertDeepEquals, getRequiredElement, setupTestViewportAndMockPluginForInk} from './test_util.js';
 
@@ -15,28 +16,44 @@
 const textbox = document.createElement('ink-text-box');
 document.body.appendChild(textbox);
 
+function getDefaultAnnotation(): TextAnnotation {
+  return {
+    text: 'Hello World',
+    textAttributes: {
+      size: 12,
+      typeface: TextTypeface.SANS_SERIF,
+      styles: {
+        [TextStyle.BOLD]: false,
+        [TextStyle.ITALIC]: false,
+      },
+      alignment: TextAlignment.LEFT,
+      color: hexToColor(TEXT_COLORS[0]!.color),
+    },
+    textBoxRect: {height: 100, locationX: 400, locationY: 300, width: 100},
+    textOrientation: 0,
+    id: 0,
+    pageNumber: 0,
+  };
+}
+
 function initializeBox(
     width: number, height: number, x: number, y: number, existing?: boolean,
     orientation?: number) {
+  const annotation = getDefaultAnnotation();
+  if (!existing) {
+    annotation.text = '';
+  }
+  if (orientation) {
+    annotation.textOrientation = orientation;
+  }
+  annotation.textBoxRect.locationX = x;
+  annotation.textBoxRect.locationY = y;
+  annotation.textBoxRect.width = width;
+  annotation.textBoxRect.height = height;
+
   manager.dispatchEvent(new CustomEvent('initialize-text-box', {
     detail: {
-      annotation: {
-        text: existing ? 'Hello World' : '',
-        textAttributes: {
-          size: 12,
-          typeface: TextTypeface.SANS_SERIF,
-          styles: {
-            [TextStyle.BOLD]: false,
-            [TextStyle.ITALIC]: false,
-          },
-          alignment: TextAlignment.LEFT,
-          color: hexToColor(TEXT_COLORS[0]!.color),
-        },
-        textBoxRect: {height, locationX: x, locationY: y, width},
-        textOrientation: orientation ? orientation : 0,
-        id: 0,
-        pageNumber: 0,
-      },
+      annotation,
       pageCoordinates: {x: 10, y: 3},
     },
   }));
@@ -74,6 +91,27 @@
   await microtasksFinished();
 }
 
+async function dragHandleWithKeyboard(
+    handle: HTMLElement, key: string, numEvents: number,
+    useFocusOut: boolean = false) {
+  for (let i = 0; i < numEvents; i++) {
+    keyDownOn(handle, 0, [], key);
+  }
+  if (useFocusOut) {
+    handle.dispatchEvent(new CustomEvent('focusout'));
+  } else {
+    keyUpOn(handle, 0, [], key);
+  }
+  await microtasksFinished();
+}
+
+function verifyFinishTextAnnotationMessage(expectedAnnotation: TextAnnotation) {
+  const message = mockPlugin.findMessage('finishTextAnnotation');
+  chrome.test.assertTrue(message !== undefined);
+  chrome.test.assertEq('finishTextAnnotation', message.type);
+  assertDeepEquals(expectedAnnotation, message.data);
+}
+
 chrome.test.runTests([
   // Test drawing the box based on data from the manager.
   async function testDrawsBox() {
@@ -551,32 +589,15 @@
     initializeBox(100, 100, 400, 300);
     await microtasksFinished();
     chrome.test.assertTrue(isVisible(textbox));
-    const testAnnotation: TextAnnotation = {
-      text: 'Hello World',
-      id: 0,
-      pageNumber: 0,
-      textAttributes: {
-        size: 12,
-        typeface: TextTypeface.SANS_SERIF,
-        styles: {
-          [TextStyle.BOLD]: false,
-          [TextStyle.ITALIC]: false,
-        },
-        alignment: TextAlignment.LEFT,
-        color: hexToColor(TEXT_COLORS[0]!.color),
-      },
-      // Messages to the backend are in page coordinates.
-      textBoxRect: {locationX: 195, locationY: 147, height: 50, width: 50},
-      textOrientation: 0,
-    };
+    const testAnnotation = getDefaultAnnotation();
+    // Messages to the backend are in page coordinates.
+    testAnnotation
+        .textBoxRect = {locationX: 195, locationY: 147, height: 50, width: 50};
 
     function startNewAnnotationAndVerifyMessage(existing: boolean = false) {
       mockPlugin.clearMessages();
       initializeBox(100, 100, 400, 300, existing);
-      const message = mockPlugin.findMessage('finishTextAnnotation');
-      chrome.test.assertTrue(message !== undefined);
-      chrome.test.assertEq('finishTextAnnotation', message.type);
-      assertDeepEquals(testAnnotation, message.data);
+      verifyFinishTextAnnotationMessage(testAnnotation);
     }
 
     textbox.$.textbox.value = testAnnotation.text;
@@ -657,6 +678,190 @@
     chrome.test.succeed();
   },
 
+  async function testResizeWithKeyboard() {
+    // Initialize to a 100x200 box at 400, 300.
+    initializeBox(100, 200, 400, 300);
+    await microtasksFinished();
+    chrome.test.assertFalse(textbox.hidden);
+    chrome.test.assertTrue(isVisible(textbox));
+    assertPositionAndSize(textbox, '100px', '200px', '400px', '300px');
+
+    // 5px up and left.
+    const topLeft = getRequiredElement(textbox, '.handle.top.left');
+    await dragHandleWithKeyboard(topLeft, 'ArrowUp', 5);
+    await dragHandleWithKeyboard(topLeft, 'ArrowLeft', 5);
+    assertPositionAndSize(textbox, '105px', '205px', '395px', '295px');
+
+    // Top handle 3px up and left. Left arrow is ignored on this handle.
+    const top = getRequiredElement(textbox, '.handle.top.center');
+    await dragHandleWithKeyboard(top, 'ArrowUp', 3);
+    await dragHandleWithKeyboard(top, 'ArrowLeft', 3);
+    assertPositionAndSize(textbox, '105px', '208px', '395px', '292px');
+
+    // Top right handle 4px down and right -> makes box shorter and wider.
+    // Use focusout event to finish right arrow drag to ensure it works.
+    const topRight = getRequiredElement(textbox, '.handle.top.right');
+    await dragHandleWithKeyboard(topRight, 'ArrowDown', 4);
+    await dragHandleWithKeyboard(topRight, 'ArrowRight', 4, true);
+    assertPositionAndSize(textbox, '109px', '204px', '395px', '296px');
+
+    // Drag the left handle right and up. Upward motion is ignored. Left motion
+    // makes the box 2px narrower.
+    const left = getRequiredElement(textbox, '.handle.left.center');
+    await dragHandleWithKeyboard(left, 'ArrowUp', 2);
+    await dragHandleWithKeyboard(left, 'ArrowRight', 2);
+    assertPositionAndSize(textbox, '107px', '204px', '397px', '296px');
+
+    // Drag the right handle right and down. Downward motion is ignored. Right
+    // motion makes the box 3px wider.
+    const right = getRequiredElement(textbox, '.handle.right.center');
+    await dragHandleWithKeyboard(right, 'ArrowDown', 3);
+    await dragHandleWithKeyboard(right, 'ArrowRight', 3);
+    assertPositionAndSize(textbox, '110px', '204px', '397px', '296px');
+
+    // Drag the bottom left handle down and left to make the box 10px bigger
+    // in both dimensions.
+    const bottomLeft = getRequiredElement(textbox, '.handle.bottom.left');
+    await dragHandleWithKeyboard(bottomLeft, 'ArrowDown', 10);
+    await dragHandleWithKeyboard(bottomLeft, 'ArrowLeft', 10);
+    assertPositionAndSize(textbox, '120px', '214px', '387px', '296px');
+
+    // Drag the bottom handle down and left to make the box 5px taller.
+    // Motion left is ignored.
+    const bottom = getRequiredElement(textbox, '.handle.bottom.center');
+    await dragHandleWithKeyboard(bottom, 'ArrowDown', 5);
+    await dragHandleWithKeyboard(bottom, 'ArrowLeft', 5);
+    assertPositionAndSize(textbox, '120px', '219px', '387px', '296px');
+
+    // Drag the bottom right handle down and right to make the box 2px bigger
+    // in both dimensions.
+    const bottomRight = getRequiredElement(textbox, '.handle.bottom.right');
+    await dragHandleWithKeyboard(bottomRight, 'ArrowDown', 2);
+    await dragHandleWithKeyboard(bottomRight, 'ArrowRight', 2);
+    assertPositionAndSize(textbox, '122px', '221px', '387px', '296px');
+
+    // Drag the bottom right handle up and left to try to make the box too
+    // small. Make sure it clamps at the same minimum size, anchored on the top
+    // left corner.
+    await dragHandleWithKeyboard(bottomRight, 'ArrowUp', 100);
+    await dragHandleWithKeyboard(bottomRight, 'ArrowLeft', 100);
+    const clampedWidth = textbox.$.textbox.clientWidth;
+    const clampedHeight = textbox.$.textbox.clientHeight;
+    chrome.test.assertTrue(clampedHeight >= textbox.$.textbox.scrollHeight);
+    // Min width is 36px.
+    chrome.test.assertEq(36, clampedWidth);
+    assertPositionAndSize(
+        textbox, '36px', `${clampedHeight}px`, '387px', '296px');
+
+    chrome.test.succeed();
+  },
+
+  async function testMoveWithKeyboard() {
+    // Initialize to a 100x100 box at 400, 300.
+    initializeBox(100, 100, 400, 300);
+    await microtasksFinished();
+    assertPositionAndSize(textbox, '100px', '100px', '400px', '300px');
+    await dragHandleWithKeyboard(textbox, 'ArrowUp', 5);
+    assertPositionAndSize(textbox, '100px', '100px', '400px', '295px');
+    await dragHandleWithKeyboard(textbox, 'ArrowDown', 5);
+    assertPositionAndSize(textbox, '100px', '100px', '400px', '300px');
+    await dragHandleWithKeyboard(textbox, 'ArrowRight', 5);
+    assertPositionAndSize(textbox, '100px', '100px', '405px', '300px');
+    await dragHandleWithKeyboard(textbox, 'ArrowLeft', 5);
+    assertPositionAndSize(textbox, '100px', '100px', '400px', '300px');
+
+    // Make sure that arrow keys in the textarea are ignored.
+    await dragHandleWithKeyboard(textbox.$.textbox, 'ArrowUp', 1);
+    await dragHandleWithKeyboard(textbox.$.textbox, 'ArrowDown', 1);
+    await dragHandleWithKeyboard(textbox.$.textbox, 'ArrowLeft', 1);
+    await dragHandleWithKeyboard(textbox.$.textbox, 'ArrowRight', 1);
+    assertPositionAndSize(textbox, '100px', '100px', '400px', '300px');
+    chrome.test.succeed();
+  },
+
+  async function testEscapeAndDelete() {
+    // Initialize to a 100x100 box at 400, 300.
+    initializeBox(100, 100, 400, 300);
+    await microtasksFinished();
+    chrome.test.assertFalse(textbox.hidden);
+    chrome.test.assertTrue(isVisible(textbox));
+
+    // Editing text --> commit annotation on event.
+    const testAnnotation = getDefaultAnnotation();
+    // Messages to the backend are in page coordinates. Convert to page
+    // coordinates since this is used for validating the commit message.
+    testAnnotation
+        .textBoxRect = {locationX: 195, locationY: 147, height: 50, width: 50};
+
+    mockPlugin.clearMessages();
+    textbox.$.textbox.value = testAnnotation.text;
+    textbox.$.textbox.dispatchEvent(new CustomEvent('input'));
+    await microtasksFinished();
+    // Escape on the textarea focuses the top level box.
+    const whenFocused = eventToPromise('focus', textbox);
+    keyDownOn(textbox.$.textbox, 0, [], 'Escape');
+    await whenFocused;
+
+    // Escape on the textbox commits the annotation and hides the box.
+    keyDownOn(textbox, 0, [], 'Escape');
+    await microtasksFinished();
+    chrome.test.assertTrue(textbox.hidden);
+    chrome.test.assertFalse(isVisible(textbox));
+    verifyFinishTextAnnotationMessage(testAnnotation);
+
+    // If the user is dragging, escape commits the annotation at the start
+    // location and hides the box.
+    initializeBox(100, 100, 400, 300);
+    await microtasksFinished();
+    chrome.test.assertTrue(isVisible(textbox));
+    mockPlugin.clearMessages();
+    textbox.$.textbox.value = testAnnotation.text;
+    textbox.$.textbox.dispatchEvent(new CustomEvent('input'));
+    await microtasksFinished();
+    const handle = getRequiredElement(textbox, '.handle.bottom.right');
+    handle.dispatchEvent(new PointerEvent(
+        'pointerdown', {composed: true, pointerId: 1, clientX: 0, clientY: 0}));
+    handle.dispatchEvent(new PointerEvent(
+        'pointerdown',
+        {composed: true, pointerId: 1, clientX: 10, clientY: 10}));
+    await microtasksFinished();
+    keyDownOn(textbox, 0, [], 'Escape');
+    await microtasksFinished();
+    chrome.test.assertTrue(textbox.hidden);
+    chrome.test.assertFalse(isVisible(textbox));
+    // Message is identical to before because 'pointerup' was never fired.
+    verifyFinishTextAnnotationMessage(testAnnotation);
+
+    // Escape without any modification hides the box but doesn't send a message.
+    initializeBox(100, 100, 400, 300);
+    await microtasksFinished();
+    chrome.test.assertTrue(isVisible(textbox));
+    mockPlugin.clearMessages();
+    keyDownOn(textbox, 0, [], 'Escape');
+    await microtasksFinished();
+    chrome.test.assertTrue(textbox.hidden);
+    chrome.test.assertFalse(isVisible(textbox));
+    chrome.test.assertEq(
+        undefined, mockPlugin.findMessage('setTextAnnotation'));
+
+    // Initialize to a 100x100 box at 400, 300 with some text content. Use
+    // "Delete" to clear all the content, which will trigger a message since
+    // this is for an existing annotation.
+    initializeBox(100, 100, 400, 300, true);
+    await microtasksFinished();
+    chrome.test.assertFalse(textbox.hidden);
+    chrome.test.assertTrue(isVisible(textbox));
+    mockPlugin.clearMessages();
+    keyDownOn(textbox, 0, [], 'Delete');
+    await microtasksFinished();
+    chrome.test.assertTrue(textbox.hidden);
+    chrome.test.assertFalse(isVisible(textbox));
+    testAnnotation.text = '';
+    verifyFinishTextAnnotationMessage(testAnnotation);
+
+    chrome.test.succeed();
+  },
+
   async function testCloseAndEvents() {
     let textBoxStates: TextBoxState[] = [];
     textbox.addEventListener('state-changed', e => {
diff --git a/chrome/test/data/webui/bookmarks/command_manager_test.ts b/chrome/test/data/webui/bookmarks/command_manager_test.ts
index e68112f..d8355be3 100644
--- a/chrome/test/data/webui/bookmarks/command_manager_test.ts
+++ b/chrome/test/data/webui/bookmarks/command_manager_test.ts
@@ -329,6 +329,16 @@
     assertTrue(split);
   });
 
+  test('"Open in New Tab Group" does not expand nodes', async function() {
+    const items = new Set(['1']);
+    assertTrue(commandManager.canExecute(Command.OPEN_NEW_GROUP, items));
+    commandManager.handle(Command.OPEN_NEW_GROUP, items);
+    await microtasksFinished();
+
+    const [ids] = await bookmarkManagerProxy.whenCalled('openInNewTabGroup');
+    assertDeepEquals(['1'], ids);
+  });
+
   test(
       'cannot execute "Open in New Tab" on folders with no items', async () => {
         const items = new Set(['2']);
diff --git a/chrome/test/data/webui/glic/api_test.ts b/chrome/test/data/webui/glic/api_test.ts
index 6a4a037..5e4c6aa 100644
--- a/chrome/test/data/webui/glic/api_test.ts
+++ b/chrome/test/data/webui/glic/api_test.ts
@@ -928,13 +928,13 @@
     }
   }
 
+  async testReloadWebUi() {}
+
   private async assertCreateTabFails(url: string) {
     assertTrue(!!this.host.createTab);
-    try {
-      await this.host.createTab(url, {openInBackground: false});
-    } catch (e) {
-      assertEquals('createTab: failed', (e as Error).message);
-    }
+    const errorMessage = await assertRejects(
+        this.host.createTab(url, {openInBackground: false}));
+    assertEquals('createTab: failed', errorMessage);
   }
 
   private async closePanelAndWaitUntilInactive() {
@@ -953,6 +953,25 @@
   async testLoadWhileWindowClosed() {
     await observeSequence(this.host.panelActive()).waitForValue(false);
   }
+
+  async testDeferredFocusedTabStateAtCreation() {
+    // Initial state.
+    assertTrue(!!this.host.getFocusedTabStateV2);
+    const focusedTabStateV2Sequence =
+        observeSequence(this.host.getFocusedTabStateV2());
+    let focusedTabState = await focusedTabStateV2Sequence.next();
+    assertTrue(!!focusedTabState.hasNoFocus);
+    const tabStatePromise = focusedTabStateV2Sequence.next();
+    assertRejects(waitFor(tabStatePromise, 200));
+    // We should only see the second page.
+    await this.advanceToNextStep();
+    focusedTabState = await tabStatePromise;
+    assertTrue(!!focusedTabState.hasFocus);
+    assertTrue(
+        focusedTabState.hasFocus.tabData.url.endsWith(
+            'scrollable_page_with_content.html'),
+        `url=${focusedTabState.hasFocus.tabData.url}`);
+  }
 }
 
 type InitFailureType = 'error'|'timeout'|'none'|'reloadAfterInitialize'|
@@ -1340,4 +1359,16 @@
   return new Response(stream).bytes();
 }
 
+async function assertRejects<T>(promise: Promise<T>):
+    Promise<string|undefined> {
+  return promise.then(
+      () => {
+        // The promise should have rejected.
+        assertTrue(false);
+      },
+      (e) => {
+        return (e as Error).message;
+      });
+}
+
 main();
diff --git a/chrome/test/data/webui/print_preview/destination_search_test.ts b/chrome/test/data/webui/print_preview/destination_search_test.ts
index 304b927b..4eae85f 100644
--- a/chrome/test/data/webui/print_preview/destination_search_test.ts
+++ b/chrome/test/data/webui/print_preview/destination_search_test.ts
@@ -6,7 +6,6 @@
 
 import type {DestinationStore, PrintPreviewDestinationDialogElement} from 'chrome://print/print_preview.js';
 import {Destination, DestinationOrigin, DestinationStoreEventType, NativeLayerImpl} from 'chrome://print/print_preview.js';
-import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
@@ -42,7 +41,6 @@
     document.body.appendChild(dialog);
     return nativeLayer.whenCalled('getPrinterCapabilities').then(function() {
       dialog.show();
-      flush();
       nativeLayer.reset();
     });
   });
diff --git a/chrome/test/data/webui/print_preview/invalid_settings_test.ts b/chrome/test/data/webui/print_preview/invalid_settings_test.ts
index fcafa47..c6aab4c 100644
--- a/chrome/test/data/webui/print_preview/invalid_settings_test.ts
+++ b/chrome/test/data/webui/print_preview/invalid_settings_test.ts
@@ -6,7 +6,6 @@
 import {MeasurementSystemUnitType, NativeLayerImpl, PluginProxyImpl, State, whenReady} from 'chrome://print/print_preview.js';
 import {assert} from 'chrome://resources/js/assert.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {waitBeforeNextRender} from 'chrome://webui-test/polymer_test_util.js';
 import {microtasksFinished} from 'chrome://webui-test/test_util.js';
 
 import {NativeLayerStub} from './native_layer_stub.js';
@@ -90,7 +89,7 @@
     const destinationSettings =
         sidebar.shadowRoot.querySelector('print-preview-destination-settings')!;
 
-    return waitBeforeNextRender(page)
+    return microtasksFinished()
         .then(() => {
           const parentElement =
               sidebar.shadowRoot.querySelector('print-preview-button-strip')!;
diff --git a/chrome/test/data/webui/print_preview/key_event_test.ts b/chrome/test/data/webui/print_preview/key_event_test.ts
index ff11705..2d7c521 100644
--- a/chrome/test/data/webui/print_preview/key_event_test.ts
+++ b/chrome/test/data/webui/print_preview/key_event_test.ts
@@ -5,11 +5,9 @@
 import type {PrintPreviewAppElement} from 'chrome://print/print_preview.js';
 import {NativeLayerImpl, PluginProxyImpl} from 'chrome://print/print_preview.js';
 import {isChromeOS, isMac, isWindows} from 'chrome://resources/js/platform.js';
-import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {keyEventOn} from 'chrome://webui-test/keyboard_mock_interactions.js';
 import type {ModifiersParam} from 'chrome://webui-test/keyboard_mock_interactions.js';
-import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 import {eventToPromise, microtasksFinished} from 'chrome://webui-test/test_util.js';
 
 import {NativeLayerStub} from './native_layer_stub.js';
@@ -46,7 +44,6 @@
           nativeLayer.whenCalled('getPrinterCapabilities'),
         ])
         .then(function() {
-          flush();
           return microtasksFinished();
         });
   });
@@ -106,7 +103,7 @@
     const whenKeyEventFired = eventToPromise('keydown', button);
     keyEventOn(button, 'keydown', 0, [], 'Enter');
     await whenKeyEventFired;
-    await flushTasks();
+    await microtasksFinished();
     assertEquals(0, nativeLayer.getCallCount('doPrint'));
   });
 
diff --git a/chrome/test/data/webui/print_preview/pdf_viewer_test.ts b/chrome/test/data/webui/print_preview/pdf_viewer_test.ts
index cdd6771b..d70c645 100644
--- a/chrome/test/data/webui/print_preview/pdf_viewer_test.ts
+++ b/chrome/test/data/webui/print_preview/pdf_viewer_test.ts
@@ -10,7 +10,6 @@
 import type {PdfViewerPrintElement} from 'chrome://print/pdf/pdf_print_wrapper.js';
 import {pdfCreateOutOfProcessPlugin} from 'chrome://print/pdf/pdf_scripting_api.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
 import {eventToPromise, microtasksFinished} from 'chrome://webui-test/test_util.js';
 
 suite('PdfViewerTest', function() {
@@ -58,7 +57,7 @@
     // The error dialog only appears when it is needed.
     assertFalse(!!viewer.shadowRoot.querySelector('viewer-error-dialog'));
     viewer.showErrorDialog = true;
-    await waitAfterNextRender(viewer);
+    await microtasksFinished();
     assertTrue(!!viewer.shadowRoot.querySelector('viewer-error-dialog'));
   });
 
diff --git a/chrome/test/data/webui/print_preview/policy_test.ts b/chrome/test/data/webui/print_preview/policy_test.ts
index 3f1ca710..565c1b28 100644
--- a/chrome/test/data/webui/print_preview/policy_test.ts
+++ b/chrome/test/data/webui/print_preview/policy_test.ts
@@ -4,7 +4,6 @@
 
 import type {CrCheckboxElement, NativeInitialSettings, PolicyObjectEntry, PrintPreviewAppElement, SerializedSettings} from 'chrome://print/print_preview.js';
 import {BackgroundGraphicsModeRestriction, NativeLayerImpl, PluginProxyImpl} from 'chrome://print/print_preview.js';
-import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertFalse} from 'chrome://webui-test/chai_assert.js';
 import {microtasksFinished} from 'chrome://webui-test/test_util.js';
 
@@ -49,7 +48,6 @@
           nativeLayer.whenCalled('getPrinterCapabilities'),
         ])
         .then(function() {
-          flush();
           return microtasksFinished();
         });
   }
diff --git a/chrome/test/data/webui/print_preview/print_preview_test_utils.ts b/chrome/test/data/webui/print_preview/print_preview_test_utils.ts
index 685cd1f..dd570d46 100644
--- a/chrome/test/data/webui/print_preview/print_preview_test_utils.ts
+++ b/chrome/test/data/webui/print_preview/print_preview_test_utils.ts
@@ -5,10 +5,10 @@
 import type {CapabilitiesResponse, Cdd, ColorOption, DocumentSettings, DpiOption, DuplexOption, ExtensionDestinationInfo, LocalDestinationInfo, MediaSizeCapability, MediaSizeOption, NativeInitialSettings, PageOrientationOption} from 'chrome://print/print_preview.js';
 import {createDocumentSettings as createDefaultDocumentSettings, DEFAULT_MAX_COPIES, Destination, DestinationOrigin, DestinationStore, GooglePromotedDestinationId, MeasurementSystemUnitType, VendorCapabilityValueType} from 'chrome://print/print_preview.js';
 import type {CrInputElement} from 'chrome://resources/cr_elements/cr_input/cr_input.js';
-import {WebUiListenerMixin} from 'chrome://resources/cr_elements/web_ui_listener_mixin.js';
+import {WebUiListenerMixinLit} from 'chrome://resources/cr_elements/web_ui_listener_mixin_lit.js';
 import {assert} from 'chrome://resources/js/assert.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
-import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {CrLitElement} from 'chrome://resources/lit/v3_0/lit.rollup.js';
 import {assertEquals} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
@@ -353,7 +353,7 @@
   return await eventToPromise('input-change', parentElement);
 }
 
-const TestListenerElementBase = WebUiListenerMixin(PolymerElement);
+const TestListenerElementBase = WebUiListenerMixinLit(CrLitElement);
 class TestListenerElement extends TestListenerElementBase {
   static get is() {
     return 'test-listener-element';
diff --git a/chrome/test/data/webui/print_preview/system_dialog_test.ts b/chrome/test/data/webui/print_preview/system_dialog_test.ts
index 954b8ca..a14f5b0b 100644
--- a/chrome/test/data/webui/print_preview/system_dialog_test.ts
+++ b/chrome/test/data/webui/print_preview/system_dialog_test.ts
@@ -5,7 +5,6 @@
 import type {CrButtonElement, PrintPreviewLinkContainerElement, PrintPreviewSidebarElement} from 'chrome://print/print_preview.js';
 import {NativeLayerImpl, PluginProxyImpl, ScalingType, whenReady} from 'chrome://print/print_preview.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {waitBeforeNextRender} from 'chrome://webui-test/polymer_test_util.js';
 import {eventToPromise, microtasksFinished} from 'chrome://webui-test/test_util.js';
 
 import {NativeLayerStub} from './native_layer_stub.js';
@@ -45,7 +44,6 @@
     sidebar = page.shadowRoot.querySelector('print-preview-sidebar')!;
     return Promise
         .all([
-          waitBeforeNextRender(page),
           whenReady(),
           nativeLayer.whenCalled('getInitialSettings'),
           nativeLayer.whenCalled('getPrinterCapabilities'),
diff --git a/chrome/test/data/webui/privacy_sandbox/base_dialog_browsertest.cc b/chrome/test/data/webui/privacy_sandbox/base_dialog_browsertest.cc
index eadcc0fb..4d8a90d 100644
--- a/chrome/test/data/webui/privacy_sandbox/base_dialog_browsertest.cc
+++ b/chrome/test/data/webui/privacy_sandbox/base_dialog_browsertest.cc
@@ -47,16 +47,21 @@
                          PrivacySandboxBaseDialogMochaTest,
                          testing::Values(WindowSize::kSmall, WindowSize::kBig));
 
-IN_PROC_BROWSER_TEST_P(PrivacySandboxBaseDialogMochaTest, TopicsConsent) {
-  RunTestSuite("TopicsConsent");
+IN_PROC_BROWSER_TEST_P(PrivacySandboxBaseDialogMochaTest, TopicsConsentNotice) {
+  RunTestSuite("TopicsConsentNotice");
 }
 
 IN_PROC_BROWSER_TEST_P(PrivacySandboxBaseDialogMochaTest,
-                       ProtectedAudienceMeasurement) {
-  RunTestSuite("ProtectedAudienceMeasurement");
+                       ProtectedAudienceMeasurementNotice) {
+  RunTestSuite("ProtectedAudienceMeasurementNotice");
 }
 
-IN_PROC_BROWSER_TEST_P(PrivacySandboxBaseDialogMochaTest, ThreeAdsApis) {
-  RunTestSuite("ThreeAdsApis");
+IN_PROC_BROWSER_TEST_P(PrivacySandboxBaseDialogMochaTest, ThreeAdsApisNotice) {
+  RunTestSuite("ThreeAdsApisNotice");
 }
+
+IN_PROC_BROWSER_TEST_P(PrivacySandboxBaseDialogMochaTest, MeasurementNotice) {
+  RunTestSuite("MeasurementNotice");
+}
+
 }  // namespace
diff --git a/chrome/test/data/webui/privacy_sandbox/base_dialog_test.ts b/chrome/test/data/webui/privacy_sandbox/base_dialog_test.ts
index 192aff6..a479211 100644
--- a/chrome/test/data/webui/privacy_sandbox/base_dialog_test.ts
+++ b/chrome/test/data/webui/privacy_sandbox/base_dialog_test.ts
@@ -3,7 +3,9 @@
 // found in the LICENSE file.
 
 import 'chrome://privacy-sandbox-base-dialog/base_dialog_app.js';
-import 'chrome://privacy-sandbox-base-dialog/topics_consent.js';
+import 'chrome://privacy-sandbox-base-dialog/topics_consent_notice.js';
+import 'chrome://privacy-sandbox-base-dialog/protected_audience_measurement_notice.js';
+import 'chrome://privacy-sandbox-base-dialog/three_ads_apis_notice.js';
 
 import type {BaseDialogApp} from 'chrome://privacy-sandbox-base-dialog/base_dialog_app.js';
 import {BaseDialogBrowserProxy} from 'chrome://privacy-sandbox-base-dialog/base_dialog_browser_proxy.js';
@@ -42,11 +44,13 @@
 function getNoticeComponentSelector(notice: PrivacySandboxNotice) {
   switch (notice) {
     case PrivacySandboxNotice.kTopicsConsentNotice:
-      return 'topics-consent';
+      return 'topics-consent-notice';
     case PrivacySandboxNotice.kProtectedAudienceMeasurementNotice:
-      return 'protected-audience-measurement';
+      return 'protected-audience-measurement-notice';
     case PrivacySandboxNotice.kThreeAdsApisNotice:
-      return 'three-ads-apis';
+      return 'three-ads-apis-notice';
+    case PrivacySandboxNotice.kMeasurementNotice:
+      return 'measurement-notice';
     default:
       return '';
   }
@@ -77,7 +81,7 @@
   await testHandler.eventOccurred(notice, event);
 }
 
-suite('TopicsConsent', function() {
+suite('TopicsConsentNotice', function() {
   let page: BaseDialogApp;
   let testHandler: TestBaseDialogPageHandler;
 
@@ -106,7 +110,7 @@
   });
 });
 
-suite('ProtectedAudienceMeasurement', function() {
+suite('ProtectedAudienceMeasurementNotice', function() {
   let page: BaseDialogApp;
   let testHandler: TestBaseDialogPageHandler;
 
@@ -132,7 +136,7 @@
   });
 });
 
-suite('ThreeAdsApis', function() {
+suite('ThreeAdsApisNotice', function() {
   let page: BaseDialogApp;
   let testHandler: TestBaseDialogPageHandler;
 
@@ -156,3 +160,28 @@
         PrivacySandboxNoticeEvent.kAck, testHandler);
   });
 });
+
+suite('MeasurementNotice', function() {
+  let page: BaseDialogApp;
+  let testHandler: TestBaseDialogPageHandler;
+
+  suiteSetup(function() {
+    loadTimeData.overrideValues({
+      noticeIdToShow: PrivacySandboxNotice.kMeasurementNotice,
+    });
+  });
+
+  setup(async function() {
+    ({page, testHandler} = await setupBaseDialogApp());
+  });
+
+  test('CrViewManager', function() {
+    testCrViewManager(page, PrivacySandboxNotice.kMeasurementNotice);
+  });
+
+  test('Notice', async function() {
+    await testButtonClick(
+        page, PrivacySandboxNotice.kMeasurementNotice,
+        PrivacySandboxNoticeEvent.kAck, testHandler);
+  });
+});
diff --git a/chrome/test/data/webui/side_panel/read_anything/fake_reading_mode.ts b/chrome/test/data/webui/side_panel/read_anything/fake_reading_mode.ts
index eab2a73..c842a28 100644
--- a/chrome/test/data/webui/side_panel/read_anything/fake_reading_mode.ts
+++ b/chrome/test/data/webui/side_panel/read_anything/fake_reading_mode.ts
@@ -418,10 +418,6 @@
     return '';
   }
 
-  // Begins processing the speech segments on the current page to be used by
-  // Read Aloud.
-  preprocessTextForSpeech() {}
-
   // Returns a list of node ids and ranges (start and length) associated with
   // the index within the given text segment. The intended use is for
   // highlighting the ranges. Note that a highlight can span over multiple
diff --git a/chrome/test/data/webui/side_panel/read_anything/speech_controller_test.ts b/chrome/test/data/webui/side_panel/read_anything/speech_controller_test.ts
index 1f1cbd9c..00c80f32 100644
--- a/chrome/test/data/webui/side_panel/read_anything/speech_controller_test.ts
+++ b/chrome/test/data/webui/side_panel/read_anything/speech_controller_test.ts
@@ -143,22 +143,17 @@
 
   suite('initializeSpeechTree', () => {
     let initAxPositionWithNode: number;
-    let startedPreprocess: boolean = false;
 
     setup(() => {
       chrome.readingMode.initAxPositionWithNode = (nodeId) => {
         initAxPositionWithNode = nodeId;
       };
-      chrome.readingMode.preprocessTextForSpeech = () => {
-        startedPreprocess = true;
-      };
     });
 
     test('with no node id does nothing', () => {
       speechController.initializeSpeechTree();
 
       assertFalse(!!initAxPositionWithNode);
-      assertFalse(startedPreprocess);
       assertFalse(speechController.isSpeechTreeInitialized());
     });
 
@@ -166,20 +161,14 @@
       const id1 = 10;
       const id2 = 12;
       speechController.initializeSpeechTree(id1);
-      startedPreprocess = false;
-
       speechController.initializeSpeechTree(id2);
-
       assertEquals(id1, initAxPositionWithNode);
-      assertFalse(startedPreprocess);
     });
 
     test('initializes speech tree', () => {
       const id = 14;
       speechController.initializeSpeechTree(id);
-
       assertEquals(id, initAxPositionWithNode);
-      assertTrue(startedPreprocess);
       assertTrue(speechController.isSpeechTreeInitialized());
     });
   });
diff --git a/chrome/updater/test/data/offline_manifest.gup b/chrome/updater/test/data/offline_manifest.gup
new file mode 100644
index 0000000..41e60c98
--- /dev/null
+++ b/chrome/updater/test/data/offline_manifest.gup
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<response protocol="3.0">

+  <systemrequirements platform="win"

+                      arch="${ARCH_REQUIREMENT}"

+                      min_os_version="6.1"/>

+  <app appid="${APP_ID}"

+       status="ok">

+    <updatecheck status="ok">

+      <urls>

+        <url codebase="https://dl.google.com/install/${INSTALLER_VERSION}/"/>

+      </urls>

+      <manifest version="${INSTALLER_VERSION}">

+        <packages>

+          <package name="${INSTALLER_FILENAME}"

+                   hash_sha256="${INSTALLER_HASH_SHA256}"

+                   size="${INSTALLER_SIZE}"

+                   required="true"/>

+        </packages>

+        <actions>

+          <action event="install"

+                  run="${INSTALLER_FILENAME}"

+                  arguments=""

+                  needsadmin="false"/>

+          <action event="postinstall"

+                  onsuccess="exitsilentlyonlaunchcmd"/>

+        </actions>

+      </manifest>

+    </updatecheck>

+  </app>

+</response>

diff --git a/chrome/updater/test/data/tag.exe b/chrome/updater/test/data/tag.exe
new file mode 100755
index 0000000..ec5d50b
--- /dev/null
+++ b/chrome/updater/test/data/tag.exe
Binary files differ
diff --git a/chrome/updater/test/data/test_certificate.pfx b/chrome/updater/test/data/test_certificate.pfx
new file mode 100644
index 0000000..77da144
--- /dev/null
+++ b/chrome/updater/test/data/test_certificate.pfx
Binary files differ
diff --git a/chrome/updater/test/test_installer/BUILD.gn b/chrome/updater/test/test_installer/BUILD.gn
index 260fabe..65210faa 100644
--- a/chrome/updater/test/test_installer/BUILD.gn
+++ b/chrome/updater/test/test_installer/BUILD.gn
@@ -8,6 +8,8 @@
 import("//components/crx_file/crx3.gni")
 
 if (is_win) {
+  import("//build/toolchain/win/toolchain.gni")
+
   executable("test_meta_installer") {
     testonly = true
 
@@ -29,7 +31,9 @@
     sources = [
       "$root_build_dir/test_meta_installer.exe",
       "//chrome/tools/build/win/resedit.py",
+      "//chrome/updater/win/signing/sign.py",
       "create_test_installer_gn_wrapper.py",
+      "create_test_standalone_msi_installer_gn_wrapper.py",
       "embed_install_scripts.py",
     ]
 
@@ -189,6 +193,72 @@
     deps = [ ":copy_test_msi_installer2.0.0.0" ]
   }
 
+  if (host_os == "win") {
+    action("test_standalone_msi_installer") {
+      testonly = true
+      base_dir = "$root_build_dir/test_installer"
+      msi_base_name = "TestSystemMsiInstaller"
+      certificate_path = "//chrome/updater/test/data/test_certificate.pfx"
+      offline_manifest = "//chrome/updater/test/data/offline_manifest.gup"
+      tag_exe = "//chrome/updater/test/data/tag.exe"
+      appid = "{c28fcf72-bcf2-45c5-8def-31a74ac02012}"
+      script = "$base_dir/create_test_standalone_msi_installer_gn_wrapper.py"
+      output = "$base_dir/${msi_base_name}StandaloneSetup.exe"
+
+      inputs =
+          [ script ] + get_target_outputs(":copy_test_msi_installer2.0.0.0")
+      outputs = [ output ]
+
+      deps = [
+        ":copy_test_msi_installer2.0.0.0",
+        ":test_installer",
+        "//chrome/updater/tools:tag",
+        "//chrome/updater/win/installer:installer_test",
+      ]
+
+      data = [
+        certificate_path,
+        offline_manifest,
+        tag_exe,
+      ]
+
+      args = [
+        "--in_file",
+        "UpdaterSetup_test.exe",
+        "--out_file",
+        rebase_path(output, root_build_dir),
+        "--certificate_file_path",
+        rebase_path(certificate_path, root_build_dir),
+        "--appid",
+        appid,
+        "--installer_path",
+        rebase_path("$base_dir/${msi_base_name}.msi", root_build_dir),
+        "--manifest_path",
+        rebase_path(offline_manifest, root_build_dir),
+        "--lzma_7z",
+        rebase_path("//third_party/lzma_sdk/bin/win64/7za.exe", root_build_dir),
+        "--signtool",
+        rebase_path(
+            "$windows_sdk_path/bin/$windows_sdk_version/x64/signtool.exe",
+            root_build_dir),
+        "--tagging_exe",
+        rebase_path(tag_exe, root_build_dir),
+        "--manifest_dict_replacements",
+        "{'\${INSTALLER_VERSION}':'2.0.0.0', '\${ARCH_REQUIREMENT}':'x86'}",
+      ]
+    }
+  } else {
+    # TODO(crbug.com/413081282): cross-compile support, since the resource
+    # editing script does not work on non-Windows platform.
+    copy("test_standalone_msi_installer") {
+      testonly = true
+      sources = get_target_outputs(":test_msi_installer2.0.0.0")
+      deps = [ ":test_msi_installer2.0.0.0" ]
+      base_dir = "$root_build_dir/test_installer"
+      outputs = [ "$base_dir/{{source_name_part}}StandaloneSetup.exe" ]
+    }
+  }
+
   group("app_installers") {
     testonly = true
 
@@ -198,6 +268,7 @@
       ":test_installer",
       ":test_msi_installer1.0.0.0",
       ":test_msi_installer_crx",
+      ":test_standalone_msi_installer",
     ]
   }
 } else {
diff --git a/chrome/updater/test/test_installer/create_test_standalone_msi_installer_gn_wrapper.py b/chrome/updater/test/test_installer/create_test_standalone_msi_installer_gn_wrapper.py
new file mode 100755
index 0000000..900fd09
--- /dev/null
+++ b/chrome/updater/test/test_installer/create_test_standalone_msi_installer_gn_wrapper.py
@@ -0,0 +1,17 @@
+#!/usr/bin/env python3
+# 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.
+
+# GN runs python scripts with the native interpreter, but `sign.py` needs to run
+# with `vpython` to be able to run `resedit`, so this wrapper is needed.
+
+import os
+import subprocess
+import sys
+
+if __name__ == '__main__':
+    subprocess.run([
+        'vpython3.bat',
+        os.path.join(os.path.dirname(os.path.abspath(__file__)), 'sign.py')
+    ] + sys.argv[1:])
diff --git a/chrome/updater/win/signing/sign.py b/chrome/updater/win/signing/sign.py
index fe16f52a..59a5c46 100755
--- a/chrome/updater/win/signing/sign.py
+++ b/chrome/updater/win/signing/sign.py
@@ -113,9 +113,9 @@
             command += flags
             if self._certificate_file_path:
                 command += ['/f', self._certificate_file_path]
-            if self._certificate_password:
-                command += ['/p', self._certificate_password]
-            if self._identity:
+                if self._certificate_password:
+                    command += ['/p', self._certificate_password]
+            elif self._identity:
                 command += ['/s', 'My', '/n', self._identity]
 
             command += [in_file]
diff --git a/chromeos/ash/components/boca/session_api/constants.h b/chromeos/ash/components/boca/session_api/constants.h
index f214addb..9c880d79 100644
--- a/chromeos/ash/components/boca/session_api/constants.h
+++ b/chromeos/ash/components/boca/session_api/constants.h
@@ -117,6 +117,8 @@
 inline constexpr char kHostDevice[] = "hostDevice";
 inline constexpr char kSpotlightConnectionCode[] = "connectionCode";
 inline constexpr char kSpotlightConnectionParam[] = "connectionParam";
+inline constexpr char kViewScreenRequester[] = "viewScreenRequester";
+inline constexpr char kServiceAccount[] = "serviceAccount";
 inline constexpr char kStudentGroupId[] = "studentGroupId";
 inline constexpr char kViewScreenConfig[] = "viewScreenConfig";
 inline constexpr char kViewScreenState[] = "viewScreenState";
diff --git a/chromeos/ash/components/boca/session_api/session_parser.cc b/chromeos/ash/components/boca/session_api/session_parser.cc
index 5e572a3..7a31bad 100644
--- a/chromeos/ash/components/boca/session_api/session_parser.cc
+++ b/chromeos/ash/components/boca/session_api/session_parser.cc
@@ -371,6 +371,17 @@
                     ->set_connection_code(*connection_code);
               }
             }
+            if (auto* view_screen_requester_dict =
+                    view_screen_config_dict->FindDict(kViewScreenRequester)) {
+              if (auto* service_account_dict =
+                      view_screen_requester_dict->FindDict(kServiceAccount)) {
+                if (auto* ptr = service_account_dict->FindString(kEmail)) {
+                  view_screen_config.mutable_view_screen_requester()
+                      ->mutable_service_account()
+                      ->set_email(*ptr);
+                }
+              }
+            }
           }
         }
       }
diff --git a/chromeos/ash/components/boca/session_api/session_parser_unittest.cc b/chromeos/ash/components/boca/session_api/session_parser_unittest.cc
index 112c9fc2..3388690 100644
--- a/chromeos/ash/components/boca/session_api/session_parser_unittest.cc
+++ b/chromeos/ash/components/boca/session_api/session_parser_unittest.cc
@@ -53,6 +53,11 @@
                 "viewScreenState": "AVAILABLE",
                 "connectionParam": {
                   "connectionCode": "0123456789"
+                },
+                "viewScreenRequester": {
+                  "serviceAccount" : {
+                    "email": "robot@email.com"
+                  }
                 }
               }
          }
@@ -163,7 +168,12 @@
                   }
                 },
               "viewScreenConfig": {
-                "viewScreenState": "REQUESTED"
+                "viewScreenState": "REQUESTED",
+                "viewScreenRequester": {
+                  "serviceAccount" : {
+                    "email": "robot@email.com"
+                  }
+                }
               }
           }
           }
@@ -391,6 +401,14 @@
                               .view_screen_config()
                               .connection_param()
                               .connection_code());
+  EXPECT_EQ("robot@email.com", session_full->student_statuses()
+                                   .at("3")
+                                   .devices()
+                                   .at("kDummyDeviceId")
+                                   .view_screen_config()
+                                   .view_screen_requester()
+                                   .service_account()
+                                   .email());
   EXPECT_EQ(::boca::StudentStatus::ADDED,
             session_full->student_statuses().at("22").state());
   ParseStudentStatusProtoFromJson(session_dict_partial->GetIfDict(),
@@ -417,6 +435,14 @@
                 .at("kDummyDeviceId")
                 .view_screen_config()
                 .view_screen_state());
+  EXPECT_EQ("robot@email.com", session_full->student_statuses()
+                                   .at("3")
+                                   .devices()
+                                   .at("kDummyDeviceId")
+                                   .view_screen_config()
+                                   .view_screen_requester()
+                                   .service_account()
+                                   .email());
 }
 }  // namespace
 }  // namespace ash::boca
diff --git a/clank b/clank
index d994a61..51cc999 160000
--- a/clank
+++ b/clank
@@ -1 +1 @@
-Subproject commit d994a61ddd8f6d1483c152745c394af9f8e31b8b
+Subproject commit 51cc9995b6d55b6e3da653848c3dbae2e3d68316
diff --git a/components/attribution_reporting/BUILD.gn b/components/attribution_reporting/BUILD.gn
index 0596f06d..08a7792 100644
--- a/components/attribution_reporting/BUILD.gn
+++ b/components/attribution_reporting/BUILD.gn
@@ -338,10 +338,10 @@
               "namespace attribution_reporting { class EventReportWindows; }"
         },
         {
-          mojom = "attribution_reporting.mojom.TriggerSpecs"
-          cpp = "::attribution_reporting::TriggerSpecs"
+          mojom = "attribution_reporting.mojom.TriggerDataSet"
+          cpp = "::attribution_reporting::TriggerDataSet"
           forward_declaration =
-              "namespace attribution_reporting { class TriggerSpecs; }"
+              "namespace attribution_reporting { class TriggerDataSet; }"
         },
         {
           mojom = "attribution_reporting.mojom.SourceAggregatableDebugReportingConfig"
diff --git a/components/attribution_reporting/privacy_math.cc b/components/attribution_reporting/privacy_math.cc
index d1a9bf9e..d7e2b7c304 100644
--- a/components/attribution_reporting/privacy_math.cc
+++ b/components/attribution_reporting/privacy_math.cc
@@ -50,11 +50,11 @@
 }  // namespace
 
 base::expected<uint32_t, RandomizedResponseError> GetNumStates(
-    const TriggerSpecs& specs,
+    const TriggerDataSet& trigger_data,
     const EventReportWindows& event_report_windows,
     const MaxEventLevelReports max_event_level_reports) {
   const int max_reports = max_event_level_reports;
-  if (specs.trigger_data().empty() || max_reports == 0) {
+  if (trigger_data.trigger_data().empty() || max_reports == 0) {
     return 1;
   }
 
@@ -63,8 +63,8 @@
   base::CheckedNumeric<uint32_t> num_states =
       internal::GetNumberOfStarsAndBarsSequences(
           /*num_stars=*/static_cast<uint32_t>(max_reports),
-          /*num_bars=*/static_cast<uint32_t>(specs.trigger_data().size() *
-                                             num_windows));
+          /*num_bars=*/static_cast<uint32_t>(
+              trigger_data.trigger_data().size() * num_windows));
 
   if (!num_states.IsValid() ||
       num_states.ValueOrDie() > g_max_trigger_state_cardinality) {
@@ -134,7 +134,7 @@
 }
 
 bool IsValid(const RandomizedResponse& response,
-             const TriggerSpecs& specs,
+             const TriggerDataSet& trigger_data,
              const EventReportWindows& event_report_windows,
              MaxEventLevelReports max_event_level_reports) {
   if (!response.has_value()) {
@@ -146,7 +146,7 @@
          std::ranges::all_of(
              *response, [&](const FakeEventLevelReport& report) {
                const bool has_trigger_data =
-                   specs.trigger_data().contains(report.trigger_data);
+                   trigger_data.trigger_data().contains(report.trigger_data);
 
                return has_trigger_data && report.window_index >= 0 &&
                       base::MakeStrictNum(report.window_index) <
@@ -384,11 +384,11 @@
 
 base::expected<std::vector<FakeEventLevelReport>, RandomizedResponseError>
 GetFakeReportsForSequenceIndex(
-    const TriggerSpecs& specs,
+    const TriggerDataSet& trigger_data,
     const EventReportWindows& event_report_windows,
     const MaxEventLevelReports max_event_level_reports,
     base::StrictNumeric<uint32_t> random_stars_and_bars_sequence_index) {
-  const int trigger_data_cardinality = specs.trigger_data().size();
+  const int trigger_data_cardinality = trigger_data.trigger_data().size();
   const int max_reports = max_event_level_reports;
 
   ASSIGN_OR_RETURN(
@@ -425,7 +425,7 @@
 
     fake_reports.push_back({
         .trigger_data =
-            *std::next(specs.trigger_data().begin(), trigger_data_index),
+            *std::next(trigger_data.trigger_data().begin(), trigger_data_index),
         .window_index = result.quot,
     });
   }
@@ -436,16 +436,16 @@
 }  // namespace internal
 
 base::expected<RandomizedResponseData, RandomizedResponseError>
-DoRandomizedResponse(const TriggerSpecs& specs,
+DoRandomizedResponse(const TriggerDataSet& trigger_data,
                      const EventReportWindows& event_report_windows,
                      const MaxEventLevelReports max_event_level_reports,
                      double epsilon,
                      mojom::SourceType source_type,
                      const std::optional<AttributionScopesData>& scopes_data,
                      const PrivacyMathConfig& config) {
-  ASSIGN_OR_RETURN(
-      const uint32_t num_states,
-      GetNumStates(specs, event_report_windows, max_event_level_reports));
+  ASSIGN_OR_RETURN(const uint32_t num_states,
+                   GetNumStates(trigger_data, event_report_windows,
+                                max_event_level_reports));
   base::UmaHistogramCounts100000("Conversions.NumTriggerStates",
                                  base::ClampedNumeric(num_states));
 
@@ -478,8 +478,8 @@
     uint32_t sequence_index = base::RandGenerator(num_states);
     ASSIGN_OR_RETURN(fake_reports,
                      internal::GetFakeReportsForSequenceIndex(
-                         specs, event_report_windows, max_event_level_reports,
-                         sequence_index));
+                         trigger_data, event_report_windows,
+                         max_event_level_reports, sequence_index));
   }
   return RandomizedResponseData(rate, std::move(fake_reports));
 }
diff --git a/components/attribution_reporting/privacy_math.h b/components/attribution_reporting/privacy_math.h
index b08a86d4..eac094b3 100644
--- a/components/attribution_reporting/privacy_math.h
+++ b/components/attribution_reporting/privacy_math.h
@@ -22,7 +22,7 @@
 class AttributionScopesData;
 class EventReportWindows;
 class MaxEventLevelReports;
-class TriggerSpecs;
+class TriggerDataSet;
 
 struct FakeEventLevelReport {
   uint32_t trigger_data;
@@ -41,7 +41,7 @@
 
 COMPONENT_EXPORT(ATTRIBUTION_REPORTING)
 bool IsValid(const RandomizedResponse&,
-             const TriggerSpecs&,
+             const TriggerDataSet&,
              const EventReportWindows&,
              MaxEventLevelReports);
 
@@ -88,7 +88,7 @@
 // Returns the number of possible output states for the given API configuration.
 COMPONENT_EXPORT(ATTRIBUTION_REPORTING)
 base::expected<uint32_t, RandomizedResponseError> GetNumStates(
-    const TriggerSpecs&,
+    const TriggerDataSet&,
     const EventReportWindows&,
     MaxEventLevelReports);
 
@@ -114,7 +114,7 @@
 // Otherwise will return a vector of fake reports.
 COMPONENT_EXPORT(ATTRIBUTION_REPORTING)
 base::expected<RandomizedResponseData, RandomizedResponseError>
-DoRandomizedResponse(const TriggerSpecs&,
+DoRandomizedResponse(const TriggerDataSet&,
                      const EventReportWindows&,
                      MaxEventLevelReports,
                      double epsilon,
@@ -204,7 +204,7 @@
 COMPONENT_EXPORT(ATTRIBUTION_REPORTING)
 base::expected<std::vector<FakeEventLevelReport>, RandomizedResponseError>
 GetFakeReportsForSequenceIndex(
-    const TriggerSpecs&,
+    const TriggerDataSet&,
     const EventReportWindows&,
     MaxEventLevelReports,
     base::StrictNumeric<uint32_t> random_stars_and_bars_sequence_index);
diff --git a/components/attribution_reporting/privacy_math_perftest.cc b/components/attribution_reporting/privacy_math_perftest.cc
index 6da172c..cf08ed2 100644
--- a/components/attribution_reporting/privacy_math_perftest.cc
+++ b/components/attribution_reporting/privacy_math_perftest.cc
@@ -117,8 +117,8 @@
 
     const EventReportWindows event_report_windows =
         EventReportWindowsWithCount(tc.num_windows);
-    const TriggerSpecs specs =
-        TriggerSpecsWithCardinality(tc.trigger_data_cardinality);
+    const TriggerDataSet trigger_data =
+        TriggerDataSetWithCardinality(tc.trigger_data_cardinality);
     const MaxEventLevelReports max_event_level_reports(tc.max_reports);
 
     std::optional<AttributionScopesData> scopes;
@@ -131,8 +131,8 @@
 
     base::LapTimer timer;
     do {
-      auto result =
-          func(specs, event_report_windows, max_event_level_reports, scopes);
+      auto result = func(trigger_data, event_report_windows,
+                         max_event_level_reports, scopes);
       ::benchmark::DoNotOptimize(result);
       timer.NextLap();
     } while (!timer.HasTimeLimitExpired());
@@ -149,11 +149,11 @@
     GTEST_SKIP();
   }
   Run("AttributionReporting.NumStates",
-      [](const TriggerSpecs& specs,
+      [](const TriggerDataSet& trigger_data,
          const EventReportWindows& event_report_windows,
          const MaxEventLevelReports max_event_level_reports,
          const std::optional<AttributionScopesData>& scopes) {
-        return GetNumStates(specs, event_report_windows,
+        return GetNumStates(trigger_data, event_report_windows,
                             max_event_level_reports);
       });
 }
@@ -166,12 +166,12 @@
   };
 
   Run("AttributionReporting.RandomizedResponse",
-      [&](const TriggerSpecs& specs,
+      [&](const TriggerDataSet& trigger_data,
           const EventReportWindows& event_report_windows,
           const MaxEventLevelReports max_event_level_reports,
           const std::optional<AttributionScopesData>& scopes) {
         return DoRandomizedResponse(
-            specs, event_report_windows, max_event_level_reports,
+            trigger_data, event_report_windows, max_event_level_reports,
             /*epsilon=*/0,
             /*source_type=*/mojom::SourceType::kNavigation, scopes, kConfig);
       });
diff --git a/components/attribution_reporting/privacy_math_unittest.cc b/components/attribution_reporting/privacy_math_unittest.cc
index b35fc8a..92cdd8f7 100644
--- a/components/attribution_reporting/privacy_math_unittest.cc
+++ b/components/attribution_reporting/privacy_math_unittest.cc
@@ -36,7 +36,7 @@
 
 using ::attribution_reporting::EventReportWindows;
 using ::attribution_reporting::MaxEventLevelReports;
-using ::attribution_reporting::TriggerSpecs;
+using ::attribution_reporting::TriggerDataSet;
 using ::attribution_reporting::mojom::SourceType;
 using ::base::test::ErrorIs;
 using ::base::test::ValueIs;
@@ -472,7 +472,7 @@
     SCOPED_TRACE(i);
 
     EXPECT_THAT(internal::GetFakeReportsForSequenceIndex(
-                    TriggerSpecs(test_case.source_type),
+                    TriggerDataSet(test_case.source_type),
                     *EventReportWindows::FromDefaults(base::Days(30),
                                                       test_case.source_type),
                     MaxEventLevelReports(test_case.source_type),
@@ -483,20 +483,21 @@
   }
 }
 
-void RunRandomFakeReportsTest(const TriggerSpecs& specs,
+void RunRandomFakeReportsTest(const TriggerDataSet& trigger_data,
                               const EventReportWindows& event_report_windows,
                               const MaxEventLevelReports max_reports,
                               const int num_samples,
                               const double tolerance) {
   std::map<std::vector<FakeEventLevelReport>, int> output_counts;
-  ASSERT_OK_AND_ASSIGN(const uint32_t num_states,
-                       GetNumStates(specs, event_report_windows, max_reports));
+  ASSERT_OK_AND_ASSIGN(
+      const uint32_t num_states,
+      GetNumStates(trigger_data, event_report_windows, max_reports));
   for (int i = 0; i < num_samples; i++) {
     // Use epsilon = 0 to ensure that random data is always sampled from the RR
     // mechanism.
     ASSERT_OK_AND_ASSIGN(
         RandomizedResponseData response,
-        DoRandomizedResponse(specs, event_report_windows, max_reports,
+        DoRandomizedResponse(trigger_data, event_report_windows, max_reports,
                              /*epsilon=*/0, SourceType::kNavigation,
                              /*scopes_data=*/std::nullopt,
                              PrivacyMathConfig()));
@@ -552,7 +553,7 @@
   // For the distribution check, the probability of failure with `tolerance` is
   // at most 1e-9.
   RunRandomFakeReportsTest(
-      TriggerSpecs(SourceType::kEvent),
+      TriggerDataSet(SourceType::kEvent),
       *EventReportWindows::FromDefaults(base::Days(30), SourceType::kEvent),
       MaxEventLevelReports(1),
       /*num_samples=*/100'000,
@@ -567,7 +568,7 @@
   //
   // For the distribution check, the probability of failure with `tolerance` is
   // at most .0002.
-  RunRandomFakeReportsTest(TriggerSpecs(SourceType::kNavigation),
+  RunRandomFakeReportsTest(TriggerDataSet(SourceType::kNavigation),
                            *EventReportWindows::FromDefaults(
                                base::Days(30), SourceType::kNavigation),
                            MaxEventLevelReports(3),
@@ -583,7 +584,7 @@
   // For the distribution check, the probability of failure with `tolerance` is
   // at most 1e-9.
 
-  const auto kSpecs = *TriggerSpecs::Create({
+  const auto kTriggerData = *TriggerDataSet::Create({
       1,
       5,
       3,
@@ -597,10 +598,11 @@
   const MaxEventLevelReports kMaxReports(2);
 
   // The distribution check will fail with probability 6e-7.
-  ASSERT_OK_AND_ASSIGN(const uint32_t num_states,
-                       GetNumStates(kSpecs, kEventReportWindows, kMaxReports));
+  ASSERT_OK_AND_ASSIGN(
+      const uint32_t num_states,
+      GetNumStates(kTriggerData, kEventReportWindows, kMaxReports));
   EXPECT_EQ(45, num_states);
-  RunRandomFakeReportsTest(kSpecs, kEventReportWindows, kMaxReports,
+  RunRandomFakeReportsTest(kTriggerData, kEventReportWindows, kMaxReports,
                            /*num_samples=*/100'000,
                            /*tolerance=*/0.1);
 }
@@ -631,21 +633,23 @@
   for (const auto& test_case : kNumStateTestCases) {
     const auto event_report_windows =
         EventReportWindowsWithCount(test_case.num_report_windows);
-    const auto specs =
-        TriggerSpecsWithCardinality(test_case.trigger_data_cardinality);
+    const auto trigger_data =
+        TriggerDataSetWithCardinality(test_case.trigger_data_cardinality);
     EXPECT_EQ(test_case.expected_num_states,
-              GetNumStates(specs, event_report_windows, test_case.max_reports));
+              GetNumStates(trigger_data, event_report_windows,
+                           test_case.max_reports));
   }
 }
 
-TEST(PrivacyMathTest, NumStatesForTriggerSpecs_UniqueSampling) {
+TEST(PrivacyMathTest, NumStatesForTriggerDataSet_UniqueSampling) {
   for (const auto& test_case : kNumStateTestCases) {
     const auto event_report_windows =
         EventReportWindowsWithCount(test_case.num_report_windows);
-    const auto specs =
-        TriggerSpecsWithCardinality(test_case.trigger_data_cardinality);
+    const auto trigger_data =
+        TriggerDataSetWithCardinality(test_case.trigger_data_cardinality);
     ASSERT_EQ(test_case.expected_num_states,
-              GetNumStates(specs, event_report_windows, test_case.max_reports));
+              GetNumStates(trigger_data, event_report_windows,
+                           test_case.max_reports));
 
     if (!test_case.expected_num_states.has_value() ||
         *test_case.expected_num_states > 10000) {
@@ -655,7 +659,7 @@
     std::set<std::vector<FakeEventLevelReport>> seen_outputs;
     for (uint32_t i = 0; i < *test_case.expected_num_states; i++) {
       if (auto output = internal::GetFakeReportsForSequenceIndex(
-              specs, event_report_windows, test_case.max_reports, i);
+              trigger_data, event_report_windows, test_case.max_reports, i);
           output.has_value()) {
         seen_outputs.insert(*std::move(output));
       }
@@ -670,12 +674,12 @@
     base::HistogramTester histograms;
     const auto event_report_windows =
         EventReportWindowsWithCount(test_case.num_report_windows);
-    const auto specs =
-        TriggerSpecsWithCardinality(test_case.trigger_data_cardinality);
-    auto channel_capacity_response =
-        DoRandomizedResponse(specs, event_report_windows, test_case.max_reports,
-                             /*epsilon=*/14, SourceType::kNavigation,
-                             /*scopes_data=*/std::nullopt, PrivacyMathConfig());
+    const auto trigger_data =
+        TriggerDataSetWithCardinality(test_case.trigger_data_cardinality);
+    auto channel_capacity_response = DoRandomizedResponse(
+        trigger_data, event_report_windows, test_case.max_reports,
+        /*epsilon=*/14, SourceType::kNavigation,
+        /*scopes_data=*/std::nullopt, PrivacyMathConfig());
 
     if (test_case.expected_num_states.has_value()) {
       histograms.ExpectUniqueSample(
@@ -691,9 +695,9 @@
 // Regression test for http://crbug.com/1503728 in which the optimized
 // randomized-response incorrectly returned the trigger data *index* rather than
 // the trigger data *value* in the fake reports.
-TEST(PrivacyMathTest, NonDefaultTriggerDataForSingleSharedSpec) {
+TEST(PrivacyMathTest, NonDefaultTriggerData) {
   // Note that the trigger data does not start at 0.
-  const auto kSpecs = *TriggerSpecs::Create({123});
+  const auto kTriggerData = *TriggerDataSet::Create({123});
   const EventReportWindows kEventReportWindows;
   const MaxEventLevelReports kMaxReports(1);
 
@@ -704,7 +708,7 @@
   do {
     ASSERT_OK_AND_ASSIGN(
         RandomizedResponseData response_data,
-        DoRandomizedResponse(kSpecs, kEventReportWindows, kMaxReports,
+        DoRandomizedResponse(kTriggerData, kEventReportWindows, kMaxReports,
                              /*epsilon=*/0, SourceType::kNavigation,
                              /*scopes_data=*/std::nullopt,
                              PrivacyMathConfig()));
@@ -718,7 +722,7 @@
   constexpr PrivacyMathConfig kConfig{.max_channel_capacity_navigation = 1};
 
   auto channel_capacity_response =
-      DoRandomizedResponse(TriggerSpecs(SourceType::kNavigation),
+      DoRandomizedResponse(TriggerDataSet(SourceType::kNavigation),
                            EventReportWindows(), MaxEventLevelReports(1),
                            /*epsilon=*/14, SourceType::kNavigation,
                            /*scopes_data=*/std::nullopt, kConfig);
@@ -730,7 +734,7 @@
 TEST(PrivacyMathTest, RandomizedResponse_ExceedsScopesChannelCapacity) {
   // Navigation
   auto channel_capacity_response = DoRandomizedResponse(
-      TriggerSpecs(SourceType::kNavigation), EventReportWindows(),
+      TriggerDataSet(SourceType::kNavigation), EventReportWindows(),
       MaxEventLevelReports(1),
       /*epsilon=*/14, SourceType::kNavigation,
       AttributionScopesData::Create(AttributionScopesSet({"1"}),
@@ -744,7 +748,7 @@
 
   // Event
   channel_capacity_response = DoRandomizedResponse(
-      TriggerSpecs(SourceType::kEvent), EventReportWindows(),
+      TriggerDataSet(SourceType::kEvent), EventReportWindows(),
       MaxEventLevelReports(1),
       /*epsilon=*/14, SourceType::kEvent,
       AttributionScopesData::Create(AttributionScopesSet({"1"}),
@@ -757,21 +761,21 @@
       ErrorIs(RandomizedResponseError::kExceedsScopesChannelCapacityLimit));
 }
 
-// Regression test for http://crbug.com/1504144 in which empty specs cause an
-// invalid iterator dereference and thus a crash.
+// Regression test for http://crbug.com/1504144 in which empty trigger data
+// causes an invalid iterator dereference and thus a crash.
 TEST(PrivacyMathTest, UnaryChannel) {
   const struct {
     const char* desc;
-    TriggerSpecs trigger_specs;
+    TriggerDataSet trigger_data;
     MaxEventLevelReports max_reports;
   } kTestCases[] = {
       {
-          .desc = "empty-specs",
+          .desc = "empty-trigger-data",
           .max_reports = MaxEventLevelReports(20),
       },
       {
           .desc = "zero-max-reports",
-          .trigger_specs = TriggerSpecs(SourceType::kNavigation),
+          .trigger_data = TriggerDataSet(SourceType::kNavigation),
           .max_reports = MaxEventLevelReports(0),
       },
   };
@@ -781,15 +785,15 @@
 
     ASSERT_OK_AND_ASSIGN(
         const uint32_t num_states,
-        GetNumStates(test_case.trigger_specs, EventReportWindows(),
+        GetNumStates(test_case.trigger_data, EventReportWindows(),
                      test_case.max_reports));
     EXPECT_EQ(1u, num_states);
 
     EXPECT_EQ(RandomizedResponseData(
                   /*rate=*/1,
                   /*response=*/std::vector<FakeEventLevelReport>()),
-              DoRandomizedResponse(test_case.trigger_specs,
-                                   EventReportWindows(), test_case.max_reports,
+              DoRandomizedResponse(test_case.trigger_data, EventReportWindows(),
+                                   test_case.max_reports,
                                    /*epsilon=*/0, SourceType::kNavigation,
                                    /*scopes_data=*/std::nullopt,
                                    PrivacyMathConfig()));
@@ -797,7 +801,7 @@
 }
 
 TEST(PrivacyMathTest, IsValid) {
-  const TriggerSpecs kSpecs(SourceType::kNavigation);
+  const TriggerDataSet kTriggerData(SourceType::kNavigation);
   const auto kEventReportWindows = *EventReportWindows::FromDefaults(
       base::Days(30), SourceType::kNavigation);
   const MaxEventLevelReports kMaxReports(1);
@@ -870,7 +874,7 @@
 
   for (const auto& test_case : kTestCases) {
     SCOPED_TRACE(test_case.desc);
-    EXPECT_EQ(test_case.expected, IsValid(test_case.response, kSpecs,
+    EXPECT_EQ(test_case.expected, IsValid(test_case.response, kTriggerData,
                                           kEventReportWindows, kMaxReports));
   }
 }
diff --git a/components/attribution_reporting/registration.mojom b/components/attribution_reporting/registration.mojom
index 3eba2add..981a86d 100644
--- a/components/attribution_reporting/registration.mojom
+++ b/components/attribution_reporting/registration.mojom
@@ -72,8 +72,8 @@
   array<mojo_base.mojom.TimeDelta> end_times;
 };
 
-// https://wicg.github.io/attribution-reporting-api/#trigger-specs-header
-struct TriggerSpecs {
+// https://wicg.github.io/attribution-reporting-api/#trigger-data-set
+struct TriggerDataSet {
   array<uint32> trigger_data;
 };
 
@@ -130,7 +130,7 @@
   // Specifies how long this source is eligible for attribution.
   mojo_base.mojom.TimeDelta expiry;
 
-  TriggerSpecs trigger_specs;
+  TriggerDataSet trigger_data;
 
   EventReportWindows event_report_windows;
 
diff --git a/components/attribution_reporting/registration_mojom_traits.cc b/components/attribution_reporting/registration_mojom_traits.cc
index c6f3076..2eddaa0 100644
--- a/components/attribution_reporting/registration_mojom_traits.cc
+++ b/components/attribution_reporting/registration_mojom_traits.cc
@@ -178,17 +178,17 @@
 }
 
 // static
-bool StructTraits<attribution_reporting::mojom::TriggerSpecsDataView,
-                  attribution_reporting::TriggerSpecs>::
-    Read(attribution_reporting::mojom::TriggerSpecsDataView data,
-         attribution_reporting::TriggerSpecs* out) {
+bool StructTraits<attribution_reporting::mojom::TriggerDataSetDataView,
+                  attribution_reporting::TriggerDataSet>::
+    Read(attribution_reporting::mojom::TriggerDataSetDataView data,
+         attribution_reporting::TriggerDataSet* out) {
   std::vector<uint32_t> trigger_data;
   if (!data.ReadTriggerData(&trigger_data)) {
     return false;
   }
 
   auto result =
-      attribution_reporting::TriggerSpecs::Create(std::move(trigger_data));
+      attribution_reporting::TriggerDataSet::Create(std::move(trigger_data));
   if (!result.has_value()) {
     return false;
   }
@@ -351,7 +351,7 @@
     return false;
   }
 
-  if (!data.ReadTriggerSpecs(&out->trigger_specs)) {
+  if (!data.ReadTriggerData(&out->trigger_data)) {
     return false;
   }
 
diff --git a/components/attribution_reporting/registration_mojom_traits.h b/components/attribution_reporting/registration_mojom_traits.h
index c5712b4b..f17b3c1 100644
--- a/components/attribution_reporting/registration_mojom_traits.h
+++ b/components/attribution_reporting/registration_mojom_traits.h
@@ -140,15 +140,15 @@
 
 template <>
 struct COMPONENT_EXPORT(ATTRIBUTION_REPORTING_REGISTRATION_MOJOM_TRAITS)
-    StructTraits<attribution_reporting::mojom::TriggerSpecsDataView,
-                 attribution_reporting::TriggerSpecs> {
-  static const attribution_reporting::TriggerSpecs::TriggerData& trigger_data(
-      const attribution_reporting::TriggerSpecs& specs) {
-    return specs.trigger_data();
+    StructTraits<attribution_reporting::mojom::TriggerDataSetDataView,
+                 attribution_reporting::TriggerDataSet> {
+  static const attribution_reporting::TriggerDataSet::TriggerData& trigger_data(
+      const attribution_reporting::TriggerDataSet& trigger_data) {
+    return trigger_data.trigger_data();
   }
 
-  static bool Read(attribution_reporting::mojom::TriggerSpecsDataView data,
-                   attribution_reporting::TriggerSpecs* out);
+  static bool Read(attribution_reporting::mojom::TriggerDataSetDataView data,
+                   attribution_reporting::TriggerDataSet* out);
 };
 
 template <>
@@ -306,9 +306,9 @@
     return source.aggregatable_report_window;
   }
 
-  static const attribution_reporting::TriggerSpecs& trigger_specs(
+  static const attribution_reporting::TriggerDataSet& trigger_data(
       const attribution_reporting::SourceRegistration& source) {
-    return source.trigger_specs;
+    return source.trigger_data;
   }
 
   static const attribution_reporting::EventReportWindows& event_report_windows(
diff --git a/components/attribution_reporting/source_registration.cc b/components/attribution_reporting/source_registration.cc
index a7b4fcb..ac9870c 100644
--- a/components/attribution_reporting/source_registration.cc
+++ b/components/attribution_reporting/source_registration.cc
@@ -150,9 +150,9 @@
   ASSIGN_OR_RETURN(result.max_event_level_reports,
                    MaxEventLevelReports::Parse(registration, source_type));
 
-  ASSIGN_OR_RETURN(result.trigger_specs, TriggerSpecs::ParseTopLevelTriggerData(
-                                             registration, source_type,
-                                             result.trigger_data_matching));
+  ASSIGN_OR_RETURN(result.trigger_data,
+                   TriggerDataSet::Parse(registration, source_type,
+                                         result.trigger_data_matching));
 
   ASSIGN_OR_RETURN(result.filter_data,
                    FilterData::FromJSON(registration.Find(kFilterData)));
@@ -250,7 +250,7 @@
   event_report_windows.Serialize(dict);
   max_event_level_reports.Serialize(dict);
 
-  trigger_specs.Serialize(dict);
+  trigger_data.Serialize(dict);
 
   SerializeTimeDeltaInSeconds(dict, kAggregatableReportWindow,
                               aggregatable_report_window);
diff --git a/components/attribution_reporting/source_registration.h b/components/attribution_reporting/source_registration.h
index d44d1d0..a1c275e 100644
--- a/components/attribution_reporting/source_registration.h
+++ b/components/attribution_reporting/source_registration.h
@@ -74,7 +74,7 @@
   base::TimeDelta expiry = kMaxSourceExpiry;
   EventReportWindows event_report_windows;
   MaxEventLevelReports max_event_level_reports;
-  TriggerSpecs trigger_specs;
+  TriggerDataSet trigger_data;
   base::TimeDelta aggregatable_report_window = expiry;
   int64_t priority = 0;
   FilterData filter_data;
diff --git a/components/attribution_reporting/source_registration_error.mojom b/components/attribution_reporting/source_registration_error.mojom
index 10da578..14a99259 100644
--- a/components/attribution_reporting/source_registration_error.mojom
+++ b/components/attribution_reporting/source_registration_error.mojom
@@ -45,17 +45,17 @@
   kTriggerDataMatchingValueInvalid = 27,
 
   // Deprecated: kTriggerSpecsWrongType = 28,
-  kTriggerSpecTriggerDataMissing = 29,
-  kTriggerSpecTriggerDataListInvalid = 30,
+  // Deprecated: kTriggerSpecTriggerDataMissing = 29,
+  // Deprecated: kTriggerSpecTriggerDataListInvalid = 30,
   kTriggerDataListInvalid = 31,
   kDuplicateTriggerData = 32,
-  kTriggerSpecDuplicateTriggerData = 33,
+  // Deprecated: kTriggerSpecDuplicateTriggerData = 33,
 
   kExcessiveTriggerData = 34,
-  kTriggerSpecExcessiveTriggerData = 35,
+  // Deprecated: kTriggerSpecExcessiveTriggerData = 35,
   kInvalidTriggerDataForMatchingMode = 36,
 
-  kTopLevelTriggerDataAndTriggerSpecs = 37,
+  // Deprecated: kTopLevelTriggerDataAndTriggerSpecs = 37,
 
   // Deprecated: kSummaryOperatorValueInvalid = 38,
 
diff --git a/components/attribution_reporting/source_registration_unittest.cc b/components/attribution_reporting/source_registration_unittest.cc
index fe6e4dd..6c6a257 100644
--- a/components/attribution_reporting/source_registration_unittest.cc
+++ b/components/attribution_reporting/source_registration_unittest.cc
@@ -84,8 +84,8 @@
               Field(&SourceRegistration::source_event_id, 0),
               Field(&SourceRegistration::destination_set, destination),
               Field(&SourceRegistration::expiry, base::Days(30)),
-              Field(&SourceRegistration::trigger_specs,
-                    TriggerSpecs(SourceType::kNavigation)),
+              Field(&SourceRegistration::trigger_data,
+                    TriggerDataSet(SourceType::kNavigation)),
               Field(&SourceRegistration::event_report_windows,
                     *EventReportWindows::FromDefaults(base::Days(30),
                                                       SourceType::kNavigation)),
@@ -438,7 +438,7 @@
                 r.source_event_id = 7;
                 r.trigger_data_matching = mojom::TriggerDataMatching::kExact;
                 r.event_level_epsilon = EventLevelEpsilon(0);
-                r.trigger_specs = TriggerSpecs(SourceType::kNavigation);
+                r.trigger_data = TriggerDataSet(SourceType::kNavigation);
                 r.event_report_windows = EventReportWindows();
                 r.max_event_level_reports = MaxEventLevelReports(8);
                 r.aggregatable_debug_reporting_config =
diff --git a/components/attribution_reporting/test_utils.cc b/components/attribution_reporting/test_utils.cc
index 613a1f5..2e1de9a9 100644
--- a/components/attribution_reporting/test_utils.cc
+++ b/components/attribution_reporting/test_utils.cc
@@ -62,13 +62,13 @@
   return *EventReportWindows::Create(base::Days(0), std::move(deltas));
 }
 
-TriggerSpecs TriggerSpecsWithCardinality(int trigger_data_cardinality) {
-  TriggerSpecs::TriggerData trigger_data;
-  for (int i = 0; i < trigger_data_cardinality; ++i) {
+TriggerDataSet TriggerDataSetWithCardinality(int cardinality) {
+  TriggerDataSet::TriggerData trigger_data;
+  for (int i = 0; i < cardinality; ++i) {
     trigger_data.insert(i);
   }
 
-  return *TriggerSpecs::Create(std::move(trigger_data));
+  return *TriggerDataSet::Create(std::move(trigger_data));
 }
 
 std::ostream& operator<<(std::ostream& out,
@@ -158,8 +158,8 @@
              << ", debug_reporting=" << item.debug_reporting << "}";
 }
 
-std::ostream& operator<<(std::ostream& out, const TriggerSpecs& specs) {
-  return out << specs.ToJson();
+std::ostream& operator<<(std::ostream& out, const TriggerDataSet& set) {
+  return out << set.ToJson();
 }
 
 std::ostream& operator<<(
diff --git a/components/attribution_reporting/test_utils.h b/components/attribution_reporting/test_utils.h
index 405b529..4b4d6c73 100644
--- a/components/attribution_reporting/test_utils.h
+++ b/components/attribution_reporting/test_utils.h
@@ -31,7 +31,7 @@
 class RandomizedResponseData;
 class SourceAggregatableDebugReportingConfig;
 class SuitableOrigin;
-class TriggerSpecs;
+class TriggerDataSet;
 
 struct AggregatableDebugReportingConfig;
 struct AggregatableDedupKey;
@@ -46,7 +46,7 @@
     mojom::SourceType,
     std::optional<base::TimeDelta> lookback_window = std::nullopt);
 
-TriggerSpecs TriggerSpecsWithCardinality(int trigger_data_cardinality);
+TriggerDataSet TriggerDataSetWithCardinality(int cardinality);
 
 EventReportWindows EventReportWindowsWithCount(int num_report_windows);
 
@@ -85,7 +85,7 @@
 
 std::ostream& operator<<(std::ostream&, const OsRegistrationItem&);
 
-std::ostream& operator<<(std::ostream&, const TriggerSpecs&);
+std::ostream& operator<<(std::ostream&, const TriggerDataSet&);
 
 std::ostream& operator<<(std::ostream&, const AggregatableTriggerConfig&);
 
diff --git a/components/attribution_reporting/trigger_config.cc b/components/attribution_reporting/trigger_config.cc
index 4aa0dc8..c89f5a4 100644
--- a/components/attribution_reporting/trigger_config.cc
+++ b/components/attribution_reporting/trigger_config.cc
@@ -41,7 +41,7 @@
   }
 }
 
-base::expected<TriggerSpecs::TriggerData, SourceRegistrationError>
+base::expected<TriggerDataSet::TriggerData, SourceRegistrationError>
 ParseTriggerData(const base::Value& value) {
   const base::Value::List* list = value.GetIfList();
   if (!list) {
@@ -53,7 +53,7 @@
     return base::unexpected(SourceRegistrationError::kExcessiveTriggerData);
   }
 
-  TriggerSpecs::TriggerData trigger_data;
+  TriggerDataSet::TriggerData trigger_data;
   trigger_data.reserve(size);
 
   for (const base::Value& item : *list) {
@@ -70,13 +70,13 @@
   return trigger_data;
 }
 
-bool IsTriggerDataValid(const TriggerSpecs::TriggerData& trigger_data) {
+bool IsTriggerDataValid(const TriggerDataSet::TriggerData& trigger_data) {
   return trigger_data.size() <= kMaxTriggerDataPerSource;
 }
 
 base::expected<void, SourceRegistrationError>
 ValidateTriggerDataForTriggerDataMatching(
-    const TriggerSpecs::TriggerData& trigger_data,
+    const TriggerDataSet::TriggerData& trigger_data,
     TriggerDataMatching trigger_data_matching) {
   switch (trigger_data_matching) {
     case TriggerDataMatching::kExact:
@@ -128,7 +128,7 @@
   }
 }
 
-std::optional<uint32_t> TriggerSpecs::find(
+std::optional<uint32_t> TriggerDataSet::find(
     uint64_t trigger_data,
     TriggerDataMatching trigger_data_matching) const {
   switch (trigger_data_matching) {
@@ -150,14 +150,13 @@
 }
 
 // static
-base::expected<TriggerSpecs, SourceRegistrationError>
-TriggerSpecs::ParseTopLevelTriggerData(
+base::expected<TriggerDataSet, SourceRegistrationError> TriggerDataSet::Parse(
     const base::Value::Dict& registration,
     SourceType source_type,
     TriggerDataMatching trigger_data_matching) {
   const base::Value* trigger_data = registration.Find(kTriggerData);
   if (!trigger_data) {
-    return TriggerSpecs(source_type);
+    return TriggerDataSet(source_type);
   }
 
   ASSIGN_OR_RETURN(TriggerData trigger_data_set,
@@ -166,10 +165,10 @@
   RETURN_IF_ERROR(ValidateTriggerDataForTriggerDataMatching(
       trigger_data_set, trigger_data_matching));
 
-  return TriggerSpecs(std::move(trigger_data_set));
+  return TriggerDataSet(std::move(trigger_data_set));
 }
 
-TriggerSpecs::TriggerSpecs(SourceType source_type) {
+TriggerDataSet::TriggerDataSet(SourceType source_type) {
   uint32_t cardinality = DefaultTriggerDataCardinality(source_type);
 
   TriggerData::container_type trigger_data;
@@ -183,37 +182,37 @@
 }
 
 // static
-std::optional<TriggerSpecs> TriggerSpecs::Create(TriggerData trigger_data) {
+std::optional<TriggerDataSet> TriggerDataSet::Create(TriggerData trigger_data) {
   if (!IsTriggerDataValid(trigger_data)) {
     return std::nullopt;
   }
-  return TriggerSpecs(std::move(trigger_data));
+  return TriggerDataSet(std::move(trigger_data));
 }
 
-TriggerSpecs::TriggerSpecs(TriggerData trigger_data)
+TriggerDataSet::TriggerDataSet(TriggerData trigger_data)
     : trigger_data_(std::move(trigger_data)) {
   CHECK(IsTriggerDataValid(trigger_data_));
 }
 
-TriggerSpecs::TriggerSpecs() = default;
+TriggerDataSet::TriggerDataSet() = default;
 
-TriggerSpecs::~TriggerSpecs() = default;
+TriggerDataSet::~TriggerDataSet() = default;
 
-TriggerSpecs::TriggerSpecs(const TriggerSpecs&) = default;
+TriggerDataSet::TriggerDataSet(const TriggerDataSet&) = default;
 
-TriggerSpecs& TriggerSpecs::operator=(const TriggerSpecs&) = default;
+TriggerDataSet& TriggerDataSet::operator=(const TriggerDataSet&) = default;
 
-TriggerSpecs::TriggerSpecs(TriggerSpecs&&) = default;
+TriggerDataSet::TriggerDataSet(TriggerDataSet&&) = default;
 
-TriggerSpecs& TriggerSpecs::operator=(TriggerSpecs&&) = default;
+TriggerDataSet& TriggerDataSet::operator=(TriggerDataSet&&) = default;
 
-base::Value::Dict TriggerSpecs::ToJson() const {
+base::Value::Dict TriggerDataSet::ToJson() const {
   base::Value::Dict dict;
   Serialize(dict);
   return dict;
 }
 
-void TriggerSpecs::Serialize(base::Value::Dict& dict) const {
+void TriggerDataSet::Serialize(base::Value::Dict& dict) const {
   auto trigger_data_list =
       base::Value::List::with_capacity(trigger_data_.size());
 
diff --git a/components/attribution_reporting/trigger_config.h b/components/attribution_reporting/trigger_config.h
index dfdcb30..4743bfd 100644
--- a/components/attribution_reporting/trigger_config.h
+++ b/components/attribution_reporting/trigger_config.h
@@ -20,33 +20,29 @@
 
 namespace attribution_reporting {
 
-// Conceptually a map from `uint32_t` trigger data values to `TriggerSpec`s.
-class COMPONENT_EXPORT(ATTRIBUTION_REPORTING) TriggerSpecs {
+class COMPONENT_EXPORT(ATTRIBUTION_REPORTING) TriggerDataSet {
  public:
   using TriggerData = base::flat_set<uint32_t>;
 
-  // Parses the top-level `trigger_data` field.
-  static base::expected<TriggerSpecs, mojom::SourceRegistrationError>
-  ParseTopLevelTriggerData(const base::DictValue&,
-                           mojom::SourceType,
-                           mojom::TriggerDataMatching);
+  static base::expected<TriggerDataSet, mojom::SourceRegistrationError>
+  Parse(const base::DictValue&, mojom::SourceType, mojom::TriggerDataMatching);
 
-  static std::optional<TriggerSpecs> Create(TriggerData);
+  static std::optional<TriggerDataSet> Create(TriggerData);
 
-  // Creates specs matching no trigger data.
-  TriggerSpecs();
+  // Creates an empty set matching no trigger data.
+  TriggerDataSet();
 
-  // Creates specs with the default trigger data cardinality for the given
+  // Creates a set with the default trigger data cardinality for the given
   // source type.
-  explicit TriggerSpecs(mojom::SourceType);
+  explicit TriggerDataSet(mojom::SourceType);
 
-  ~TriggerSpecs();
+  ~TriggerDataSet();
 
-  TriggerSpecs(const TriggerSpecs&);
-  TriggerSpecs& operator=(const TriggerSpecs&);
+  TriggerDataSet(const TriggerDataSet&);
+  TriggerDataSet& operator=(const TriggerDataSet&);
 
-  TriggerSpecs(TriggerSpecs&&);
-  TriggerSpecs& operator=(TriggerSpecs&&);
+  TriggerDataSet(TriggerDataSet&&);
+  TriggerDataSet& operator=(TriggerDataSet&&);
 
   base::DictValue ToJson() const;
 
@@ -59,7 +55,7 @@
   //
   // Note: `TriggerDataMatching::kModulus` can still be applied
   // even if the trigger data does not form a contiguous range starting at 0.
-  // Such a combination is prohibited by `TriggerSpecs::Parse()`, but there is
+  // Such a combination is prohibited by `TriggerDataSet::Parse()`, but there is
   // still a well-defined meaning for it for arbitrary trigger data, so we do
   // not bother preventing it here, though we may do so in the future.
   std::optional<uint32_t> find(uint64_t trigger_data,
@@ -67,10 +63,11 @@
 
   const TriggerData& trigger_data() const { return trigger_data_; }
 
-  friend bool operator==(const TriggerSpecs&, const TriggerSpecs&) = default;
+  friend bool operator==(const TriggerDataSet&,
+                         const TriggerDataSet&) = default;
 
  private:
-  explicit TriggerSpecs(TriggerData);
+  explicit TriggerDataSet(TriggerData);
 
   TriggerData trigger_data_;
 };
diff --git a/components/attribution_reporting/trigger_config_unittest.cc b/components/attribution_reporting/trigger_config_unittest.cc
index 45ddcc7..4179e05 100644
--- a/components/attribution_reporting/trigger_config_unittest.cc
+++ b/components/attribution_reporting/trigger_config_unittest.cc
@@ -104,23 +104,23 @@
   }
 }
 
-TEST(TriggerSpecsTest, Default) {
-  EXPECT_THAT(TriggerSpecs(SourceType::kEvent),
-              Property(&TriggerSpecs::trigger_data, ElementsAre(0, 1)));
+TEST(TriggerDataSetTest, Default) {
+  EXPECT_THAT(TriggerDataSet(SourceType::kEvent),
+              Property(&TriggerDataSet::trigger_data, ElementsAre(0, 1)));
 
-  EXPECT_THAT(TriggerSpecs(SourceType::kNavigation),
-              Property(&TriggerSpecs::trigger_data,
+  EXPECT_THAT(TriggerDataSet(SourceType::kNavigation),
+              Property(&TriggerDataSet::trigger_data,
                        ElementsAre(0, 1, 2, 3, 4, 5, 6, 7)));
 }
 
-TEST(TriggerSpecsTest, Parse) {
+TEST(TriggerDataSetTest, Parse) {
   const struct {
     const char* desc;
     const char* json;
     SourceType source_type = SourceType::kNavigation;
     TriggerDataMatching trigger_data_matching = TriggerDataMatching::kExact;
 
-    ::testing::Matcher<base::expected<TriggerSpecs, SourceRegistrationError>>
+    ::testing::Matcher<base::expected<TriggerDataSet, SourceRegistrationError>>
         matches_top_level_trigger_data;
   } kTestCases[] = {
       {
@@ -128,14 +128,14 @@
           .json = R"json({})json",
           .source_type = SourceType::kNavigation,
           .matches_top_level_trigger_data =
-              ValueIs(TriggerSpecs(SourceType::kNavigation)),
+              ValueIs(TriggerDataSet(SourceType::kNavigation)),
       },
       {
           .desc = "missing_event",
           .json = R"json({})json",
           .source_type = SourceType::kEvent,
           .matches_top_level_trigger_data =
-              ValueIs(TriggerSpecs(SourceType::kEvent)),
+              ValueIs(TriggerDataSet(SourceType::kEvent)),
       },
       {
           .desc = "trigger_data_wrong_type",
@@ -147,7 +147,7 @@
           .desc = "trigger_data_empty",
           .json = R"json({"trigger_data": []})json",
           .matches_top_level_trigger_data =
-              ValueIs(Property(&TriggerSpecs::trigger_data, IsEmpty())),
+              ValueIs(Property(&TriggerDataSet::trigger_data, IsEmpty())),
       },
       {
           .desc = "trigger_data_too_long",
@@ -189,19 +189,19 @@
           .desc = "trigger_data_value_minimal",
           .json = R"json({"trigger_data": [0]})json",
           .matches_top_level_trigger_data =
-              ValueIs(Property(&TriggerSpecs::trigger_data, ElementsAre(0))),
+              ValueIs(Property(&TriggerDataSet::trigger_data, ElementsAre(0))),
       },
       {
           .desc = "trigger_data_value_maximal",
           .json = R"json({"trigger_data": [4294967295]})json",
           .matches_top_level_trigger_data = ValueIs(
-              Property(&TriggerSpecs::trigger_data, ElementsAre(4294967295))),
+              Property(&TriggerDataSet::trigger_data, ElementsAre(4294967295))),
       },
       {
           .desc = "trigger_data_value_trailing_zero",
           .json = R"json({"trigger_data": [2.0]})json",
           .matches_top_level_trigger_data =
-              ValueIs(Property(&TriggerSpecs::trigger_data, ElementsAre(2))),
+              ValueIs(Property(&TriggerDataSet::trigger_data, ElementsAre(2))),
       },
       {
           .desc = "trigger_data_value_duplicate",
@@ -218,7 +218,7 @@
             24, 25, 26, 27, 28, 29, 30, 31
           ]})json",
           .matches_top_level_trigger_data =
-              ValueIs(Property(&TriggerSpecs::trigger_data, SizeIs(32))),
+              ValueIs(Property(&TriggerDataSet::trigger_data, SizeIs(32))),
       },
       {
           .desc = "trigger_data_invalid_for_modulus_non_contiguous",
@@ -247,35 +247,33 @@
 
     const base::Value::Dict dict = base::test::ParseJsonDict(test_case.json);
 
-    EXPECT_THAT(
-        TriggerSpecs::ParseTopLevelTriggerData(dict, test_case.source_type,
-                                               test_case.trigger_data_matching),
-        test_case.matches_top_level_trigger_data);
+    EXPECT_THAT(TriggerDataSet::Parse(dict, test_case.source_type,
+                                      test_case.trigger_data_matching),
+                test_case.matches_top_level_trigger_data);
   }
 }
 
-TEST(TriggerSpecsTest, ToJson) {
-  const auto kSpecs = *TriggerSpecs::Create(
+TEST(TriggerDataSetTest, ToJson) {
+  const auto kSet = *TriggerDataSet::Create(
       /*trigger_data=*/{1, 5, 3, 4294967295});
 
   base::Value::Dict dict;
-  kSpecs.Serialize(dict);
+  kSet.Serialize(dict);
 
   EXPECT_THAT(dict, base::test::IsJson(R"json({
     "trigger_data": [1, 3, 5, 4294967295]
   })json"));
 }
 
-TEST(TriggerSpecsTest, Find) {
+TEST(TriggerDataSetTest, Find) {
   {
-    const TriggerSpecs kSpecs;
+    const TriggerDataSet kSet;
 
-    EXPECT_FALSE(kSpecs.find(/*trigger_data=*/1, TriggerDataMatching::kExact));
-    EXPECT_FALSE(
-        kSpecs.find(/*trigger_data=*/1, TriggerDataMatching::kModulus));
+    EXPECT_FALSE(kSet.find(/*trigger_data=*/1, TriggerDataMatching::kExact));
+    EXPECT_FALSE(kSet.find(/*trigger_data=*/1, TriggerDataMatching::kModulus));
   }
 
-  const auto kSpecs = *TriggerSpecs::Create(
+  const auto kSet = *TriggerDataSet::Create(
       /*trigger_data=*/{1, 3, 4, 5});
 
   const struct {
@@ -309,16 +307,16 @@
     SCOPED_TRACE(test_case.trigger_data);
 
     EXPECT_EQ(
-        kSpecs.find(test_case.trigger_data, test_case.trigger_data_matching),
+        kSet.find(test_case.trigger_data, test_case.trigger_data_matching),
         test_case.expected);
   }
 }
 
-// Technically redundant with `TriggerSpecsTest.Find`, but included to
-// demonstrate the expected behavior for real-world trigger specs, of which
-// `TriggerSpecs()` can return a subset.
-TEST(TriggerSpecsTest, Find_ModulusContiguous) {
-  const auto kSpecs = *TriggerSpecs::Create(
+// Technically redundant with `TriggerDataSetTest.Find`, but included to
+// demonstrate the expected behavior for real-world trigger data, of which
+// `TriggerDataSet()` can return a subset.
+TEST(TriggerDataSetTest, Find_ModulusContiguous) {
+  const auto kSet = *TriggerDataSet::Create(
       /*trigger_data=*/{0, 1, 2});
 
   const struct {
@@ -331,9 +329,8 @@
   for (const auto& test_case : kTestCases) {
     SCOPED_TRACE(test_case.trigger_data);
 
-    EXPECT_EQ(
-        kSpecs.find(test_case.trigger_data, TriggerDataMatching::kModulus),
-        test_case.expected);
+    EXPECT_EQ(kSet.find(test_case.trigger_data, TriggerDataMatching::kModulus),
+              test_case.expected);
   }
 }
 
diff --git a/components/autofill/core/browser/data_model/payments/credit_card.cc b/components/autofill/core/browser/data_model/payments/credit_card.cc
index b27be01..84ad2ab 100644
--- a/components/autofill/core/browser/data_model/payments/credit_card.cc
+++ b/components/autofill/core/browser/data_model/payments/credit_card.cc
@@ -222,6 +222,37 @@
   return obfuscated_string;
 }
 
+// static
+std::string_view CreditCard::GetBenefitSourceStringFromEnum(
+    BenefitSource benefit_source_enum) {
+  switch (benefit_source_enum) {
+    case BenefitSource::kSourceUnknown:
+      return "";
+    case BenefitSource::kSourceAmex:
+      return kAmexCardBenefitSource;
+    case BenefitSource::kSourceBmo:
+      return kBmoCardBenefitSource;
+    case BenefitSource::kSourceCurinos:
+      return kCurinosCardBenefitSource;
+  }
+  NOTREACHED();
+}
+
+// static
+CreditCard::BenefitSource CreditCard::GetEnumFromBenefitSourceString(
+    std::string_view benefit_source_string) {
+  if (benefit_source_string == kAmexCardBenefitSource) {
+    return BenefitSource::kSourceAmex;
+  }
+  if (benefit_source_string == kBmoCardBenefitSource) {
+    return BenefitSource::kSourceBmo;
+  }
+  if (benefit_source_string == kCurinosCardBenefitSource) {
+    return BenefitSource::kSourceCurinos;
+  }
+  return BenefitSource::kSourceUnknown;
+}
+
 CreditCard::CreditCard(const std::string& guid, const std::string& origin)
     : guid_(guid),
       origin_(origin),
diff --git a/components/autofill/core/browser/data_model/payments/credit_card.h b/components/autofill/core/browser/data_model/payments/credit_card.h
index 6b1cd3aa..e839131 100644
--- a/components/autofill/core/browser/data_model/payments/credit_card.h
+++ b/components/autofill/core/browser/data_model/payments/credit_card.h
@@ -83,6 +83,16 @@
     kExternalIssuer = 2,
   };
 
+  // The source of the card benefits. This must stay in sync with the proto
+  // enum in autofill_specifics.proto.
+  enum class BenefitSource {
+    kSourceUnknown = 0,
+    kSourceAmex = 1,
+    kSourceBmo = 2,
+    kSourceCurinos = 3,
+    kMaxValue = kSourceCurinos,
+  };
+
   // Whether the card has been enrolled in the virtual card feature. This must
   // stay in sync with the proto enum in autofill_specifics.proto. A java
   // IntDef@ is generated from this.
@@ -161,6 +171,15 @@
       int obfuscation_length,
       const std::u16string& digits);
 
+  // Conversion between benefit source enum and benefit source in string.
+  // Benefit source in string can be fully migrated to the enum after
+  // feature flag `AutofillEnableCardBenefitsSourceSync` is default-enabled
+  // and cleaned up.
+  static std::string_view GetBenefitSourceStringFromEnum(
+      BenefitSource benefit_source_enum);
+  static BenefitSource GetEnumFromBenefitSourceString(
+      std::string_view benefit_source);
+
   CreditCard(const std::string& guid, const std::string& origin);
 
   // Creates a server card. The type must be RecordType::kMaskedServerCard or
diff --git a/components/autofill/core/browser/data_model/payments/credit_card_unittest.cc b/components/autofill/core/browser/data_model/payments/credit_card_unittest.cc
index 47bee0c7..56ad27e 100644
--- a/components/autofill/core/browser/data_model/payments/credit_card_unittest.cc
+++ b/components/autofill/core/browser/data_model/payments/credit_card_unittest.cc
@@ -876,6 +876,37 @@
             FULL_SERVER_CARD, "John Dillinger", "423456789012", "01", "2010",
             "2", nullptr, false}));
 
+class BenefitSourceConversionTest
+    : public testing::TestWithParam<
+          std::pair<CreditCard::BenefitSource, std::string_view>> {
+ public:
+  CreditCard::BenefitSource GetBenefitSourceEnum() { return GetParam().first; }
+
+  std::string_view GetBenefitSourceString() { return GetParam().second; }
+};
+
+TEST_P(BenefitSourceConversionTest, GetBenefitSourceStringFromEnum) {
+  EXPECT_EQ(GetBenefitSourceString(),
+            CreditCard::GetBenefitSourceStringFromEnum(GetBenefitSourceEnum()));
+}
+
+TEST_P(BenefitSourceConversionTest, GetEnumFromBenefitSourceString) {
+  EXPECT_EQ(GetBenefitSourceEnum(), CreditCard::GetEnumFromBenefitSourceString(
+                                        GetBenefitSourceString()));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    CreditCardTest,
+    BenefitSourceConversionTest,
+    testing::Values(std::make_pair(CreditCard::BenefitSource::kSourceUnknown,
+                                   ""),
+                    std::make_pair(CreditCard::BenefitSource::kSourceAmex,
+                                   kAmexCardBenefitSource),
+                    std::make_pair(CreditCard::BenefitSource::kSourceBmo,
+                                   kBmoCardBenefitSource),
+                    std::make_pair(CreditCard::BenefitSource::kSourceCurinos,
+                                   kCurinosCardBenefitSource)));
+
 TEST(CreditCardTest, MatchingCardDetails) {
   CreditCard a(base::Uuid::GenerateRandomV4().AsLowercaseString(),
                std::string());
diff --git a/components/autofill/core/browser/suggestions/valuables/valuable_suggestion_generator.cc b/components/autofill/core/browser/suggestions/valuables/valuable_suggestion_generator.cc
index 9f35d1b..76efbb9 100644
--- a/components/autofill/core/browser/suggestions/valuables/valuable_suggestion_generator.cc
+++ b/components/autofill/core/browser/suggestions/valuables/valuable_suggestion_generator.cc
@@ -153,7 +153,9 @@
       SuggestionType::kLoyaltyCardEntry);
   submenu_suggestion.acceptability = Suggestion::Acceptability::kUnacceptable;
   submenu_suggestion.children = loyalty_card_suggestions;
-
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
+  submenu_suggestion.icon = Suggestion::Icon::kGoogleWalletMonochrome;
+#endif
   // There is at least one email, separator and manage addresses suggestion.
   CHECK(email_suggestions.size() >= 3);
   email_suggestions.insert(email_suggestions.end() - 1, submenu_suggestion);
diff --git a/components/autofill/core/browser/suggestions/valuables/valuable_suggestion_generator_unittest.cc b/components/autofill/core/browser/suggestions/valuables/valuable_suggestion_generator_unittest.cc
index 93b07380..59667b9 100644
--- a/components/autofill/core/browser/suggestions/valuables/valuable_suggestion_generator_unittest.cc
+++ b/components/autofill/core/browser/suggestions/valuables/valuable_suggestion_generator_unittest.cc
@@ -258,6 +258,8 @@
                                        IDS_AUTOFILL_MANAGE_LOYALTY_CARDS),
                                    Suggestion::Icon::kSettings)));
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
+  EXPECT_THAT(lc_submenu_suggestion,
+              HasIcon(Suggestion::Icon::kGoogleWalletMonochrome));
   EXPECT_THAT(lc_submenu_suggestion.children.back(),
               HasTrailingIcon(Suggestion::Icon::kGoogleWallet));
 #endif
diff --git a/components/autofill/core/browser/webdata/payments/payments_autofill_table.cc b/components/autofill/core/browser/webdata/payments/payments_autofill_table.cc
index dd1c71f7..463011620 100644
--- a/components/autofill/core/browser/webdata/payments/payments_autofill_table.cc
+++ b/components/autofill/core/browser/webdata/payments/payments_autofill_table.cc
@@ -103,6 +103,7 @@
 constexpr std::string_view kProductTermsUrl = "product_terms_url";
 constexpr std::string_view kCardInfoRetrievalEnrollmentState =
     "card_info_retrieval_enrollment_state";
+constexpr std::string_view kCardBenefitSource = "card_benefit_source";
 
 constexpr std::string_view kServerCardCloudTokenDataTable =
     "server_card_cloud_token_data";
@@ -480,6 +481,20 @@
   return reinterpret_cast<void*>(&table_key);
 }
 
+// TODO(crbug.com/417251716): Add logic to check `num` is a valid enum entry.
+template <typename T>
+  requires(std::is_enum_v<T>)
+bool IsValidValue(int num, T enum_type) {
+  return num >= 0 && num <= static_cast<int>(T::kMaxValue);
+}
+
+std::string_view ConvertToBenefitSource(int benefit_source_from_table) {
+  return IsValidValue(benefit_source_from_table, CreditCard::BenefitSource())
+             ? CreditCard::GetBenefitSourceStringFromEnum(
+                   static_cast<CreditCard::BenefitSource>(
+                       benefit_source_from_table))
+             : "";
+}
 }  // namespace
 
 PaymentsAutofillTable::PaymentsAutofillTable() = default;
@@ -601,6 +616,9 @@
     case 136:
       *update_compatible_version = false;
       return MigrateToVersion136AddPaymentInstrumentCreationOptionsTable();
+    case 141:
+      *update_compatible_version = false;
+      return MigrateToVersion141AddCardBenefitSourceColumn();
   }
   return true;
 }
@@ -929,7 +947,8 @@
                  kCardArtUrl,
                  kProductDescription,
                  kProductTermsUrl,
-                 kCardInfoRetrievalEnrollmentState},
+                 kCardInfoRetrievalEnrollmentState,
+                 kCardBenefitSource},
                 "LEFT OUTER JOIN server_card_metadata AS metadata USING (id)");
   while (s.Step()) {
     int index = 0;
@@ -978,6 +997,7 @@
     } else {
       index++;
     }
+    card->set_benefit_source(ConvertToBenefitSource(s.ColumnInt(index++)));
     // Add CVC to the the `card` if the CVC storage flag is enabled.
     if (base::FeatureList::IsEnabled(
             features::kAutofillEnableCvcStorageAndFilling)) {
@@ -1176,12 +1196,13 @@
 
   // Add all the masked cards.
   sql::Statement masked_insert;
-  InsertBuilder(db(), masked_insert, kMaskedCreditCardsTable,
-                {kId, kNetwork, kNameOnCard, kLastFour, kExpMonth, kExpYear,
-                 kBankName, kNickname, kCardIssuer, kCardIssuerId,
-                 kInstrumentId, kVirtualCardEnrollmentState,
-                 kVirtualCardEnrollmentType, kCardArtUrl, kProductDescription,
-                 kProductTermsUrl, kCardInfoRetrievalEnrollmentState});
+  InsertBuilder(
+      db(), masked_insert, kMaskedCreditCardsTable,
+      {kId, kNetwork, kNameOnCard, kLastFour, kExpMonth, kExpYear, kBankName,
+       kNickname, kCardIssuer, kCardIssuerId, kInstrumentId,
+       kVirtualCardEnrollmentState, kVirtualCardEnrollmentType, kCardArtUrl,
+       kProductDescription, kProductTermsUrl, kCardInfoRetrievalEnrollmentState,
+       kCardBenefitSource});
 
   int index;
   for (const CreditCard& card : credit_cards) {
@@ -1208,6 +1229,9 @@
     masked_insert.BindString(index++, card.product_terms_url().spec());
     masked_insert.BindInt(
         index++, static_cast<int>(card.card_info_retrieval_enrollment_state()));
+    masked_insert.BindInt(
+        index++, static_cast<int>(CreditCard::GetEnumFromBenefitSourceString(
+                     card.benefit_source())));
     masked_insert.Run();
     masked_insert.Reset(/*clear_bound_vars=*/true);
   }
@@ -2135,16 +2159,22 @@
                       {kSerializedValueEncrypted, "VARCHAR NOT NULL"}});
 }
 
+bool PaymentsAutofillTable::MigrateToVersion141AddCardBenefitSourceColumn() {
+  return AddColumn(db(), "masked_credit_cards", "card_benefit_source",
+                   "INTEGER DEFAULT 0");
+}
+
 void PaymentsAutofillTable::AddMaskedCreditCards(
     const std::vector<CreditCard>& credit_cards) {
   DCHECK_GT(db()->transaction_nesting(), 0);
   sql::Statement masked_insert;
-  InsertBuilder(db(), masked_insert, kMaskedCreditCardsTable,
-                {kId, kNetwork, kNameOnCard, kLastFour, kExpMonth, kExpYear,
-                 kBankName, kNickname, kCardIssuer, kCardIssuerId,
-                 kInstrumentId, kVirtualCardEnrollmentState,
-                 kVirtualCardEnrollmentType, kCardArtUrl, kProductDescription,
-                 kProductTermsUrl, kCardInfoRetrievalEnrollmentState});
+  InsertBuilder(
+      db(), masked_insert, kMaskedCreditCardsTable,
+      {kId, kNetwork, kNameOnCard, kLastFour, kExpMonth, kExpYear, kBankName,
+       kNickname, kCardIssuer, kCardIssuerId, kInstrumentId,
+       kVirtualCardEnrollmentState, kVirtualCardEnrollmentType, kCardArtUrl,
+       kProductDescription, kProductTermsUrl, kCardInfoRetrievalEnrollmentState,
+       kCardBenefitSource});
 
   int index;
   for (const CreditCard& card : credit_cards) {
@@ -2171,6 +2201,9 @@
     masked_insert.BindString(index++, card.product_terms_url().spec());
     masked_insert.BindInt(
         index++, static_cast<int>(card.card_info_retrieval_enrollment_state()));
+    masked_insert.BindInt(
+        index++, static_cast<int>(CreditCard::GetEnumFromBenefitSourceString(
+                     card.benefit_source())));
     masked_insert.Run();
     masked_insert.Reset(/*clear_bound_vars=*/true);
 
@@ -2241,7 +2274,8 @@
        {kCardIssuerId, "VARCHAR"},
        {kVirtualCardEnrollmentType, "INTEGER DEFAULT 0"},
        {kProductTermsUrl, "VARCHAR"},
-       {kCardInfoRetrievalEnrollmentState, "INTEGER DEFAULT 0"}});
+       {kCardInfoRetrievalEnrollmentState, "INTEGER DEFAULT 0"},
+       {kCardBenefitSource, "INTEGER DEFAULT 0"}});
 }
 
 bool PaymentsAutofillTable::InitMaskedIbansTable() {
diff --git a/components/autofill/core/browser/webdata/payments/payments_autofill_table.h b/components/autofill/core/browser/webdata/payments/payments_autofill_table.h
index b876ea3..aad96143 100644
--- a/components/autofill/core/browser/webdata/payments/payments_autofill_table.h
+++ b/components/autofill/core/browser/webdata/payments/payments_autofill_table.h
@@ -134,6 +134,11 @@
 //                      the card is not enrolled and is not eligible for
 //                      enrollment. kRetrievalUnenrolledAndEligible means the
 //                      card is not enrolled but is eligible for enrollment.
+//   card_benefit_source
+//                      The source of the saved benefit for this card. Can be a
+//                      card issuer or a third party platform represented by
+//                      an integer. Converted from CardBenefitSource enum from
+//                      the Chrome Sync response.
 // -----------------------------------------------------------------------------
 // server_card_cloud_token_data
 //                      Stores data related to Cloud Primary Account Number
@@ -596,6 +601,7 @@
   bool MigrateToVersion133RemoveLengthColumnFromMaskedIbansTable();
   bool MigrateToVersion135AddCardInfoRetrievalEnrollmentState();
   bool MigrateToVersion136AddPaymentInstrumentCreationOptionsTable();
+  bool MigrateToVersion141AddCardBenefitSourceColumn();
 
  private:
   // Adds to |masked_credit_cards| and updates |server_card_metadata|.
diff --git a/components/autofill/core/browser/webdata/payments/payments_autofill_table_unittest.cc b/components/autofill/core/browser/webdata/payments/payments_autofill_table_unittest.cc
index ff1b56a..9e98245 100644
--- a/components/autofill/core/browser/webdata/payments/payments_autofill_table_unittest.cc
+++ b/components/autofill/core/browser/webdata/payments/payments_autofill_table_unittest.cc
@@ -764,7 +764,8 @@
     feature.InitWithFeatureStates(
         {{features::kAutofillEnableCvcStorageAndFilling,
           is_cvc_storage_flag_enabled},
-         {features::kAutofillEnableCardInfoRuntimeRetrieval, true}});
+         {features::kAutofillEnableCardInfoRuntimeRetrieval, true},
+         {features::kAutofillEnableCardBenefitsSourceSync, true}});
 
     std::vector<CreditCard> inputs;
     inputs.emplace_back(CreditCard::RecordType::kMaskedServerCard, "a123");
@@ -784,6 +785,7 @@
     inputs[0].set_card_info_retrieval_enrollment_state(
         CreditCard::CardInfoRetrievalEnrollmentState::
             kRetrievalUnenrolledAndNotEligible);
+    inputs[0].set_benefit_source("");
 
     inputs.emplace_back(CreditCard::RecordType::kMaskedServerCard, "b456");
     inputs[1].SetRawInfo(CREDIT_CARD_NAME_FULL, u"Rick Roman");
@@ -805,6 +807,7 @@
     inputs[1].set_cvc(u"111");
     inputs[1].set_card_info_retrieval_enrollment_state(
         CreditCard::CardInfoRetrievalEnrollmentState::kRetrievalEnrolled);
+    inputs[1].set_benefit_source(kCurinosCardBenefitSource);
 
     // The CVC modification dates are set to `now` during insertion.
     const time_t now = base::Time::Now().ToTimeT();
@@ -870,6 +873,9 @@
     EXPECT_EQ(GURL("https://www.example_term.com"),
               outputs[1]->product_terms_url());
 
+    EXPECT_EQ("", outputs[0]->benefit_source());
+    EXPECT_EQ(kCurinosCardBenefitSource, outputs[1]->benefit_source());
+
     EXPECT_EQ(u"Fake description", outputs[0]->product_description());
 
     if (is_cvc_storage_flag_enabled) {
@@ -1105,8 +1111,10 @@
 TEST_F(PaymentsAutofillTableTest, SetServerCardsData) {
   // Set a card data.
   base::test::ScopedFeatureList feature;
-  feature.InitAndEnableFeature(
-      features::kAutofillEnableCardInfoRuntimeRetrieval);
+  feature.InitWithFeatures(
+      /*enabled_features=*/{features::kAutofillEnableCardInfoRuntimeRetrieval,
+                            features::kAutofillEnableCardBenefitsSourceSync},
+      /*disabled_features=*/{});
   std::vector<CreditCard> inputs;
   inputs.emplace_back(CreditCard::RecordType::kMaskedServerCard, "card1");
   inputs[0].SetRawInfo(CREDIT_CARD_NAME_FULL, u"Rick Roman");
@@ -1127,6 +1135,7 @@
   inputs[0].set_product_description(u"Fake description");
   inputs[0].set_card_info_retrieval_enrollment_state(
       CreditCard::CardInfoRetrievalEnrollmentState::kRetrievalEnrolled);
+  inputs[0].set_benefit_source(kAmexCardBenefitSource);
 
   table_->SetServerCardsData(inputs);
 
@@ -1158,6 +1167,7 @@
   EXPECT_EQ(u"Fake description", outputs[0]->product_description());
   EXPECT_EQ(CreditCard::CardInfoRetrievalEnrollmentState::kRetrievalEnrolled,
             outputs[0]->card_info_retrieval_enrollment_state());
+  EXPECT_EQ(kAmexCardBenefitSource, outputs[0]->benefit_source());
 
   // Make sure no metadata was added.
   std::vector<PaymentsMetadata> metadata;
@@ -1174,12 +1184,42 @@
   EXPECT_EQ("card2", outputs[0]->server_id());
   EXPECT_EQ(CreditCard::Issuer::kIssuerUnknown, outputs[0]->card_issuer());
   EXPECT_EQ("", outputs[0]->issuer_id());
+  EXPECT_EQ("", outputs[0]->benefit_source());
 
   // Make sure no metadata was added.
   ASSERT_TRUE(table_->GetServerCardsMetadata(metadata));
   ASSERT_EQ(0U, metadata.size());
 }
 
+// Tests that benefit source out of enum range will be converted to the default
+// unknown source.
+TEST_F(PaymentsAutofillTableTest,
+       GetServerCreditCards_BenefitSourceOutOfRange) {
+  base::test::ScopedFeatureList feature;
+  feature.InitAndEnableFeature(features::kAutofillEnableCardBenefitsSourceSync);
+
+  std::vector<CreditCard> inputs;
+  inputs.emplace_back(CreditCard::RecordType::kMaskedServerCard, "server id");
+  table_->SetServerCardsData(inputs);
+
+  // Insert a masked card entry with benefit source out of defined range.
+  sql::Statement update_masked_card(db_->GetSQLConnection()->GetUniqueStatement(
+      "UPDATE masked_credit_cards "
+      "SET card_benefit_source = ? "));
+  ASSERT_TRUE(update_masked_card.is_valid());
+  update_masked_card.BindInt(
+      0, static_cast<int>(CreditCard::BenefitSource::kMaxValue) + 1);
+  ASSERT_TRUE(update_masked_card.Run());
+
+  // Check the converted card has an unknown benefit source.
+  std::vector<std::unique_ptr<CreditCard>> outputs;
+  ASSERT_TRUE(table_->GetServerCreditCards(outputs));
+  ASSERT_EQ(1U, outputs.size());
+  EXPECT_EQ(
+      CreditCard::BenefitSource::kSourceUnknown,
+      CreditCard::GetEnumFromBenefitSourceString(outputs[0]->benefit_source()));
+}
+
 // Tests that adding server cards data does not delete the existing metadata.
 TEST_F(PaymentsAutofillTableTest, SetServerCardsData_ExistingMetadata) {
   // Create and set some metadata.
diff --git a/components/autofill_strings.grdp b/components/autofill_strings.grdp
index e2d07a5..64bacf0 100644
--- a/components/autofill_strings.grdp
+++ b/components/autofill_strings.grdp
@@ -98,6 +98,9 @@
   <message name="IDS_AUTOFILL_LOYALTY_CARDS_SUBMENU_TITLE" desc="The loyalty cards submenu suggestion text." translateable="false">
     Loyalty cards
   </message>
+  <message name="IDS_AUTOFILL_LOYALTY_CARDS_ALL_YOUR_CARDS_SUBMENU_TITLE" desc="The loyalty cards submenu containing all loyalty cards suggestion text." translateable="false">
+    All your loyalty cards
+  </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_CARDS_ALL_YOUR_CARDS_SUBMENU_TITLE.png.sha1 b/components/autofill_strings_grdp/IDS_AUTOFILL_LOYALTY_CARDS_ALL_YOUR_CARDS_SUBMENU_TITLE.png.sha1
new file mode 100644
index 0000000..a78620d
--- /dev/null
+++ b/components/autofill_strings_grdp/IDS_AUTOFILL_LOYALTY_CARDS_ALL_YOUR_CARDS_SUBMENU_TITLE.png.sha1
@@ -0,0 +1 @@
+a4d7bd6edb544012008908bfb83c90539c2e7671
\ No newline at end of file
diff --git a/components/blocked_content/safe_browsing_triggered_popup_blocker_unittest.cc b/components/blocked_content/safe_browsing_triggered_popup_blocker_unittest.cc
index 727d530a..398e693 100644
--- a/components/blocked_content/safe_browsing_triggered_popup_blocker_unittest.cc
+++ b/components/blocked_content/safe_browsing_triggered_popup_blocker_unittest.cc
@@ -35,6 +35,7 @@
 #include "content/public/browser/navigation_throttle.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/test/mock_navigation_handle.h"
+#include "content/public/test/mock_navigation_throttle_registry.h"
 #include "content/public/test/navigation_simulator.h"
 #include "content/public/test/test_navigation_throttle_inserter.h"
 #include "content/public/test/test_renderer_host.h"
@@ -139,18 +140,17 @@
   HostContentSettingsMap* settings_map() { return settings_map_.get(); }
 
  protected:
-  std::unique_ptr<content::NavigationThrottle> CreateThrottle(
-      content::NavigationHandle* handle) {
+  void CreateThrottle(content::NavigationThrottleRegistry& registry) {
     // Activation is only computed when navigating a subresource filter root
     // (see content_subresource_filter_throttle_manager.h for the definition of
     // a root).
-    if (subresource_filter::IsInSubresourceFilterRoot(handle)) {
-      return std::make_unique<
-          subresource_filter::SafeBrowsingPageActivationThrottle>(
-          handle, /*delegate=*/nullptr, fake_safe_browsing_database_);
+    auto& handle = registry.GetNavigationHandle();
+    if (subresource_filter::IsInSubresourceFilterRoot(&handle)) {
+      registry.AddThrottle(
+          std::make_unique<
+              subresource_filter::SafeBrowsingPageActivationThrottle>(
+              registry, /*delegate=*/nullptr, fake_safe_browsing_database_));
     }
-
-    return nullptr;
   }
 
   base::test::ScopedFeatureList scoped_feature_list_;
@@ -480,7 +480,12 @@
     // abusive.
     content::MockNavigationHandle handle(url1, main_rfh());
     handle.set_has_committed(true);
-    auto throttle = CreateThrottle(&handle);
+    content::MockNavigationThrottleRegistry registry(
+        &handle,
+        content::MockNavigationThrottleRegistry::RegistrationMode::kHold);
+    CreateThrottle(registry);
+    ASSERT_EQ(registry.throttles().size(), 1u);
+    auto throttle = registry.throttles().back().get();
     auto result = throttle->WillProcessResponse();
     if (result.action() == content::NavigationThrottle::ThrottleAction::DEFER) {
       base::RunLoop loop;
@@ -502,7 +507,12 @@
     // abusive.
     content::MockNavigationHandle handle(url2, main_rfh());
     handle.set_has_committed(true);
-    auto throttle = CreateThrottle(&handle);
+    content::MockNavigationThrottleRegistry registry(
+        &handle,
+        content::MockNavigationThrottleRegistry::RegistrationMode::kHold);
+    CreateThrottle(registry);
+    ASSERT_EQ(registry.throttles().size(), 1u);
+    auto throttle = registry.throttles().back().get();
     auto result = throttle->WillProcessResponse();
     if (result.action() == content::NavigationThrottle::ThrottleAction::DEFER) {
       base::RunLoop loop;
@@ -525,7 +535,12 @@
     content::MockNavigationHandle handle(url2, main_rfh());
     handle.set_has_committed(true);
     handle.set_is_in_primary_main_frame(false);
-    auto throttle = CreateThrottle(&handle);
+    content::MockNavigationThrottleRegistry registry(
+        &handle,
+        content::MockNavigationThrottleRegistry::RegistrationMode::kHold);
+    CreateThrottle(registry);
+    ASSERT_EQ(registry.throttles().size(), 1u);
+    auto throttle = registry.throttles().back().get();
     auto result = throttle->WillProcessResponse();
     if (result.action() == content::NavigationThrottle::ThrottleAction::DEFER) {
       base::RunLoop loop;
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/ClipDrawableProgressBar.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/ClipDrawableProgressBar.java
index a8a7198..546e0cb 100644
--- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/ClipDrawableProgressBar.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/ClipDrawableProgressBar.java
@@ -9,6 +9,9 @@
 import android.graphics.Rect;
 import android.graphics.drawable.ClipDrawable;
 import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.LayerDrawable;
 import android.util.AttributeSet;
 import android.view.Gravity;
 import android.widget.ImageView;
@@ -49,7 +52,10 @@
     // http://developer.android.com/reference/android/graphics/drawable/ClipDrawable.html
     private static final int CLIP_DRAWABLE_MAX = 10000;
 
-    private final ColorDrawable mForegroundDrawable;
+    @Nullable private ColorDrawable mForegroundColorDrawable;
+    @Nullable private GradientDrawable mForegroundGradientDrawable;
+    @Nullable private GradientDrawable mBackgroundGradientDrawable;
+    private int mForegroundColor;
     private int mBackgroundColor;
     private float mProgress;
     private int mDesiredVisibility;
@@ -67,15 +73,68 @@
 
         mDesiredVisibility = getVisibility();
 
-        int foregroundColor = SemanticColorUtils.getProgressBarForeground(getContext());
+        mForegroundColor = SemanticColorUtils.getProgressBarForeground(getContext());
         mBackgroundColor = getContext().getColor(R.color.progress_bar_bg_color_list);
-
-        mForegroundDrawable = new ColorDrawable(foregroundColor);
-        setImageDrawable(
-                new ClipDrawable(mForegroundDrawable, Gravity.START, ClipDrawable.HORIZONTAL));
+        initializeDrawables();
         setBackgroundColor(mBackgroundColor);
     }
 
+    /**
+     * Initializes the underlying drawables for the progress bar.
+     * If {@link #useGradientDrawable()} is true, this sets up a {@link LayerDrawable}
+     * with a moving foreground and background components, otherwise it sets up just a moving
+     * foreground component with a plain background.
+     */
+    private void initializeDrawables() {
+        if (useGradientDrawable()) {
+            mForegroundGradientDrawable = createGradientDrawable(mForegroundColor);
+            ClipDrawable foregroundClipDrawable =
+                    new ClipDrawable(mForegroundGradientDrawable, Gravity.START,
+                            ClipDrawable.HORIZONTAL);
+
+            mBackgroundGradientDrawable = createGradientDrawable(mBackgroundColor);
+            ClipDrawable backgroundClipDrawable = new ClipDrawable(
+                    mBackgroundGradientDrawable, Gravity.END, ClipDrawable.HORIZONTAL);
+            // Background will be fully visible.
+            backgroundClipDrawable.setLevel(CLIP_DRAWABLE_MAX);
+
+            // A layerDrawable with the 2 moving components, foreground and background.
+            Drawable[] layers = {foregroundClipDrawable, backgroundClipDrawable};
+
+            setImageDrawable(new LayerDrawable(layers));
+        } else {
+            mForegroundColorDrawable = new ColorDrawable(mForegroundColor);
+            setImageDrawable(
+                    new ClipDrawable(mForegroundColorDrawable, Gravity.START,
+                            ClipDrawable.HORIZONTAL));
+        }
+    }
+
+    /**
+     * Creates a new {@link GradientDrawable} with a rectangular shape and the specified color.
+     *
+     * @param color The color to set for the drawable.
+     * @return A new {@link GradientDrawable} instance.
+     */
+    private static GradientDrawable createGradientDrawable(int color) {
+        GradientDrawable drawable = new GradientDrawable();
+        drawable.setShape(GradientDrawable.RECTANGLE);
+        drawable.setColor(color);
+        return drawable;
+    }
+
+    /**
+     * Determines whether to use a {@link GradientDrawable} for the progress bar,
+     * which allows for a moving foreground and background. If false, only a moving foreground is
+     * used.
+     *
+     * @return True if {@link GradientDrawable} should be used, false otherwise.
+     * Currently, this always returns false.
+     */
+    protected boolean useGradientDrawable() {
+        return false;
+    }
+
     /** @param observer An update observer for the progress bar. */
     @VisibleForTesting
     public void setProgressBarObserver(ProgressBarObserver observer) {
@@ -102,13 +161,35 @@
         if (mProgress == progress) return;
 
         mProgress = progress;
-        getDrawable().setLevel(Math.round(progress * CLIP_DRAWABLE_MAX));
+
+        if (useGradientDrawable()) {
+            updateGradientDrawableProgress(progress);
+        } else {
+            getDrawable().setLevel(Math.round(progress * CLIP_DRAWABLE_MAX));
+        }
         if (mProgressBarObserver != null) mProgressBarObserver.onVisibleProgressUpdated();
     }
 
+    /**
+     * Updates the progress of the foreground and background drawables.
+     * The foreground drawable's level is set proportional to the progress,
+     * and the background drawable's level is set to the inverse of the progress.
+     *
+     * @param progress The current progress value, between 0.0 and 1.0.
+     */
+    private void updateGradientDrawableProgress(float progress) {
+        LayerDrawable layerDrawable = (LayerDrawable) getDrawable();
+        ClipDrawable foregroundClip = (ClipDrawable) layerDrawable.getDrawable(0);
+        foregroundClip.setLevel(Math.round(progress * CLIP_DRAWABLE_MAX));
+        if (layerDrawable.getNumberOfLayers() >= 2) {
+            ClipDrawable backgroundClip = (ClipDrawable) layerDrawable.getDrawable(1);
+            backgroundClip.setLevel(Math.round((1.0f - progress) * CLIP_DRAWABLE_MAX));
+        }
+    }
+
     /** @return Foreground color of the progress bar. */
     public int getForegroundColor() {
-        return mForegroundDrawable.getColor();
+        return mForegroundColor;
     }
 
     /**
@@ -124,9 +205,8 @@
      * @param drawingInfoOut An instance that the result will be written.
      */
     public void getDrawingInfo(DrawingInfo drawingInfoOut) {
-        int foregroundColor = mForegroundDrawable.getColor();
         float effectiveAlpha = getVisibility() == VISIBLE ? getAlpha() : 0.0f;
-        drawingInfoOut.progressBarColor = applyAlpha(foregroundColor, effectiveAlpha);
+        drawingInfoOut.progressBarColor = applyAlpha(mForegroundColor, effectiveAlpha);
         drawingInfoOut.progressBarBackgroundColor = applyAlpha(mBackgroundColor, effectiveAlpha);
 
         if (ViewCompat.getLayoutDirection(this) == LAYOUT_DIRECTION_LTR) {
@@ -174,15 +254,31 @@
         updateInternalVisibility();
     }
 
+    /**
+     * Sets the color for the background of the progress bar.
+     *
+     * @param color The new color of the progress bar background.
+     */
     @Override
     public void setBackgroundColor(int color) {
-        if (color == Color.TRANSPARENT) {
+        if (useGradientDrawable()) {
+            assert mBackgroundGradientDrawable != null;
+            if (color == Color.TRANSPARENT) {
+                mBackgroundGradientDrawable.setColor(null);
+                super.setBackground(null);
+            } else {
+                // The updated progress bar will have a fully transparent background and a
+                // moving background clip.
+                mBackgroundGradientDrawable.setColor(color);
+                mBackgroundColor = color;
+                super.setBackgroundColor(Color.TRANSPARENT);
+            }
+        } else if (color == Color.TRANSPARENT) {
             setBackground(null);
         } else {
             super.setBackgroundColor(color);
+            mBackgroundColor = color;
         }
-
-        mBackgroundColor = color;
     }
 
     /**
@@ -190,7 +286,13 @@
      * @param color The new color of the progress bar foreground.
      */
     public void setForegroundColor(int color) {
-        mForegroundDrawable.setColor(color);
+        if (useGradientDrawable() && mForegroundGradientDrawable != null) {
+            mForegroundGradientDrawable.setColor(color);
+        } else {
+            assert mForegroundColorDrawable != null;
+            mForegroundColorDrawable.setColor(color);
+        }
+        mForegroundColor = color;
     }
 
     @Override
diff --git a/components/component_updater/android/OWNERS b/components/component_updater/android/OWNERS
index 3b0be42..a2febe5 100644
--- a/components/component_updater/android/OWNERS
+++ b/components/component_updater/android/OWNERS
@@ -1,2 +1 @@
-avvall@chromium.org
 pbirk@chromium.org
diff --git a/components/cronet/gn2bp/gen_android_bp.py b/components/cronet/gn2bp/gen_android_bp.py
index da44ed99..4465d17 100755
--- a/components/cronet/gn2bp/gen_android_bp.py
+++ b/components/cronet/gn2bp/gen_android_bp.py
@@ -30,6 +30,8 @@
 REPOSITORY_ROOT = os.path.abspath(
     os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, os.pardir))
 sys.path.insert(0, REPOSITORY_ROOT)
+
+import components.cronet.tools.utils as cronet_utils
 import build.gn_helpers
 
 CRONET_LICENSE_NAME = "external_cronet_license"
@@ -242,8 +244,6 @@
     "buildtools/third_party/libc++abi": "third_party/libc++abi",
 }
 
-_MIN_SDK_VERSION = 30
-
 # Path for the protobuf sources in the standalone build.
 buildtools_protobuf_src = '//buildtools/protobuf/src'
 
@@ -2247,7 +2247,7 @@
 def create_java_module(bp_module_name, target, blueprint):
 
   def add_java_library_properties(module):
-    module.min_sdk_version = _MIN_SDK_VERSION
+    module.min_sdk_version = cronet_utils.MIN_SDK_VERSION_FOR_AOSP
     module.apex_available = [tethering_apex]
     module.defaults.add(java_framework_defaults_module)
     module.build_file_path = target.build_file_path
@@ -2451,7 +2451,7 @@
   # to already be present in AOSP (currently, in Android.extras.bp). See
   # https://r.android.com/3413202.
   module.header_libs = {f"{MODULE_PREFIX}repository_root_include_dirs_anchor"}
-  module.min_sdk_version = _MIN_SDK_VERSION
+  module.min_sdk_version = cronet_utils.MIN_SDK_VERSION_FOR_AOSP
   module.apex_available = [tethering_apex]
   blueprint.add_module(module)
   return module
@@ -2743,7 +2743,7 @@
     if module.type in ["rust_proc_macro", "rust_binary", "rust_ffi_static"]:
       module.crate_name = target.crate_name
       module.crate_root = gn_utils.label_to_path(target.crate_root)
-      module.min_sdk_version = _MIN_SDK_VERSION
+      module.min_sdk_version = cronet_utils.MIN_SDK_VERSION_FOR_AOSP
       module.apex_available = [tethering_apex]
       for arch_name, arch in target.get_archs().items():
         _set_rust_flags(module.target[arch_name], arch.rust_flags, arch_name)
@@ -3131,7 +3131,7 @@
   defaults.target['host'].compile_multilib = '64'
   defaults.stl = 'none'
   defaults.cpp_std = CPP_VERSION
-  defaults.min_sdk_version = _MIN_SDK_VERSION
+  defaults.min_sdk_version = cronet_utils.MIN_SDK_VERSION_FOR_AOSP
   defaults.apex_available.add(tethering_apex)
   return defaults
 
diff --git a/components/cronet/tools/utils.py b/components/cronet/tools/utils.py
index a7297a39..708a35f 100755
--- a/components/cronet/tools/utils.py
+++ b/components/cronet/tools/utils.py
@@ -22,9 +22,11 @@
 _MB_PATH = os.path.join(REPOSITORY_ROOT, 'tools/mb/mb.py')
 GN_PATH = os.path.join(REPOSITORY_ROOT, 'buildtools/linux64/gn')
 NINJA_PATH = os.path.join(REPOSITORY_ROOT, 'third_party/ninja/ninja')
+MIN_SDK_VERSION_FOR_AOSP = 30
 ARCHS = ['x86', 'x64', 'arm', 'arm64', 'riscv64']
 AOSP_EXTRA_ARGS = ('is_cronet_for_aosp_build=true', 'use_nss_certs=false',
-                   'use_allocator_shim=false')
+                   'use_allocator_shim=false',
+                   f'default_min_sdk_version={MIN_SDK_VERSION_FOR_AOSP}')
 _GN_ARG_MATCHER = re.compile("^.*=.*$")
 
 
@@ -157,9 +159,10 @@
 
 
 def get_gn_args_for_aosp(arch: str) -> List[str]:
-  default_args = get_android_gn_args(True, arch)
+  default_args = filter_gn_args(get_android_gn_args(True, arch),
+                                ["use_remoteexec", "default_min_sdk_version"])
   default_args.extend(AOSP_EXTRA_ARGS)
-  return filter_gn_args(default_args, ["use_remoteexec"])
+  return default_args
 
 
 def android_gn_gen(is_release, target_cpu, out_dir):
diff --git a/components/dbus/menu/menu.h b/components/dbus/menu/menu.h
index 319f1b5..08a8bf1 100644
--- a/components/dbus/menu/menu.h
+++ b/components/dbus/menu/menu.h
@@ -78,7 +78,7 @@
     const raw_ptr<ui::MenuModel> menu;
     // |containing_menu| will be null for the root item.  If it's null, then
     // |containing_menu_index| is meaningless.
-    const raw_ptr<ui::MenuModel> containing_menu;
+    const raw_ptr<ui::MenuModel, DanglingUntriaged> containing_menu;
     const size_t containing_menu_index;
   };
 
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 51d4dd93..8a2990bd 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
@@ -70,17 +70,18 @@
       : throttle_inserter_(
             web_contents,
             base::BindLambdaForTesting(
-                [error, error_page_contents](content::NavigationHandle* handle)
-                    -> std::unique_ptr<content::NavigationThrottle> {
+                [error, error_page_contents](
+                    content::NavigationThrottleRegistry& registry) -> void {
                   auto throttle =
-                      std::make_unique<content::TestNavigationThrottle>(handle);
+                      std::make_unique<content::TestNavigationThrottle>(
+                          registry);
                   throttle->SetResponse(
                       content::TestNavigationThrottle::WILL_START_REQUEST,
                       content::TestNavigationThrottle::SYNCHRONOUS,
                       content::NavigationThrottle::ThrottleCheckResult(
                           content::NavigationThrottle::CANCEL, error,
                           error_page_contents));
-                  return throttle;
+                  registry.AddThrottle(std::move(throttle));
                 })) {}
   ~CustomErrorPageThrottleInserter() = default;
 
@@ -96,9 +97,9 @@
  public:
   class DeferringThrottle : public content::NavigationThrottle {
    public:
-    explicit DeferringThrottle(content::NavigationHandle* handle,
+    explicit DeferringThrottle(content::NavigationThrottleRegistry& registry,
                                base::OnceClosure callback)
-        : NavigationThrottle(handle), callback_(std::move(callback)) {}
+        : NavigationThrottle(registry), callback_(std::move(callback)) {}
 
     ~DeferringThrottle() override = default;
 
@@ -141,16 +142,15 @@
   }
 
  private:
-  std::unique_ptr<content::NavigationThrottle> MaybeCreateThrottle(
-      content::NavigationHandle* handle) {
+  void MaybeCreateThrottle(content::NavigationThrottleRegistry& registry) {
     if (throttle_) {
-      return nullptr;
+      return;
     }
 
     auto throttle = std::make_unique<DeferringThrottle>(
-        handle, defer_wait_loop_.QuitClosure());
+        registry, defer_wait_loop_.QuitClosure());
     throttle_ = throttle.get();
-    return throttle;
+    registry.AddThrottle(std::move(throttle));
   }
 
   const content::TestNavigationThrottleInserter throttle_inserter_;
diff --git a/components/facilitated_payments/android/BUILD.gn b/components/facilitated_payments/android/BUILD.gn
index 90f89b6..1e2ffd5 100644
--- a/components/facilitated_payments/android/BUILD.gn
+++ b/components/facilitated_payments/android/BUILD.gn
@@ -6,6 +6,8 @@
 
 static_library("android") {
   sources = [
+    "device_delegate_android.cc",
+    "device_delegate_android.h",
     "facilitated_payments_api_client_android.cc",
     "facilitated_payments_api_client_android.h",
     "secure_payload_android.cc",
diff --git a/components/facilitated_payments/android/device_delegate_android.cc b/components/facilitated_payments/android/device_delegate_android.cc
new file mode 100644
index 0000000..03ac4f82
--- /dev/null
+++ b/components/facilitated_payments/android/device_delegate_android.cc
@@ -0,0 +1,19 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/facilitated_payments/android/device_delegate_android.h"
+
+#include "base/android/jni_android.h"
+
+// Must come after all headers that specialize FromJniType() / ToJniType().
+#include "components/facilitated_payments/android/java/jni_headers/DeviceDelegate_jni.h"
+
+namespace payments::facilitated {
+
+bool IsWalletEligibleForPixAccountLinking() {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  return Java_DeviceDelegate_isWalletEligibleForPixAccountLinking(env);
+}
+
+}  // namespace payments::facilitated
diff --git a/components/facilitated_payments/android/device_delegate_android.h b/components/facilitated_payments/android/device_delegate_android.h
new file mode 100644
index 0000000..3d351c6
--- /dev/null
+++ b/components/facilitated_payments/android/device_delegate_android.h
@@ -0,0 +1,16 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_FACILITATED_PAYMENTS_ANDROID_DEVICE_DELEGATE_ANDROID_H_
+#define COMPONENTS_FACILITATED_PAYMENTS_ANDROID_DEVICE_DELEGATE_ANDROID_H_
+
+namespace payments::facilitated {
+
+// Returns true if Google Wallet is installed, and its version supports Pix
+// account linking.
+bool IsWalletEligibleForPixAccountLinking();
+
+}  // namespace payments::facilitated
+
+#endif  // COMPONENTS_FACILITATED_PAYMENTS_ANDROID_DEVICE_DELEGATE_ANDROID_H_
diff --git a/components/facilitated_payments/android/java/BUILD.gn b/components/facilitated_payments/android/java/BUILD.gn
index d94cec0..7e179f94 100644
--- a/components/facilitated_payments/android/java/BUILD.gn
+++ b/components/facilitated_payments/android/java/BUILD.gn
@@ -8,6 +8,7 @@
 
 android_library("java") {
   sources = [
+    "src/org/chromium/components/facilitated_payments/DeviceDelegate.java",
     "src/org/chromium/components/facilitated_payments/FacilitatedPaymentsApiClient.java",
     "src/org/chromium/components/facilitated_payments/FacilitatedPaymentsApiClientBridge.java",
     "src/org/chromium/components/facilitated_payments/SecureData.java",
@@ -27,6 +28,7 @@
 
 generate_jni("jni_headers") {
   sources = [
+    "src/org/chromium/components/facilitated_payments/DeviceDelegate.java",
     "src/org/chromium/components/facilitated_payments/FacilitatedPaymentsApiClientBridge.java",
     "src/org/chromium/components/facilitated_payments/SecureData.java",
     "src/org/chromium/components/facilitated_payments/SecurePayload.java",
diff --git a/components/facilitated_payments/android/java/src/org/chromium/components/facilitated_payments/DeviceDelegate.java b/components/facilitated_payments/android/java/src/org/chromium/components/facilitated_payments/DeviceDelegate.java
new file mode 100644
index 0000000..9fcf114
--- /dev/null
+++ b/components/facilitated_payments/android/java/src/org/chromium/components/facilitated_payments/DeviceDelegate.java
@@ -0,0 +1,45 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.components.facilitated_payments;
+
+import android.content.pm.PackageInfo;
+
+import org.jni_zero.CalledByNative;
+import org.jni_zero.JNINamespace;
+
+import org.chromium.base.PackageUtils;
+import org.chromium.build.annotations.NullMarked;
+
+/** A JNI bridge to allow the native side to interact with the Android device. */
+@JNINamespace("payments::facilitated")
+@NullMarked
+public class DeviceDelegate {
+    private static final String GOOGLE_WALLET_PACKAGE_NAME = "com.google.android.apps.walletnfcrel";
+    // Minimum Google Wallet version that supports Pix account linking.
+    private static final long PIX_MIN_SUPPORTED_WALLET_VERSION = 931593518;
+
+    private DeviceDelegate() {}
+
+    /**
+     * The Pix account linking prompt redirects to the Google Wallet app on acceptance. Checks if
+     * Wallet is eligible for Pix account linking.
+     *
+     * @return True if Google Wallet is installed, and the Wallet version supports Pix account
+     *     linking.
+     */
+    @CalledByNative
+    private static boolean isWalletEligibleForPixAccountLinking() {
+        PackageInfo walletPackageInfo =
+                PackageUtils.getPackageInfo(GOOGLE_WALLET_PACKAGE_NAME, /* flags= */ 0);
+
+        // {@link PackageInfo} is null if the package is not installed.
+        if (walletPackageInfo == null) {
+            return false;
+        }
+        // Verify Google Wallet version supports Pix account linking.
+        return PackageUtils.packageVersionCode(walletPackageInfo)
+                >= PIX_MIN_SUPPORTED_WALLET_VERSION;
+    }
+}
diff --git a/components/facilitated_payments/core/browser/facilitated_payments_client.cc b/components/facilitated_payments/core/browser/facilitated_payments_client.cc
index 0836291..320aa05 100644
--- a/components/facilitated_payments/core/browser/facilitated_payments_client.cc
+++ b/components/facilitated_payments/core/browser/facilitated_payments_client.cc
@@ -16,9 +16,9 @@
 
 namespace payments::facilitated {
 
-FacilitatedPaymentsClient::FacilitatedPaymentsClient() {
-  pix_account_linking_manager_ = std::make_unique<PixAccountLinkingManager>();
-}
+FacilitatedPaymentsClient::FacilitatedPaymentsClient()
+    : pix_account_linking_manager_(
+          std::make_unique<PixAccountLinkingManager>(/* client= */ this)) {}
 
 FacilitatedPaymentsClient::~FacilitatedPaymentsClient() = default;
 
@@ -39,6 +39,10 @@
 void FacilitatedPaymentsClient::SetUiEventListener(
     base::RepeatingCallback<void(UiEvent)> ui_event_listener) {}
 
+bool FacilitatedPaymentsClient::IsPixAccountLinkingSupported() const {
+  return false;
+}
+
 void FacilitatedPaymentsClient::InitPixAccountLinkingFlow() {
   pix_account_linking_manager_->MaybeShowPixAccountLinkingPrompt();
 }
diff --git a/components/facilitated_payments/core/browser/facilitated_payments_client.h b/components/facilitated_payments/core/browser/facilitated_payments_client.h
index 2d6163e..e7f55ac 100644
--- a/components/facilitated_payments/core/browser/facilitated_payments_client.h
+++ b/components/facilitated_payments/core/browser/facilitated_payments_client.h
@@ -113,6 +113,9 @@
   // returned so check before use.
   virtual autofill::StrikeDatabase* GetStrikeDatabase() = 0;
 
+  // Checks if Pix account linking is supported by the platform.
+  virtual bool IsPixAccountLinkingSupported() const;
+
   // Virtual so it can be overridden in tests.
   virtual void InitPixAccountLinkingFlow();
 
diff --git a/components/facilitated_payments/core/browser/pix_account_linking_manager.cc b/components/facilitated_payments/core/browser/pix_account_linking_manager.cc
index 1c56f2cf..c80e20a 100644
--- a/components/facilitated_payments/core/browser/pix_account_linking_manager.cc
+++ b/components/facilitated_payments/core/browser/pix_account_linking_manager.cc
@@ -4,8 +4,19 @@
 
 #include "components/facilitated_payments/core/browser/pix_account_linking_manager.h"
 
+#include "base/check_deref.h"
+#include "components/facilitated_payments/core/browser/facilitated_payments_client.h"
+
 namespace payments::facilitated {
 
-void PixAccountLinkingManager::MaybeShowPixAccountLinkingPrompt() {}
+PixAccountLinkingManager::PixAccountLinkingManager(
+    FacilitatedPaymentsClient* client)
+    : client_(CHECK_DEREF(client)) {}
+
+void PixAccountLinkingManager::MaybeShowPixAccountLinkingPrompt() {
+  if (!client_->IsPixAccountLinkingSupported()) {
+    return;
+  }
+}
 
 }  // namespace payments::facilitated
diff --git a/components/facilitated_payments/core/browser/pix_account_linking_manager.h b/components/facilitated_payments/core/browser/pix_account_linking_manager.h
index c761d93..89396f0 100644
--- a/components/facilitated_payments/core/browser/pix_account_linking_manager.h
+++ b/components/facilitated_payments/core/browser/pix_account_linking_manager.h
@@ -5,8 +5,12 @@
 #ifndef COMPONENTS_FACILITATED_PAYMENTS_CORE_BROWSER_PIX_ACCOUNT_LINKING_MANAGER_H_
 #define COMPONENTS_FACILITATED_PAYMENTS_CORE_BROWSER_PIX_ACCOUNT_LINKING_MANAGER_H_
 
+#include "base/memory/raw_ref.h"
+
 namespace payments::facilitated {
 
+class FacilitatedPaymentsClient;
+
 // A cross-platform interface that manages the Pix account linking flow. It is
 // owned by `FacilitatedPaymentsClient`. There is 1 instance of this class per
 // tab. Its lifecycle is same as that of `FacilitatedPaymentsClient`.
@@ -17,11 +21,16 @@
 // the tab, and not a single frame.
 class PixAccountLinkingManager {
  public:
+  explicit PixAccountLinkingManager(FacilitatedPaymentsClient* client);
   virtual ~PixAccountLinkingManager() = default;
 
   // Initialize the Pix account linking flow. Virtual so it can be overridden in
   // tests.
   virtual void MaybeShowPixAccountLinkingPrompt();
+
+ private:
+  // Owner.
+  const raw_ref<FacilitatedPaymentsClient> client_;
 };
 
 }  // namespace payments::facilitated
diff --git a/components/fingerprinting_protection_filter/browser/fingerprinting_protection_child_navigation_throttle.cc b/components/fingerprinting_protection_filter/browser/fingerprinting_protection_child_navigation_throttle.cc
index 062580c..c9bdb00 100644
--- a/components/fingerprinting_protection_filter/browser/fingerprinting_protection_child_navigation_throttle.cc
+++ b/components/fingerprinting_protection_filter/browser/fingerprinting_protection_child_navigation_throttle.cc
@@ -27,13 +27,13 @@
 
 FingerprintingProtectionChildNavigationThrottle::
     FingerprintingProtectionChildNavigationThrottle(
-        content::NavigationHandle* handle,
+        content::NavigationThrottleRegistry& registry,
         subresource_filter::AsyncDocumentSubresourceFilter* parent_frame_filter,
         bool is_incognito,
         base::RepeatingCallback<std::string(const GURL& url)>
             disallow_message_callback)
     : subresource_filter::ChildFrameNavigationFilteringThrottle(
-          handle,
+          registry,
           parent_frame_filter,
           /*alias_check_enabled=*/
           base::FeatureList::IsEnabled(
diff --git a/components/fingerprinting_protection_filter/browser/fingerprinting_protection_child_navigation_throttle.h b/components/fingerprinting_protection_filter/browser/fingerprinting_protection_child_navigation_throttle.h
index af858c8..cdaeefc3 100644
--- a/components/fingerprinting_protection_filter/browser/fingerprinting_protection_child_navigation_throttle.h
+++ b/components/fingerprinting_protection_filter/browser/fingerprinting_protection_child_navigation_throttle.h
@@ -11,10 +11,6 @@
 
 class GURL;
 
-namespace content {
-class NavigationHandle;
-}  // namespace content
-
 namespace subresource_filter {
 class AsyncDocumentSubresourceFilter;
 }  // namespace subresource_filter
@@ -27,7 +23,7 @@
     : public subresource_filter::ChildFrameNavigationFilteringThrottle {
  public:
   FingerprintingProtectionChildNavigationThrottle(
-      content::NavigationHandle* handle,
+      content::NavigationThrottleRegistry& registry,
       subresource_filter::AsyncDocumentSubresourceFilter* parent_frame_filter,
       bool is_incognito,
       base::RepeatingCallback<std::string(const GURL& url)>
diff --git a/components/fingerprinting_protection_filter/browser/fingerprinting_protection_child_navigation_throttle_unittest.cc b/components/fingerprinting_protection_filter/browser/fingerprinting_protection_child_navigation_throttle_unittest.cc
index 6340d3bc..f563504 100644
--- a/components/fingerprinting_protection_filter/browser/fingerprinting_protection_child_navigation_throttle_unittest.cc
+++ b/components/fingerprinting_protection_filter/browser/fingerprinting_protection_child_navigation_throttle_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/command_line.h"
 #include "base/functional/bind.h"
 #include "base/strings/stringprintf.h"
+#include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "components/fingerprinting_protection_filter/common/fingerprinting_protection_filter_features.h"
@@ -18,6 +19,7 @@
 #include "components/variations/variations_switches.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/test/navigation_simulator.h"
+#include "content/public/test/test_navigation_throttle_inserter.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
@@ -81,29 +83,43 @@
 
   ~FingerprintingProtectionChildNavigationThrottleTest() override = default;
 
+  void SetUp() override {
+    ChildFrameNavigationFilteringThrottleTestHarness::SetUp();
+    throttle_inserter_ =
+        std::make_unique<content::TestNavigationThrottleInserter>(
+            content::RenderViewHostTestHarness::web_contents(),
+            base::BindLambdaForTesting(
+                [&](content::NavigationThrottleRegistry& registry) -> void {
+                  // The |parent_filter_| is the parent frame's filter. Do not
+                  // register a throttle if the parent is not activated with a
+                  // valid filter.
+                  if (parent_filter_) {
+                    auto throttle = std::make_unique<
+                        FingerprintingProtectionChildNavigationThrottle>(
+                        registry, parent_filter_.get(),
+                        /*is_incognito=*/GetParam(),
+                        base::BindRepeating([](const GURL& filtered_url) {
+                          // TODO(https://crbug.com/40280666): Implement new
+                          // console message.
+                          return base::StringPrintf(
+                              kDisallowedConsoleMessageFormat,
+                              filtered_url.possibly_invalid_spec().c_str());
+                        }));
+                    ASSERT_EQ("FingerprintingProtectionChildNavigationThrottle",
+                              std::string(throttle->GetNameForLogging()));
+                    registry.AddThrottle(std::move(throttle));
+                  }
+                }));
+  }
+
   // content::WebContentsObserver:
   void DidStartNavigation(
       content::NavigationHandle* navigation_handle) override {
     ASSERT_FALSE(navigation_handle->IsInMainFrame());
-    // The |parent_filter_| is the parent frame's filter. Do not register a
-    // throttle if the parent is not activated with a valid filter.
-    if (parent_filter_) {
-      auto throttle =
-          std::make_unique<FingerprintingProtectionChildNavigationThrottle>(
-              navigation_handle, parent_filter_.get(),
-              /*is_incognito=*/GetParam(),
-              base::BindRepeating([](const GURL& filtered_url) {
-                // TODO(https://crbug.com/40280666): Implement new console
-                // message.
-                return base::StringPrintf(
-                    kDisallowedConsoleMessageFormat,
-                    filtered_url.possibly_invalid_spec().c_str());
-              }));
-      ASSERT_EQ("FingerprintingProtectionChildNavigationThrottle",
-                std::string(throttle->GetNameForLogging()));
-      navigation_handle->RegisterThrottleForTesting(std::move(throttle));
-    }
   }
+
+ private:
+  std::unique_ptr<content::TestNavigationThrottleInserter> throttle_inserter_;
 };
 
 INSTANTIATE_TEST_SUITE_P(
diff --git a/components/fingerprinting_protection_filter/browser/throttle_manager.cc b/components/fingerprinting_protection_filter/browser/throttle_manager.cc
index 377b7a9d..0f30854 100644
--- a/components/fingerprinting_protection_filter/browser/throttle_manager.cc
+++ b/components/fingerprinting_protection_filter/browser/throttle_manager.cc
@@ -188,7 +188,7 @@
       }
       registry.AddThrottle(
           std::make_unique<FingerprintingProtectionChildNavigationThrottle>(
-              &navigation_handle, parent_filter, is_incognito_,
+              registry, parent_filter, is_incognito_,
               base::BindRepeating([](const GURL& url) {
                 return base::StringPrintf(
                     kDisallowChildFrameConsoleMessageFormat,
diff --git a/components/lens/lens_features.cc b/components/lens/lens_features.cc
index bdaab00..067b4e3 100644
--- a/components/lens/lens_features.cc
+++ b/components/lens/lens_features.cc
@@ -474,6 +474,10 @@
     kLensOverlayVisualSelectionUpdatesEnableBorderGlow{
         &kLensOverlayVisualSelectionUpdates, "enable-border-glow", true};
 
+constexpr base::FeatureParam<bool>
+    kLensOverlayVisualSelectionUpdatesCsbThumbnail{
+        &kLensOverlayVisualSelectionUpdates, "enable-csb-thumbnail", true};
+
 constexpr base::FeatureParam<std::string> kHomepageURLForLens{
     &kLensStandalone, "lens-homepage-url", "https://lens.google.com/v3/"};
 
@@ -999,6 +1003,11 @@
          kLensOverlayVisualSelectionUpdatesEnableBorderGlow.Get();
 }
 
+bool GetVisualSelectionUpdatesEnableCsbThumbnail() {
+  return base::FeatureList::IsEnabled(kLensOverlayVisualSelectionUpdates) &&
+         kLensOverlayVisualSelectionUpdatesCsbThumbnail.Get();
+}
+
 bool PageContentUploadRequestIdFixEnabled() {
   return kPageContentUploadRequestIdFix.Get();
 }
diff --git a/components/lens/lens_features.h b/components/lens/lens_features.h
index d1933ab..3ec446f 100644
--- a/components/lens/lens_features.h
+++ b/components/lens/lens_features.h
@@ -764,6 +764,10 @@
 COMPONENT_EXPORT(LENS_FEATURES)
 extern bool GetVisualSelectionUpdatesEnableBorderGlow();
 
+// Whether to enable the thumbnail in the contextual searchbox.
+COMPONENT_EXPORT(LENS_FEATURES)
+extern bool GetVisualSelectionUpdatesEnableCsbThumbnail();
+
 // Whether to fix the request id for page content upload requests. When enabled,
 // this will not increment the image upload request ID when the page content
 // upload request is sent.
diff --git a/components/messages/android/java/src/org/chromium/components/messages/MessageFeatureList.java b/components/messages/android/java/src/org/chromium/components/messages/MessageFeatureList.java
index 209f8d1f..0b34805 100644
--- a/components/messages/android/java/src/org/chromium/components/messages/MessageFeatureList.java
+++ b/components/messages/android/java/src/org/chromium/components/messages/MessageFeatureList.java
@@ -14,11 +14,24 @@
  */
 @NullMarked
 public abstract class MessageFeatureList {
+    public static final String MESSAGES_ACCESSIBILITY_EVENT_INVESTIGATIONS =
+            "MessagesAccessibilityEventInvestigations";
     public static final String MESSAGES_FOR_ANDROID_FULLY_VISIBLE_CALLBACK =
             "MessagesForAndroidFullyVisibleCallback";
     public static final String MESSAGES_ANDROID_EXTRA_HISTOGRAMS = "MessagesAndroidExtraHistograms";
     public static final String MESSAGES_CLOSE_BUTTON = "MessagesCloseButton";
 
+    public static int getMessagesAccessibilityEventInvestigationsParam() {
+        return getFieldTrialParamByFeatureAsInt(
+                MESSAGES_ACCESSIBILITY_EVENT_INVESTIGATIONS,
+                "messages_accessibility_events_investigations_param",
+                0);
+    }
+
+    public static boolean isMessagesAccessibilityEventInvestigationsEnabled() {
+        return MessageFeatureMap.isEnabled(MESSAGES_ACCESSIBILITY_EVENT_INVESTIGATIONS);
+    }
+
     public static boolean isFullyVisibleCallbackEnabled() {
         return MessageFeatureMap.isEnabled(MESSAGES_FOR_ANDROID_FULLY_VISIBLE_CALLBACK);
     }
@@ -30,4 +43,19 @@
     public static boolean isCloseButtonEnabled() {
         return MessageFeatureMap.isEnabled(MESSAGES_CLOSE_BUTTON);
     }
+
+    /**
+     * Returns a field trial param as an int for the specified feature.
+     *
+     * @param featureName The name of the feature to retrieve a param for.
+     * @param paramName The name of the param for which to get as an integer.
+     * @param defaultValue The integer value to use if the param is not available.
+     * @return The parameter value as an int. Default value if the feature does not exist or the
+     *     specified parameter does not exist or its string value does not represent an int.
+     */
+    private static int getFieldTrialParamByFeatureAsInt(
+            String featureName, String paramName, int defaultValue) {
+        return MessageFeatureMap.getInstance()
+                .getFieldTrialParamByFeatureAsInt(featureName, paramName, defaultValue);
+    }
 }
diff --git a/components/messages/android/messages_feature.cc b/components/messages/android/messages_feature.cc
index 9429f94..44c8235 100644
--- a/components/messages/android/messages_feature.cc
+++ b/components/messages/android/messages_feature.cc
@@ -6,7 +6,6 @@
 
 #include "base/android/feature_map.h"
 #include "base/feature_list.h"
-#include "base/metrics/field_trial_params.h"
 #include "base/no_destructor.h"
 
 // Must come after all headers that specialize FromJniType() / ToJniType().
@@ -17,6 +16,7 @@
 namespace {
 
 const base::Feature* const kFeaturesExposedToJava[] = {
+    &kMessagesAccessibilityEventInvestigations,
     &kMessagesForAndroidFullyVisibleCallback, &kMessagesAndroidExtraHistograms,
     &kMessagesCloseButton};
 
@@ -29,6 +29,9 @@
 
 }  // namespace
 
+BASE_FEATURE(kMessagesAccessibilityEventInvestigations,
+             "MessagesAccessibilityEventInvestigations",
+             base::FEATURE_DISABLED_BY_DEFAULT);
 
 BASE_FEATURE(kMessagesForAndroidFullyVisibleCallback,
              "MessagesForAndroidFullyVisibleCallback",
diff --git a/components/messages/android/messages_feature.h b/components/messages/android/messages_feature.h
index ae6e9145..c9ea205 100644
--- a/components/messages/android/messages_feature.h
+++ b/components/messages/android/messages_feature.h
@@ -6,9 +6,20 @@
 #define COMPONENTS_MESSAGES_ANDROID_MESSAGES_FEATURE_H_
 
 #include "base/feature_list.h"
+#include "base/metrics/field_trial_params.h"
 
 namespace messages {
 
+// Feature that allows for AccessibilityEvents to be sent in Java-side impl to
+// test possible crash solutions.
+BASE_DECLARE_FEATURE(kMessagesAccessibilityEventInvestigations);
+
+// A feature param of type int that corresponds to the possible approaches for
+// fixing the crash.
+const base::FeatureParam<int> kMessagesAccessibilityEventInvestigationsParam{
+    &kMessagesAccessibilityEventInvestigations,
+    "messages_accessibility_events_investigations_param", 0};
+
 // Feature that exposes a listener to notify whether the current message
 // is fully visible.
 BASE_DECLARE_FEATURE(kMessagesForAndroidFullyVisibleCallback);
diff --git a/components/network_hints/browser/simple_network_hints_handler_impl.cc b/components/network_hints/browser/simple_network_hints_handler_impl.cc
index 4023fde4..5731c98 100644
--- a/components/network_hints/browser/simple_network_hints_handler_impl.cc
+++ b/components/network_hints/browser/simple_network_hints_handler_impl.cc
@@ -23,9 +23,9 @@
 #include "net/dns/public/resolve_error_info.h"
 #include "net/log/net_log_with_source.h"
 #include "services/network/public/cpp/resolve_host_client_base.h"
+#include "services/network/public/mojom/connection_change_observer_client.mojom.h"
 #include "services/network/public/mojom/host_resolver.mojom.h"
 #include "services/network/public/mojom/network_context.mojom.h"
-#include "services/network/public/mojom/reconnect_event_observer.mojom.h"
 #include "url/gurl.h"
 
 namespace network_hints {
diff --git a/components/omnibox/browser/BUILD.gn b/components/omnibox/browser/BUILD.gn
index c0ec93b..9ac8eb2 100644
--- a/components/omnibox/browser/BUILD.gn
+++ b/components/omnibox/browser/BUILD.gn
@@ -256,6 +256,8 @@
     "omnibox_field_trial.h",
     "omnibox_log.cc",
     "omnibox_log.h",
+    "omnibox_logging_utils.cc",
+    "omnibox_logging_utils.h",
     "omnibox_metrics_provider.cc",
     "omnibox_metrics_provider.h",
     "omnibox_navigation_observer.h",
diff --git a/components/omnibox/browser/featured_search_provider.cc b/components/omnibox/browser/featured_search_provider.cc
index 5dcf8e5..06dace2 100644
--- a/components/omnibox/browser/featured_search_provider.cc
+++ b/components/omnibox/browser/featured_search_provider.cc
@@ -525,15 +525,11 @@
                         u" ";
   std::u16string link_text = l10n_util::GetStringUTF16(
       IDS_OMNIBOX_HISTORY_EMBEDDINGS_SETTINGS_PROMO_IPH_LINK_TEXT);
-  GURL link_url =
-      GURL(optimization_guide::features::IsAiSettingsPageRefreshEnabled()
-               ? "chrome://settings/ai/historySearch"
-               : "chrome://settings/historySearch");
   AddIPHMatch(IphType::kHistoryEmbeddingsSettingsPromo,
               /*iph_contents=*/text,
               /*matched_term=*/u"",
               /*iph_link_text=*/link_text,
-              /*iph_link_url=*/link_url,
+              /*iph_link_url=*/GURL("chrome://settings/ai/historySearch"),
               /*relevance=*/kIPHRelevance,
               /*deletable=*/true);
 }
@@ -553,15 +549,11 @@
       u" ";
   std::u16string link_text = l10n_util::GetStringUTF16(
       IDS_OMNIBOX_HISTORY_EMBEDDINGS_DISCLAIMER_IPH_LINK_TEXT);
-  GURL link_url =
-      GURL(optimization_guide::features::IsAiSettingsPageRefreshEnabled()
-               ? "chrome://settings/ai/historySearch"
-               : "chrome://settings/historySearch");
   AddIPHMatch(IphType::kHistoryEmbeddingsDisclaimer,
               /*iph_contents=*/text,
               /*matched_term=*/u"",
               /*iph_link_text=*/link_text,
-              /*iph_link_url=*/link_url,
+              /*iph_link_url=*/GURL("chrome://settings/ai/historySearch"),
               /*relevance=*/kIPHRelevance,
               /*deletable=*/false);
 }
diff --git a/components/omnibox/browser/featured_search_provider_unittest.cc b/components/omnibox/browser/featured_search_provider_unittest.cc
index 29f5e62..4a0893f 100644
--- a/components/omnibox/browser/featured_search_provider_unittest.cc
+++ b/components/omnibox/browser/featured_search_provider_unittest.cc
@@ -667,10 +667,7 @@
   // feature is enabled).
   {
     base::test::ScopedFeatureList features;
-    features.InitWithFeatures(
-        {{history_embeddings::kHistoryEmbeddings},
-         {optimization_guide::features::kAiSettingsPageRefresh}},
-        {});
+    features.InitWithFeatures({{history_embeddings::kHistoryEmbeddings}}, {});
     mock_setting(false, false);
     {
       SCOPED_TRACE("");
@@ -771,44 +768,6 @@
       RunAndVerifyIph(scope_input, {});
     }
   }
-
-  // TODO(crbug.com/362225975): Remove after AiSettingsPageRefresh is launched.
-  //   History Embeddings Promo points to chrome://settings/historySearch when
-  //   AI refresh flag is disabled.
-  {
-    base::test::ScopedFeatureList features_without_ai_refresh;
-    features_without_ai_refresh.InitWithFeatures(
-        {history_embeddings::kHistoryEmbeddings},
-        {optimization_guide::features::kAiSettingsPageRefresh});
-    mock_setting(true, false);
-    {
-      SCOPED_TRACE("");
-      RunAndVerifyIph(
-          scope_input,
-          {{IphType::kHistoryEmbeddingsSettingsPromo,
-            // Should end with whitespace since there's a link following it.
-            u"For a more powerful way to search your browsing history, turn "
-            u"on ",
-            u"History search, powered by AI",
-            GURL("chrome://settings/historySearch")}});
-    }
-
-    // History Embeddings Disclaimer points to chrome://settings/historySearch
-    // when AI refresh flag is disabled.
-    mock_setting(true, true);
-    {
-      SCOPED_TRACE("");
-      RunAndVerifyIph(
-          scope_input,
-          {{IphType::kHistoryEmbeddingsDisclaimer,
-            // Should end with whitespace since there's a link following it.
-            u"Your searches, best matches, and their page contents are sent to "
-            u"Google and may be seen by human reviewers to improve this "
-            u"feature. "
-            u"This is an experimental feature and won't always get it right. ",
-            u"Learn more", GURL("chrome://settings/historySearch")}});
-    }
-  }
 }
 
 TEST_F(FeaturedSearchProviderTest, IphShownLimit) {
diff --git a/components/omnibox/browser/omnibox_edit_model.cc b/components/omnibox/browser/omnibox_edit_model.cc
index 69cc7da..a9b45f2 100644
--- a/components/omnibox/browser/omnibox_edit_model.cc
+++ b/components/omnibox/browser/omnibox_edit_model.cc
@@ -49,6 +49,7 @@
 #include "components/omnibox/browser/omnibox_event_global_tracker.h"
 #include "components/omnibox/browser/omnibox_field_trial.h"
 #include "components/omnibox/browser/omnibox_log.h"
+#include "components/omnibox/browser/omnibox_logging_utils.h"
 #include "components/omnibox/browser/omnibox_metrics_provider.h"
 #include "components/omnibox/browser/omnibox_navigation_observer.h"
 #include "components/omnibox/browser/omnibox_popup_selection.h"
@@ -228,95 +229,6 @@
   return parts;
 }
 
-// This function provides a logging implementation that aligns with the original
-// definition of the `DEPRECATED_UMA_HISTOGRAM_MEDIUM_TIMES()` macro, which is
-// currently being used to log the `FocusToOpenTimeAnyPopupState3` Omnibox
-// metric.
-void LogHistogramMediumTimes(const std::string& histogram_name,
-                             base::TimeDelta elapsed) {
-  base::UmaHistogramCustomTimes(histogram_name, elapsed, base::Milliseconds(10),
-                                base::Minutes(3), 50);
-}
-
-void LogFocusToOpenTime(base::TimeDelta elapsed,
-                        bool is_zero_prefix,
-                        PageClassification page_classification,
-                        AutocompleteMatch& match,
-                        size_t action_index) {
-  LogHistogramMediumTimes("Omnibox.FocusToOpenTimeAnyPopupState3", elapsed);
-
-  std::string summarized_result_type;
-  switch (OmniboxMetricsProvider::GetClientSummarizedResultType(
-      match.GetOmniboxEventResultType(action_index))) {
-    case ClientSummarizedResultType::kSearch:
-      summarized_result_type = "SEARCH";
-      break;
-    case ClientSummarizedResultType::kUrl:
-      summarized_result_type = "URL";
-      break;
-    default:
-      summarized_result_type = "OTHER";
-      break;
-  }
-
-  LogHistogramMediumTimes(
-      base::StrCat(
-          {"Omnibox.FocusToOpenTimeAnyPopupState3.BySummarizedResultType.",
-           summarized_result_type}),
-      elapsed);
-
-  const std::string page_context =
-      OmniboxEventProto::PageClassification_Name(page_classification);
-  LogHistogramMediumTimes(
-      base::StrCat({"Omnibox.FocusToOpenTimeAnyPopupState3.ByPageContext.",
-                    page_context}),
-      elapsed);
-
-  LogHistogramMediumTimes(
-      base::StrCat(
-          {"Omnibox.FocusToOpenTimeAnyPopupState3.BySummarizedResultType.",
-           summarized_result_type, ".ByPageContext.", page_context}),
-      elapsed);
-
-  if (is_zero_prefix) {
-    LogHistogramMediumTimes("Omnibox.FocusToOpenTimeAnyPopupState3.ZeroSuggest",
-                            elapsed);
-    LogHistogramMediumTimes(
-        base::StrCat({"Omnibox.FocusToOpenTimeAnyPopupState3.ZeroSuggest."
-                      "BySummarizedResultType.",
-                      summarized_result_type}),
-        elapsed);
-    LogHistogramMediumTimes(
-        base::StrCat(
-            {"Omnibox.FocusToOpenTimeAnyPopupState3.ZeroSuggest.ByPageContext.",
-             page_context}),
-        elapsed);
-    LogHistogramMediumTimes(
-        base::StrCat({"Omnibox.FocusToOpenTimeAnyPopupState3.ZeroSuggest."
-                      "BySummarizedResultType.",
-                      summarized_result_type, ".ByPageContext.", page_context}),
-        elapsed);
-  } else {
-    LogHistogramMediumTimes(
-        "Omnibox.FocusToOpenTimeAnyPopupState3.TypedSuggest", elapsed);
-    LogHistogramMediumTimes(
-        base::StrCat({"Omnibox.FocusToOpenTimeAnyPopupState3.TypedSuggest."
-                      "BySummarizedResultType.",
-                      summarized_result_type}),
-        elapsed);
-    LogHistogramMediumTimes(
-        base::StrCat({"Omnibox.FocusToOpenTimeAnyPopupState3.TypedSuggest."
-                      "ByPageContext.",
-                      page_context}),
-        elapsed);
-    LogHistogramMediumTimes(
-        base::StrCat({"Omnibox.FocusToOpenTimeAnyPopupState3.TypedSuggest."
-                      "BySummarizedResultType.",
-                      summarized_result_type, ".ByPageContext.", page_context}),
-        elapsed);
-  }
-}
-
 }  // namespace
 
 // OmniboxEditModel::State ----------------------------------------------------
@@ -2698,9 +2610,10 @@
     elapsed_time_since_user_focused_omnibox = now - last_omnibox_focus_;
     // Only record focus to open time when a focus actually happened (as
     // opposed to, say, dragging a link onto the omnibox).
-    LogFocusToOpenTime(elapsed_time_since_user_focused_omnibox,
-                       input_.IsZeroSuggest(), GetPageClassification(), match,
-                       selection.IsAction() ? selection.action_index : -1);
+    omnibox::LogFocusToOpenTime(
+        elapsed_time_since_user_focused_omnibox, input_.IsZeroSuggest(),
+        GetPageClassification(), match,
+        selection.IsAction() ? selection.action_index : -1);
   }
 
   // In some unusual cases, we ignore autocomplete_controller()->result() and
diff --git a/components/omnibox/browser/omnibox_logging_utils.cc b/components/omnibox/browser/omnibox_logging_utils.cc
new file mode 100644
index 0000000..921825e
--- /dev/null
+++ b/components/omnibox/browser/omnibox_logging_utils.cc
@@ -0,0 +1,109 @@
+// 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/omnibox/browser/omnibox_logging_utils.h"
+
+#include "base/metrics/histogram_functions.h"
+#include "base/strings/strcat.h"
+#include "components/omnibox/browser/autocomplete_match.h"
+#include "components/omnibox/browser/omnibox_metrics_provider.h"
+
+namespace {
+
+// This function provides a logging implementation that aligns with the original
+// definition of the `DEPRECATED_UMA_HISTOGRAM_MEDIUM_TIMES()` macro, which is
+// currently being used to log the `FocusToOpenTimeAnyPopupState3` Omnibox
+// metric.
+void LogHistogramMediumTimes(const std::string& histogram_name,
+                             base::TimeDelta elapsed) {
+  base::UmaHistogramCustomTimes(histogram_name, elapsed, base::Milliseconds(10),
+                                base::Minutes(3), 50);
+}
+
+}  // namespace
+
+namespace omnibox {
+
+void LogFocusToOpenTime(
+    base::TimeDelta elapsed,
+    bool is_zero_prefix,
+    ::metrics::OmniboxEventProto::PageClassification page_classification,
+    const AutocompleteMatch& match,
+    size_t action_index) {
+  LogHistogramMediumTimes("Omnibox.FocusToOpenTimeAnyPopupState3", elapsed);
+
+  std::string summarized_result_type;
+  switch (OmniboxMetricsProvider::GetClientSummarizedResultType(
+      match.GetOmniboxEventResultType(action_index))) {
+    case ClientSummarizedResultType::kSearch:
+      summarized_result_type = "SEARCH";
+      break;
+    case ClientSummarizedResultType::kUrl:
+      summarized_result_type = "URL";
+      break;
+    default:
+      summarized_result_type = "OTHER";
+      break;
+  }
+
+  LogHistogramMediumTimes(
+      base::StrCat(
+          {"Omnibox.FocusToOpenTimeAnyPopupState3.BySummarizedResultType.",
+           summarized_result_type}),
+      elapsed);
+
+  const std::string page_context =
+      ::metrics::OmniboxEventProto::PageClassification_Name(
+          page_classification);
+  LogHistogramMediumTimes(
+      base::StrCat({"Omnibox.FocusToOpenTimeAnyPopupState3.ByPageContext.",
+                    page_context}),
+      elapsed);
+
+  LogHistogramMediumTimes(
+      base::StrCat(
+          {"Omnibox.FocusToOpenTimeAnyPopupState3.BySummarizedResultType.",
+           summarized_result_type, ".ByPageContext.", page_context}),
+      elapsed);
+
+  if (is_zero_prefix) {
+    LogHistogramMediumTimes("Omnibox.FocusToOpenTimeAnyPopupState3.ZeroSuggest",
+                            elapsed);
+    LogHistogramMediumTimes(
+        base::StrCat({"Omnibox.FocusToOpenTimeAnyPopupState3.ZeroSuggest."
+                      "BySummarizedResultType.",
+                      summarized_result_type}),
+        elapsed);
+    LogHistogramMediumTimes(
+        base::StrCat(
+            {"Omnibox.FocusToOpenTimeAnyPopupState3.ZeroSuggest.ByPageContext.",
+             page_context}),
+        elapsed);
+    LogHistogramMediumTimes(
+        base::StrCat({"Omnibox.FocusToOpenTimeAnyPopupState3.ZeroSuggest."
+                      "BySummarizedResultType.",
+                      summarized_result_type, ".ByPageContext.", page_context}),
+        elapsed);
+  } else {
+    LogHistogramMediumTimes(
+        "Omnibox.FocusToOpenTimeAnyPopupState3.TypedSuggest", elapsed);
+    LogHistogramMediumTimes(
+        base::StrCat({"Omnibox.FocusToOpenTimeAnyPopupState3.TypedSuggest."
+                      "BySummarizedResultType.",
+                      summarized_result_type}),
+        elapsed);
+    LogHistogramMediumTimes(
+        base::StrCat({"Omnibox.FocusToOpenTimeAnyPopupState3.TypedSuggest."
+                      "ByPageContext.",
+                      page_context}),
+        elapsed);
+    LogHistogramMediumTimes(
+        base::StrCat({"Omnibox.FocusToOpenTimeAnyPopupState3.TypedSuggest."
+                      "BySummarizedResultType.",
+                      summarized_result_type, ".ByPageContext.", page_context}),
+        elapsed);
+  }
+}
+
+}  // namespace omnibox
diff --git a/components/omnibox/browser/omnibox_logging_utils.h b/components/omnibox/browser/omnibox_logging_utils.h
new file mode 100644
index 0000000..50a4873
--- /dev/null
+++ b/components/omnibox/browser/omnibox_logging_utils.h
@@ -0,0 +1,26 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OMNIBOX_BROWSER_OMNIBOX_LOGGING_UTILS_H_
+#define COMPONENTS_OMNIBOX_BROWSER_OMNIBOX_LOGGING_UTILS_H_
+
+#include "base/time/time.h"
+#include "third_party/metrics_proto/omnibox_event.pb.h"
+
+struct AutocompleteMatch;
+
+namespace omnibox {
+
+// Logs metrics related to the time elapsed from when the user focused the
+// omnibox until a suggestion is opened/acted upon.
+void LogFocusToOpenTime(
+    base::TimeDelta elapsed,
+    bool is_zero_prefix,
+    ::metrics::OmniboxEventProto::PageClassification page_classification,
+    const AutocompleteMatch& match,
+    size_t action_index);
+
+}  // namespace omnibox
+
+#endif  // COMPONENTS_OMNIBOX_BROWSER_OMNIBOX_LOGGING_UTILS_H_
diff --git a/components/omnibox/common/omnibox_features.cc b/components/omnibox/common/omnibox_features.cc
index 55f926d..4f781ad5 100644
--- a/components/omnibox/common/omnibox_features.cc
+++ b/components/omnibox/common/omnibox_features.cc
@@ -337,6 +337,10 @@
              "IpadZeroSuggestMatches",
              enable_if(IS_IOS));
 
+// Updates various NTP/Omnibox assets and descriptions for visual alignment on
+// Android and iOS.
+BASE_FEATURE(kOmniboxMobileParityUpdate, "OmniboxMobileParityUpdate", DISABLED);
+
 // The features below allow tuning number of suggestions offered to users in
 // specific contexts. These features are default enabled and are used to control
 // related fieldtrial parameters.
@@ -401,9 +405,6 @@
 // Controls various Omnibox Diagnostics features.
 BASE_FEATURE(kDiagnostics, "OmniboxDiagnostics", DISABLED);
 
-// Updates various NTP/Omnibox assets and descriptions for visual alignment.
-BASE_FEATURE(kOmniboxMobileParityUpdate, "OmniboxMobileParityUpdate", DISABLED);
-
 namespace android {
 static jlong JNI_OmniboxFeatureMap_GetNativeMap(JNIEnv* env) {
   static const base::Feature* const kFeaturesExposedToJava[] = {
@@ -436,4 +437,12 @@
              "EnableSearchAggregatorPolicy",
              ENABLED);
 
+// If enabled, site search engines, defined by the `SiteSearchSettings` policy,
+// can be marked as user-overridable by administrators using an
+// `allow_user_override` field. This setting is stored in preferences and
+// determines if the engine can be overridden on the Settings page.
+BASE_FEATURE(kEnableSiteSearchAllowUserOverridePolicy,
+             "EnableSiteSearchAllowUserOverridePolicy",
+             DISABLED);
+
 }  // namespace omnibox
diff --git a/components/omnibox/common/omnibox_features.h b/components/omnibox/common/omnibox_features.h
index 9ed62dcd..aab6200be 100644
--- a/components/omnibox/common/omnibox_features.h
+++ b/components/omnibox/common/omnibox_features.h
@@ -122,6 +122,8 @@
 BASE_DECLARE_FEATURE(kOmniboxAsyncViewInflation);
 BASE_DECLARE_FEATURE(kUseFusedLocationProvider);
 
+BASE_DECLARE_FEATURE(kOmniboxMobileParityUpdate);
+
 // Omnibox suggestions tuning
 BASE_DECLARE_FEATURE(kNumNtpZpsRecentSearches);
 BASE_DECLARE_FEATURE(kNumNtpZpsTrendingSearches);
@@ -140,7 +142,6 @@
 // Delay focusTab to prioritize navigation (https://crbug.com/374852568).
 BASE_DECLARE_FEATURE(kPostDelayedTaskFocusTab);
 BASE_DECLARE_FEATURE(kAndroidHubSearchTabGroups);
-BASE_DECLARE_FEATURE(kOmniboxMobileParityUpdate);
 #endif  // BUILDFLAG(IS_ANDROID)
 
 // `ShortcutsProvider` features.
@@ -152,6 +153,9 @@
 // Ipad ZPS matches limit increase.
 BASE_DECLARE_FEATURE(kIpadZeroSuggestMatches);
 
+// Site search allow user override feature.
+BASE_DECLARE_FEATURE(kEnableSiteSearchAllowUserOverridePolicy);
+
 }  // namespace omnibox
 
 #endif  // COMPONENTS_OMNIBOX_COMMON_OMNIBOX_FEATURES_H_
diff --git a/components/optimization_guide/core/model_execution/model_execution_features.cc b/components/optimization_guide/core/model_execution/model_execution_features.cc
index cdba7d84..57632a7 100644
--- a/components/optimization_guide/core/model_execution/model_execution_features.cc
+++ b/components/optimization_guide/core/model_execution/model_execution_features.cc
@@ -84,11 +84,6 @@
     case UserVisibleFeatureKey::kPasswordChangeSubmission:
       break;
   }
-  DCHECK(!is_graduated ||
-         !base::FeatureList::IsEnabled(
-             *GetFeatureToUseToCheckSettingsVisibility(feature)))
-      << "Feature should not be both graduated and visible in settings: "
-      << GetFeatureToUseToCheckSettingsVisibility(feature)->name;
   return is_graduated;
 }
 
diff --git a/components/optimization_guide/core/model_execution/model_execution_features_controller.cc b/components/optimization_guide/core/model_execution/model_execution_features_controller.cc
index 50ddf0d3..0af64f0 100644
--- a/components/optimization_guide/core/model_execution/model_execution_features_controller.cc
+++ b/components/optimization_guide/core/model_execution/model_execution_features_controller.cc
@@ -355,16 +355,6 @@
       break;
   }
 
-  // Graduated feature should never be visible in settings.
-  // TODO(crbug.com/362225975): This code can be removed when the settings
-  // refresh is launched.
-  if (features::internal::IsGraduatedFeature(feature) &&
-      !features::IsAiSettingsPageRefreshEnabled()) {
-    metrics_recorder.SetResult(
-        feature, SettingsVisibilityResult::kNotVisibleGraduatedFeature);
-    return false;
-  }
-
   // Check feature-specific requirements.
   if (feature == UserVisibleFeatureKey::kHistorySearch) {
     SettingsVisibilityResult result = ShouldHideHistorySearch();
diff --git a/components/optimization_guide/core/model_execution/model_execution_features_controller.h b/components/optimization_guide/core/model_execution/model_execution_features_controller.h
index 5b30b69..b81fac24 100644
--- a/components/optimization_guide/core/model_execution/model_execution_features_controller.h
+++ b/components/optimization_guide/core/model_execution/model_execution_features_controller.h
@@ -47,7 +47,7 @@
     // account.
     kNotVisibleModelExecutionCapability = 6,
     // Not visible because the feature is already graduated.
-    kNotVisibleGraduatedFeature = 7,
+    // DEPRECATED: kNotVisibleGraduatedFeature = 7,
     // Not visible because the device is unsupported by the feature.
     kNotVisibleHardwareUnsupported = 8,
     // Updates should match with FeaturesSettingsVisibilityResult enum in
diff --git a/components/optimization_guide/core/model_execution/model_execution_features_controller_unittest.cc b/components/optimization_guide/core/model_execution/model_execution_features_controller_unittest.cc
index e5f6d8c..591443fb 100644
--- a/components/optimization_guide/core/model_execution/model_execution_features_controller_unittest.cc
+++ b/components/optimization_guide/core/model_execution/model_execution_features_controller_unittest.cc
@@ -123,8 +123,8 @@
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitWithFeatures(
       {features::internal::kComposeSettingsVisibility},
-      {features::internal::kComposeGraduated,
-       features::kAiSettingsPageRefresh});
+      {features::internal::kWallpaperSearchGraduated,
+       features::internal::kTabOrganizationGraduated});
   CreateController();
 
   EnableSignIn();
@@ -152,9 +152,7 @@
   scoped_feature_list.InitWithFeaturesAndParameters(
       {{features::internal::kComposeSettingsVisibility, {}},
        {features::internal::kTabOrganizationSettingsVisibility, {}}},
-      {features::internal::kComposeGraduated,
-       features::internal::kTabOrganizationGraduated,
-       features::kAiSettingsPageRefresh});
+      {features::internal::kWallpaperSearchGraduated});
   CreateController();
   EXPECT_FALSE(controller()->IsSettingVisible(UserVisibleFeatureKey::kCompose));
   EXPECT_FALSE(
@@ -176,9 +174,7 @@
         {{"allow_unsigned_user", "true"}}},
        {features::internal::kTabOrganizationSettingsVisibility,
         {{"allow_unsigned_user", "true"}}}},
-      {features::internal::kComposeGraduated,
-       features::internal::kTabOrganizationGraduated,
-       features::kAiSettingsPageRefresh});
+      {features::internal::kWallpaperSearchGraduated});
   CreateController();
   EXPECT_TRUE(controller()->IsSettingVisible(UserVisibleFeatureKey::kCompose));
   EXPECT_TRUE(
@@ -200,8 +196,7 @@
         {{"allow_unsigned_user", "true"}}},
        {features::internal::kTabOrganizationSettingsVisibility,
         {{"allow_unsigned_user", "true"}}}},
-      {features::internal::kComposeGraduated,
-       features::internal::kTabOrganizationGraduated});
+      {features::internal::kWallpaperSearchGraduated});
   CreateController();
   EXPECT_TRUE(controller()->IsSettingVisible(UserVisibleFeatureKey::kCompose));
   EXPECT_TRUE(
@@ -220,7 +215,8 @@
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitWithFeatures(
       {features::internal::kComposeSettingsVisibility},
-      {features::internal::kComposeGraduated});
+      {features::internal::kWallpaperSearchGraduated,
+       features::internal::kTabOrganizationGraduated});
   CreateController();
   EnableSignInWithoutCapability();
   EXPECT_FALSE(controller()->IsSettingVisible(UserVisibleFeatureKey::kCompose));
@@ -236,8 +232,7 @@
   scoped_feature_list.InitWithFeatures(
       {features::internal::kComposeSettingsVisibility,
        features::internal::kModelExecutionCapabilityDisable},
-      {features::internal::kComposeGraduated,
-       features::kAiSettingsPageRefresh});
+      {features::internal::kTabOrganizationGraduated});
   CreateController();
   EnableSignInWithoutCapability();
 
@@ -246,7 +241,7 @@
       controller()->IsSettingVisible(UserVisibleFeatureKey::kTabOrganization));
 }
 
-TEST_F(ModelExecutionFeaturesControllerTest, GraduatedFeatureIsNotVisible) {
+TEST_F(ModelExecutionFeaturesControllerTest, GraduatedFeatureIsVisible) {
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitWithFeatures(
       /*enabled_features=*/
@@ -256,16 +251,15 @@
       /*disabled_features=*/
       {features::internal::kComposeSettingsVisibility,
        features::internal::kTabOrganizationSettingsVisibility,
-       features::internal::kWallpaperSearchSettingsVisibility,
-       features::kAiSettingsPageRefresh});
+       features::internal::kWallpaperSearchSettingsVisibility});
   CreateController();
 
   EnableSignIn();
   // IsSettingVisible
-  EXPECT_FALSE(controller()->IsSettingVisible(UserVisibleFeatureKey::kCompose));
-  EXPECT_FALSE(
+  EXPECT_TRUE(controller()->IsSettingVisible(UserVisibleFeatureKey::kCompose));
+  EXPECT_TRUE(
       controller()->IsSettingVisible(UserVisibleFeatureKey::kTabOrganization));
-  EXPECT_FALSE(
+  EXPECT_TRUE(
       controller()->IsSettingVisible(UserVisibleFeatureKey::kWallpaperSearch));
   // ShouldFeatureBeCurrentlyEnabledForUser
   EXPECT_TRUE(controller()->ShouldFeatureBeCurrentlyEnabledForUser(
diff --git a/components/optimization_guide/core/optimization_guide_features.cc b/components/optimization_guide/core/optimization_guide_features.cc
index 6503031..85b663a 100644
--- a/components/optimization_guide/core/optimization_guide_features.cc
+++ b/components/optimization_guide/core/optimization_guide_features.cc
@@ -176,13 +176,12 @@
              "OnDeviceModelFetchPerformanceClassEveryStartup",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-// Enable the "Synapse" refreshed AI settings page.
-BASE_FEATURE(kAiSettingsPageRefresh,
-             "AiSettingsPageRefresh",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
-const base::FeatureParam<bool> kShowAiSettingsForTesting{
-    &kAiSettingsPageRefresh, "show_ai_settings_for_testing", false};
+// Force show the AI page and all AI feature sub-pages in settings, even if they
+// would be unavailable otherwise. This is meant for development and test
+// purposes only.
+BASE_FEATURE(kAiSettingsPageForceAvailable,
+             "AiSettingsPageForceAvailable",
+             base::FEATURE_DISABLED_BY_DEFAULT);
 
 // Enable AI settings page integration with Privacy Guide.
 BASE_FEATURE(kPrivacyGuideAiSettings,
@@ -901,12 +900,6 @@
   return base::FeatureList::IsEnabled(kOptimizationGuideIconView);
 }
 
-bool IsAiSettingsPageRefreshEnabled() {
-  return base::FeatureList::IsEnabled(kAiSettingsPageRefresh) ||
-         base::FeatureList::IsEnabled(kPrivacyGuideAiSettings) ||
-         base::FeatureList::IsEnabled(kAiSettingsPageEnterpriseDisabledUi);
-}
-
 bool IsPrivacyGuideAiSettingsEnabled() {
   return base::FeatureList::IsEnabled(kPrivacyGuideAiSettings);
 }
diff --git a/components/optimization_guide/core/optimization_guide_features.h b/components/optimization_guide/core/optimization_guide_features.h
index 3b1248f..6b56f3d 100644
--- a/components/optimization_guide/core/optimization_guide_features.h
+++ b/components/optimization_guide/core/optimization_guide_features.h
@@ -79,15 +79,12 @@
 COMPONENT_EXPORT(OPTIMIZATION_GUIDE_FEATURES)
 BASE_DECLARE_FEATURE(kOnDeviceModelFetchPerformanceClassEveryStartup);
 COMPONENT_EXPORT(OPTIMIZATION_GUIDE_FEATURES)
-BASE_DECLARE_FEATURE(kAiSettingsPageRefresh);
+BASE_DECLARE_FEATURE(kAiSettingsPageForceAvailable);
 COMPONENT_EXPORT(OPTIMIZATION_GUIDE_FEATURES)
 BASE_DECLARE_FEATURE(kPrivacyGuideAiSettings);
 COMPONENT_EXPORT(OPTIMIZATION_GUIDE_FEATURES)
 BASE_DECLARE_FEATURE(kAnnotatedPageContentWithActionableElements);
 
-COMPONENT_EXPORT(OPTIMIZATION_GUIDE_FEATURES)
-extern const base::FeatureParam<bool> kShowAiSettingsForTesting;
-
 // Allows setting feature params for model download configuration, such as
 // minimum performance class for download.
 COMPONENT_EXPORT(OPTIMIZATION_GUIDE_FEATURES)
@@ -553,10 +550,6 @@
 COMPONENT_EXPORT(OPTIMIZATION_GUIDE_FEATURES)
 bool ShouldEnableOptimizationGuideIconView();
 
-// Whether Ai settings page refresh or any dependent feature is enabled.
-COMPONENT_EXPORT(OPTIMIZATION_GUIDE_FEATURES)
-bool IsAiSettingsPageRefreshEnabled();
-
 // Whether Ai settings page integration with Privacy Guide is enabled.
 COMPONENT_EXPORT(OPTIMIZATION_GUIDE_FEATURES)
 bool IsPrivacyGuideAiSettingsEnabled();
diff --git a/components/optimization_guide/internal b/components/optimization_guide/internal
index f58041b..315d910 160000
--- a/components/optimization_guide/internal
+++ b/components/optimization_guide/internal
@@ -1 +1 @@
-Subproject commit f58041bc19d8cd401fe481331f0b399ccc3a73a4
+Subproject commit 315d910d17d14d2132ec008d5867390276e54386
diff --git a/components/page_load_metrics/browser/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc b/components/page_load_metrics/browser/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc
index 55dad9f2..29393be 100644
--- a/components/page_load_metrics/browser/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc
+++ b/components/page_load_metrics/browser/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc
@@ -172,14 +172,14 @@
 class ResourceLoadingCancellingThrottle
     : public content::TestNavigationThrottle {
  public:
-  static std::unique_ptr<content::NavigationThrottle> Create(
-      content::NavigationHandle* handle) {
-    return std::make_unique<ResourceLoadingCancellingThrottle>(handle);
+  static void Create(content::NavigationThrottleRegistry& registry) {
+    registry.AddThrottle(
+        std::make_unique<ResourceLoadingCancellingThrottle>(registry));
   }
 
   explicit ResourceLoadingCancellingThrottle(
-      content::NavigationHandle* navigation_handle)
-      : content::TestNavigationThrottle(navigation_handle) {
+      content::NavigationThrottleRegistry& registry)
+      : content::TestNavigationThrottle(registry) {
     SetResponse(TestNavigationThrottle::WILL_PROCESS_RESPONSE,
                 TestNavigationThrottle::ASYNCHRONOUS, CANCEL);
   }
diff --git a/components/payments/OWNERS b/components/payments/OWNERS
index c4c7959..6675dbf 100644
--- a/components/payments/OWNERS
+++ b/components/payments/OWNERS
@@ -1,4 +1,3 @@
 rouslan@chromium.org
-npnavarro@chromium.org
 smcgruer@chromium.org
 slobodan@chromium.org
diff --git a/components/payments/content/BUILD.gn b/components/payments/content/BUILD.gn
index 028c3b99..569cee8d 100644
--- a/components/payments/content/BUILD.gn
+++ b/components/payments/content/BUILD.gn
@@ -170,6 +170,7 @@
     "//base",
     "//components/autofill/core/browser",
     "//components/network_session_configurator/common",
+    "//components/payments/content/browser_binding:browser_bound_key_metadata",
     "//components/payments/content/icon",
     "//components/payments/content/utility",
     "//components/payments/core",
@@ -202,12 +203,17 @@
     ":utils",
     "//base/test:test_support",
     "//components/os_crypt/async/browser:test_support",
+    "//components/payments/content/browser_binding:browser_bound_key_metadata",
     "//components/webdata/common",
     "//components/webdata_services",
     "//testing/gmock",
     "//testing/gtest",
     "//third_party/blink/public:blink_headers",
   ]
+  if (is_android) {
+    public_deps =
+        [ "//components/payments/content/browser_binding:test_support" ]
+  }
 }
 
 group("unit_tests") {
diff --git a/components/payments/content/browser_binding/BUILD.gn b/components/payments/content/browser_binding/BUILD.gn
index ca36df5..019e8b5 100644
--- a/components/payments/content/browser_binding/BUILD.gn
+++ b/components/payments/content/browser_binding/BUILD.gn
@@ -28,6 +28,7 @@
   ]
   public_deps = [ ":interface" ]
   deps = [
+    ":browser_bound_key_metadata",
     "//base",
     "//components/payments/content:utils",
     "//components/webdata/common",
@@ -39,6 +40,7 @@
   visibility = [ "//components/payments/*" ]
   sources = [ "passkey_browser_binder_unittest.cc" ]
   deps = [
+    ":browser_bound_key_metadata",
     ":fake_browser_bound_key_store",
     ":interface",
     ":passkey_browser_binder",
@@ -55,21 +57,28 @@
 if (is_android) {
   source_set("browser_bound_keys_deleter_factory") {
     sources = [
+      "browser_bound_keys_deleter.cc",
+      "browser_bound_keys_deleter.h",
       "browser_bound_keys_deleter_factory.cc",
       "browser_bound_keys_deleter_factory.h",
     ]
     public_deps = [
       "//base",  # base/no_destructor.h
       "//components/keyed_service/content",
+      "//components/payments/content:utils",
     ]
     deps = [
+      ":android",
+      ":passkey_browser_binder",
       "//components/keyed_service/content",
+      "//components/webauthn/android",
       "//components/webdata_services",
+      "//content/public/browser",
     ]
   }
 
   source_set("android") {
-    visibility = [ "//components/payments/content/browser_binding" ]
+    visibility = [ ":*" ]
     sources = [
       "browser_bound_key_android.cc",
       "browser_bound_key_android.h",
@@ -84,6 +93,18 @@
       "//third_party/jni_zero",
     ]
   }
+
+  source_set("test_support") {
+    testonly = true
+    sources = [
+      "mock_browser_bound_keys_deleter.cc",
+      "mock_browser_bound_keys_deleter.h",
+    ]
+    public_deps = [
+      ":browser_bound_keys_deleter_factory",
+      "//testing/gmock",
+    ]
+  }
 }
 
 source_set("fake_browser_bound_key_store") {
@@ -100,3 +121,10 @@
     "//device/fido",
   ]
 }
+
+source_set("browser_bound_key_metadata") {
+  sources = [
+    "browser_bound_key_metadata.cc",
+    "browser_bound_key_metadata.h",
+  ]
+}
diff --git a/components/payments/content/browser_binding/browser_bound_key_metadata.cc b/components/payments/content/browser_binding/browser_bound_key_metadata.cc
new file mode 100644
index 0000000..4e19930e
--- /dev/null
+++ b/components/payments/content/browser_binding/browser_bound_key_metadata.cc
@@ -0,0 +1,47 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/payments/content/browser_binding/browser_bound_key_metadata.h"
+
+namespace payments {
+
+BrowserBoundKeyMetadata::RelyingPartyAndCredentialId::
+    RelyingPartyAndCredentialId() = default;
+
+BrowserBoundKeyMetadata::RelyingPartyAndCredentialId::
+    RelyingPartyAndCredentialId(const RelyingPartyAndCredentialId&) = default;
+BrowserBoundKeyMetadata::RelyingPartyAndCredentialId&
+BrowserBoundKeyMetadata::RelyingPartyAndCredentialId::operator=(
+    const RelyingPartyAndCredentialId&) = default;
+
+BrowserBoundKeyMetadata::RelyingPartyAndCredentialId::
+    RelyingPartyAndCredentialId(RelyingPartyAndCredentialId&&) = default;
+BrowserBoundKeyMetadata::RelyingPartyAndCredentialId&
+BrowserBoundKeyMetadata::RelyingPartyAndCredentialId::operator=(
+    RelyingPartyAndCredentialId&&) = default;
+
+BrowserBoundKeyMetadata::RelyingPartyAndCredentialId::
+    RelyingPartyAndCredentialId(std::string relying_party_id,
+                                std::vector<uint8_t> credential_id)
+    : relying_party_id(std::move(relying_party_id)),
+      credential_id(std::move(credential_id)) {}
+
+BrowserBoundKeyMetadata::RelyingPartyAndCredentialId::
+    ~RelyingPartyAndCredentialId() = default;
+
+BrowserBoundKeyMetadata::BrowserBoundKeyMetadata() = default;
+
+BrowserBoundKeyMetadata::BrowserBoundKeyMetadata(
+    const BrowserBoundKeyMetadata&) = default;
+BrowserBoundKeyMetadata& BrowserBoundKeyMetadata::operator=(
+    const BrowserBoundKeyMetadata&) = default;
+
+BrowserBoundKeyMetadata::BrowserBoundKeyMetadata(BrowserBoundKeyMetadata&&) =
+    default;
+BrowserBoundKeyMetadata& BrowserBoundKeyMetadata::operator=(
+    BrowserBoundKeyMetadata&&) = default;
+
+BrowserBoundKeyMetadata::~BrowserBoundKeyMetadata() = default;
+
+}  // namespace payments
diff --git a/components/payments/content/browser_binding/browser_bound_key_metadata.h b/components/payments/content/browser_binding/browser_bound_key_metadata.h
new file mode 100644
index 0000000..a1091c4
--- /dev/null
+++ b/components/payments/content/browser_binding/browser_bound_key_metadata.h
@@ -0,0 +1,57 @@
+// 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_PAYMENTS_CONTENT_BROWSER_BINDING_BROWSER_BOUND_KEY_METADATA_H_
+#define COMPONENTS_PAYMENTS_CONTENT_BROWSER_BINDING_BROWSER_BOUND_KEY_METADATA_H_
+
+#include <string>
+#include <vector>
+
+namespace payments {
+
+// Describes the metadata stored about browser bound keys.
+struct BrowserBoundKeyMetadata {
+  // Describes the primary key of the browser bound key metadata. The browser
+  // bound key is unique identified by the passkey's relying party id and its
+  // credential_id.
+  struct RelyingPartyAndCredentialId {
+    // The relying party associated to the passkey.
+    std::string relying_party_id;
+    // The id of the passkey.
+    std::vector<uint8_t> credential_id;
+
+    RelyingPartyAndCredentialId();
+
+    // Please use std::move() on `relying_party_id` and `credential_id` if
+    // possible to avoid copying the string and vector.
+    RelyingPartyAndCredentialId(std::string relying_party_id,
+                                std::vector<uint8_t> credential_id);
+
+    RelyingPartyAndCredentialId(const RelyingPartyAndCredentialId&);
+    RelyingPartyAndCredentialId& operator=(const RelyingPartyAndCredentialId&);
+
+    RelyingPartyAndCredentialId(RelyingPartyAndCredentialId&&);
+    RelyingPartyAndCredentialId& operator=(RelyingPartyAndCredentialId&&);
+
+    ~RelyingPartyAndCredentialId();
+  };
+
+  RelyingPartyAndCredentialId passkey;
+  // The platform-specific id of the browser bound key.
+  std::vector<uint8_t> browser_bound_key_id;
+
+  BrowserBoundKeyMetadata();
+
+  BrowserBoundKeyMetadata(const BrowserBoundKeyMetadata&);
+  BrowserBoundKeyMetadata& operator=(const BrowserBoundKeyMetadata&);
+
+  BrowserBoundKeyMetadata(BrowserBoundKeyMetadata&&);
+  BrowserBoundKeyMetadata& operator=(BrowserBoundKeyMetadata&&);
+
+  ~BrowserBoundKeyMetadata();
+};
+
+}  // namespace payments
+
+#endif  // COMPONENTS_PAYMENTS_CONTENT_BROWSER_BINDING_BROWSER_BOUND_KEY_METADATA_H_
diff --git a/components/payments/content/browser_binding/browser_bound_keys_deleter.cc b/components/payments/content/browser_binding/browser_bound_keys_deleter.cc
new file mode 100644
index 0000000..96815a94
--- /dev/null
+++ b/components/payments/content/browser_binding/browser_bound_keys_deleter.cc
@@ -0,0 +1,55 @@
+// 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/payments/content/browser_binding/browser_bound_keys_deleter.h"
+
+#include "components/payments/content/browser_binding/passkey_browser_binder.h"
+#include "components/payments/content/payment_manifest_web_data_service.h"
+#include "components/webauthn/android/internal_authenticator_android.h"
+#include "third_party/blink/public/common/features.h"
+
+namespace payments {
+
+BrowserBoundKeyDeleter::BrowserBoundKeyDeleter(
+    scoped_refptr<PaymentManifestWebDataService> web_data_service)
+    : web_data_service_(web_data_service) {}
+
+BrowserBoundKeyDeleter::~BrowserBoundKeyDeleter() = default;
+
+void BrowserBoundKeyDeleter::RemoveInvalidBBKs() {
+  if (!base::FeatureList::IsEnabled(
+          blink::features::kSecurePaymentConfirmationBrowserBoundKeys)) {
+    return;
+  }
+  auto authenticator = std::make_unique<webauthn::InternalAuthenticatorAndroid>(
+      /*render_frame_host=*/nullptr);
+  if (!authenticator->IsGetMatchingCredentialIdsSupported()) {
+    // SPC (on Android) requires GetMatchingCredentialIds, so BBKs are not
+    // relevant when this API is not supported.
+    return;
+  }
+  scoped_refptr<BrowserBoundKeyStore> bbk_key_store =
+      GetBrowserBoundKeyStoreInstance();
+  CHECK(bbk_key_store);
+  auto passkey_browser_binder =
+      std::make_unique<PasskeyBrowserBinder>(bbk_key_store, web_data_service_);
+  passkey_browser_binder->DeleteAllUnknownBrowserBoundKeys(
+      base::BindRepeating(
+          &webauthn::InternalAuthenticatorAndroid::GetMatchingCredentialIds,
+          // The authenticator will be destroyed once this callback is no longer
+          // referenced.
+          base::Owned(std::move(authenticator))),
+      base::BindOnce(
+          [](std::unique_ptr<PasskeyBrowserBinder> passkey_browser_binder) {
+            // This callback runs after BBK metadata has been deleted.
+            // Destroy the unique_ptr objects: Reset here explicitly for
+            // emphasis. Note the unique_ptr object would be reset regardless
+            // by going out of scope.
+            passkey_browser_binder.reset();
+          },
+          std::move(passkey_browser_binder)));
+  // Don't access authenticator nor passkey_browser_binder after this point.
+}
+
+}  // namespace payments
diff --git a/components/payments/content/browser_binding/browser_bound_keys_deleter.h b/components/payments/content/browser_binding/browser_bound_keys_deleter.h
new file mode 100644
index 0000000..9301428
--- /dev/null
+++ b/components/payments/content/browser_binding/browser_bound_keys_deleter.h
@@ -0,0 +1,39 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_PAYMENTS_CONTENT_BROWSER_BINDING_BROWSER_BOUND_KEYS_DELETER_H_
+#define COMPONENTS_PAYMENTS_CONTENT_BROWSER_BINDING_BROWSER_BOUND_KEYS_DELETER_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "components/keyed_service/core/keyed_service.h"
+#include "components/payments/content/payment_manifest_web_data_service.h"
+
+namespace payments {
+
+class BrowserBoundKeyDeleter : public KeyedService {
+ public:
+  explicit BrowserBoundKeyDeleter(
+      scoped_refptr<PaymentManifestWebDataService> web_data_service);
+
+  // Non-copyable
+  BrowserBoundKeyDeleter(const BrowserBoundKeyDeleter&) = delete;
+  BrowserBoundKeyDeleter operator=(const BrowserBoundKeyDeleter&) = delete;
+
+  // Non-moveable
+  BrowserBoundKeyDeleter(const BrowserBoundKeyDeleter&&) = delete;
+  BrowserBoundKeyDeleter operator=(const BrowserBoundKeyDeleter&&) = delete;
+
+  ~BrowserBoundKeyDeleter() override;
+
+  // Starts the asynchronous process to find browser bound keys and delete them.
+  // Declared virtual to allow overriding by testing mocks.
+  virtual void RemoveInvalidBBKs();
+
+ private:
+  scoped_refptr<PaymentManifestWebDataService> web_data_service_;
+};
+
+}  // namespace payments
+
+#endif  // COMPONENTS_PAYMENTS_CONTENT_BROWSER_BINDING_BROWSER_BOUND_KEYS_DELETER_H_
diff --git a/components/payments/content/browser_binding/browser_bound_keys_deleter_factory.cc b/components/payments/content/browser_binding/browser_bound_keys_deleter_factory.cc
index 4100f13..53695a2 100644
--- a/components/payments/content/browser_binding/browser_bound_keys_deleter_factory.cc
+++ b/components/payments/content/browser_binding/browser_bound_keys_deleter_factory.cc
@@ -5,13 +5,17 @@
 #include "components/payments/content/browser_binding/browser_bound_keys_deleter_factory.h"
 
 #include "base/logging.h"
+#include "base/memory/scoped_refptr.h"
 #include "base/no_destructor.h"
 #include "build/build_config.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/keyed_service/content/browser_context_keyed_service_factory.h"
 #include "components/keyed_service/core/keyed_service.h"
+#include "components/payments/content/browser_binding/browser_bound_key_store_android.h"
+#include "components/payments/content/browser_binding/browser_bound_keys_deleter.h"
 #include "components/webdata_services/web_data_service_wrapper.h"
 #include "components/webdata_services/web_data_service_wrapper_factory.h"
+#include "content/public/browser/browser_context.h"
 
 namespace payments {
 
@@ -21,14 +25,32 @@
   return instance.get();
 }
 
+// static
+BrowserBoundKeyDeleter* BrowserBoundKeyDeleterFactory::GetForBrowserContext(
+    content::BrowserContext* context) {
+  return static_cast<BrowserBoundKeyDeleter*>(
+      GetInstance()->GetServiceForBrowserContext(context, /*create=*/false));
+}
+
+content::BrowserContext* BrowserBoundKeyDeleterFactory::GetBrowserContextToUse(
+    content::BrowserContext* context) const {
+  if (context->IsOffTheRecord()) {
+    // There is no need to remove invalid BBKs for a derived OTR profile, since
+    // it would have been done for the original profile.
+    return nullptr;
+  }
+  return context;
+}
+
 std::unique_ptr<KeyedService>
 BrowserBoundKeyDeleterFactory::BuildServiceInstanceForBrowserContext(
     content::BrowserContext* context) const {
-  // TODO(https://crbug.com/406299749): Delete invalid browser bound keys.
-
-  // Return an empty service object to avoid holding dependencies forever when
-  // this service only runs a clean-up method on initialization.
-  return std::make_unique<KeyedService>();
+  CHECK(!context->IsOffTheRecord());
+  auto service = std::make_unique<BrowserBoundKeyDeleter>(
+      webdata_services::WebDataServiceWrapperFactory::
+          GetPaymentManifestWebDataServiceForBrowserContext(
+              context, ServiceAccessType::EXPLICIT_ACCESS));
+  return service;
 }
 
 bool BrowserBoundKeyDeleterFactory::ServiceIsCreatedWithBrowserContext() const {
diff --git a/components/payments/content/browser_binding/browser_bound_keys_deleter_factory.h b/components/payments/content/browser_binding/browser_bound_keys_deleter_factory.h
index 49bd9c6..1580b774 100644
--- a/components/payments/content/browser_binding/browser_bound_keys_deleter_factory.h
+++ b/components/payments/content/browser_binding/browser_bound_keys_deleter_factory.h
@@ -18,16 +18,24 @@
 
 namespace payments {
 
+class BrowserBoundKeyDeleter;
+
 // Responsible for creating a service to start the process of finding and
 // deleting invalid browser bound key metadata metadata.
 class BrowserBoundKeyDeleterFactory : public BrowserContextKeyedServiceFactory {
  public:
   static BrowserBoundKeyDeleterFactory* GetInstance();
 
+  static BrowserBoundKeyDeleter* GetForBrowserContext(
+      content::BrowserContext* context);
+
  protected:
-  // BrowserContextKeyedServiceFactory:
+  content::BrowserContext* GetBrowserContextToUse(
+      content::BrowserContext* context) const override;
+
   std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
       content::BrowserContext* context) const override;
+
   bool ServiceIsCreatedWithBrowserContext() const override;
 
  private:
diff --git a/components/payments/content/browser_binding/mock_browser_bound_keys_deleter.cc b/components/payments/content/browser_binding/mock_browser_bound_keys_deleter.cc
new file mode 100644
index 0000000..7bcc2d8
--- /dev/null
+++ b/components/payments/content/browser_binding/mock_browser_bound_keys_deleter.cc
@@ -0,0 +1,14 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/payments/content/browser_binding/mock_browser_bound_keys_deleter.h"
+
+namespace payments {
+
+MockBrowserBoundKeyDeleter::MockBrowserBoundKeyDeleter()
+    : BrowserBoundKeyDeleter(/*web_data_service=*/nullptr) {}
+
+MockBrowserBoundKeyDeleter::~MockBrowserBoundKeyDeleter() = default;
+
+}  // namespace payments
diff --git a/components/payments/content/browser_binding/mock_browser_bound_keys_deleter.h b/components/payments/content/browser_binding/mock_browser_bound_keys_deleter.h
new file mode 100644
index 0000000..56863626
--- /dev/null
+++ b/components/payments/content/browser_binding/mock_browser_bound_keys_deleter.h
@@ -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.
+
+#ifndef COMPONENTS_PAYMENTS_CONTENT_BROWSER_BINDING_MOCK_BROWSER_BOUND_KEYS_DELETER_H_
+#define COMPONENTS_PAYMENTS_CONTENT_BROWSER_BINDING_MOCK_BROWSER_BOUND_KEYS_DELETER_H_
+
+#include "components/payments/content/browser_binding/browser_bound_keys_deleter.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace payments {
+
+class MockBrowserBoundKeyDeleter : public BrowserBoundKeyDeleter {
+ public:
+  MockBrowserBoundKeyDeleter();
+
+  ~MockBrowserBoundKeyDeleter() override;
+
+  MOCK_METHOD(void, RemoveInvalidBBKs, (), (override));
+};
+
+}  // namespace payments
+
+#endif  // COMPONENTS_PAYMENTS_CONTENT_BROWSER_BINDING_MOCK_BROWSER_BOUND_KEYS_DELETER_H_
diff --git a/components/payments/content/browser_binding/passkey_browser_binder.cc b/components/payments/content/browser_binding/passkey_browser_binder.cc
index 316fba7..4a8c84b6 100644
--- a/components/payments/content/browser_binding/passkey_browser_binder.cc
+++ b/components/payments/content/browser_binding/passkey_browser_binder.cc
@@ -10,9 +10,14 @@
 #include <utility>
 #include <vector>
 
+#include "base/barrier_callback.h"
+#include "base/base64.h"
 #include "base/check.h"
+#include "base/containers/contains.h"
+#include "base/containers/to_vector.h"
 #include "base/functional/callback.h"
 #include "components/payments/content/browser_binding/browser_bound_key.h"
+#include "components/payments/content/browser_binding/browser_bound_key_metadata.h"
 #include "components/payments/content/browser_binding/browser_bound_key_store.h"
 #include "components/payments/content/payment_manifest_web_data_service.h"
 #include "components/webdata/common/web_data_results.h"
@@ -23,6 +28,16 @@
 // The length of the random browser bound key identifiers.
 constexpr size_t kBrowserBoundKeyIdLength = 32;
 
+using GetMatchingCredentialIdsCallback = base::RepeatingCallback<void(
+    const std::string& relying_party_id,
+    const std::vector<std::vector<uint8_t>>& credential_ids,
+    bool require_third_party_payment_bit_set,
+    base::OnceCallback<void(std::vector<std::vector<uint8_t>>)>)>;
+
+// Type for a map from string (relying party) to a vector of BBK metadata.
+using RelyingPartyToBkkMetadata =
+    base::flat_map<std::string, std::vector<BrowserBoundKeyMetadata>>;
+
 // Returns the callback matching `handle` and erases it from the `handlers` map.
 template <typename CallbackType>
 static CallbackType RemoveHandler(
@@ -35,6 +50,76 @@
   return callback;
 }
 
+// Converts a generic `result` of unique_ptr<WDTypedResult> to
+// the vector of BrowserBoundKeyMetadata.
+static std::vector<BrowserBoundKeyMetadata>
+ConvertBrowserBoundKeyMetadataResult(WebDataServiceBase::Handle handle,
+                                     std::unique_ptr<WDTypedResult> result) {
+  if (result) {
+    CHECK(result->GetType() == BROWSER_BOUND_KEY_METADATA);
+    return static_cast<WDResult<std::vector<BrowserBoundKeyMetadata>>*>(
+               result.get())
+        ->GetValue();
+  } else {
+    return {};
+  }
+}
+
+static RelyingPartyToBkkMetadata GroupByRelyingPartyId(
+    std::vector<BrowserBoundKeyMetadata> bbk_metas) {
+  RelyingPartyToBkkMetadata grouped;
+  for (auto& bbk_meta : bbk_metas) {
+    grouped[bbk_meta.passkey.relying_party_id].push_back(std::move(bbk_meta));
+  }
+  return grouped;
+}
+
+static std::vector<BrowserBoundKeyMetadata> RemoveMatchingCredentialIds(
+    std::vector<BrowserBoundKeyMetadata> bbks,
+    std::vector<std::vector<uint8_t>> matching_credential_ids) {
+  std::erase_if(bbks, [&matching_credential_ids](auto& bbk) {
+    return base::Contains(matching_credential_ids, bbk.passkey.credential_id);
+  });
+  return bbks;
+}
+
+// Flattens a vector of vector of metadata to a vector of metadata.
+static std::vector<BrowserBoundKeyMetadata> Flatten(
+    std::vector<std::vector<BrowserBoundKeyMetadata>> nested) {
+  std::vector<BrowserBoundKeyMetadata> flattened;
+  for (auto& inner : nested) {
+    std::ranges::move(inner, std::back_inserter(flattened));
+  }
+  return flattened;
+}
+
+// Finds the BBKs in `stored_bbks` that are no longer present in
+// `get_matching_credential_ids_callback`. `callback` will be invoked with a
+// vector of the browser bound key metadatas that are no longer valid.
+static void FindDeletedPasskeys(
+    GetMatchingCredentialIdsCallback get_matching_credential_ids_callback,
+    base::OnceCallback<void(std::vector<BrowserBoundKeyMetadata>)> callback,
+    std::vector<BrowserBoundKeyMetadata> stored_bbks) {
+  RelyingPartyToBkkMetadata bbks_by_relying_party =
+      GroupByRelyingPartyId(std::move(stored_bbks));
+  auto barrier_callback =
+      base::BarrierCallback<std::vector<BrowserBoundKeyMetadata>>(
+          bbks_by_relying_party.size(),
+          base::BindOnce(&Flatten).Then(std::move(callback)));
+  for (std::pair<std::string, std::vector<BrowserBoundKeyMetadata>>& entry :
+       bbks_by_relying_party) {
+    auto stored_credential_ids =
+        base::ToVector(entry.second, [](const BrowserBoundKeyMetadata& bbk) {
+          return bbk.passkey.credential_id;
+        });
+    get_matching_credential_ids_callback.Run(
+        entry.first, stored_credential_ids,
+        /*require_third_party_payment_bit=*/false,
+        base::BindOnce(&RemoveMatchingCredentialIds, std::move(entry.second))
+            .Then(barrier_callback));
+  }
+}
+
 }  // namespace
 
 PasskeyBrowserBinder::PasskeyBrowserBinder(
@@ -102,6 +187,34 @@
   }
 }
 
+void PasskeyBrowserBinder::DeleteAllUnknownBrowserBoundKeys(
+    GetMatchingCredentialIdsCallback get_matching_credential_ids_callback,
+    base::OnceClosure callback) {
+  // `callback` may be holding the reference to this PasskeyBrowserBinder, so
+  // after `callback` is run `this` may not be accessed.
+  base::OnceCallback<void(std::vector<BrowserBoundKeyMetadata>)>
+      delete_browser_bound_keys_and_finish =
+          base::BindOnce(&PasskeyBrowserBinder::DeleteBrowserBoundKeys,
+                         weak_ptr_factory_.GetWeakPtr(), std::move(callback));
+  web_data_service_->GetAllBrowserBoundKeys(
+      base::BindOnce(&ConvertBrowserBoundKeyMetadataResult)
+          .Then(base::BindOnce(
+              &FindDeletedPasskeys, get_matching_credential_ids_callback,
+              std::move(delete_browser_bound_keys_and_finish))));
+}
+
+void PasskeyBrowserBinder::DeleteBrowserBoundKeys(
+    base::OnceClosure callback,
+    std::vector<BrowserBoundKeyMetadata> stale_bbk_metas) {
+  if (stale_bbk_metas.empty()) {
+    return;
+  }
+  web_data_service_->DeleteBrowserBoundKeys(
+      base::ToVector(stale_bbk_metas,
+                     [](auto& meta) { return std::move(meta.passkey); }),
+      std::move(callback));
+}
+
 void PasskeyBrowserBinder::GetOrCreateBoundKeyForPasskey(
     std::vector<uint8_t> credential_id,
     std::string relying_party,
diff --git a/components/payments/content/browser_binding/passkey_browser_binder.h b/components/payments/content/browser_binding/passkey_browser_binder.h
index 98e36458..7872556d 100644
--- a/components/payments/content/browser_binding/passkey_browser_binder.h
+++ b/components/payments/content/browser_binding/passkey_browser_binder.h
@@ -22,6 +22,7 @@
 namespace payments {
 
 class BrowserBoundKey;
+struct BrowserBoundKeyMetadata;
 
 // Facilitates binding browser bound keys to passkeys.
 //
@@ -129,6 +130,22 @@
                const std::vector<uint8_t>& credential_id,
                const std::string& relying_party);
 
+  // Deletes all unknown browser bound keys, querying using the provided
+  // `get_matching_credential_ids_callback` to find credentials matching each
+  // relying party in the BBK storage. The
+  // `get_matching_credential_ids_callback` must be valid until `callback` is
+  // invoked. `callback` may hold and release the reference to this
+  // PasskeyBrowserBinder object (DeleteAllUnknownBrowserBoundKeys will run
+  // `callback` as its last action).
+  void DeleteAllUnknownBrowserBoundKeys(
+      base::RepeatingCallback<
+          void(const std::string& relying_party_id,
+               const std::vector<std::vector<uint8_t>>& credential_ids,
+               bool require_third_party_payment_bit_set,
+               base::OnceCallback<void(std::vector<std::vector<uint8_t>>)>)>
+          get_matching_credential_ids_callback,
+      base::OnceClosure callback);
+
   // WebDataServiceConsumer:
   void OnWebDataServiceRequestDone(
       WebDataServiceBase::Handle h,
@@ -153,6 +170,12 @@
       base::OnceCallback<void(std::unique_ptr<BrowserBoundKey>)> callback,
       std::vector<uint8_t> existing_browser_bound_key_id);
 
+  // Called after internal authenticator was called to find stale BBKs.
+  // `callback` is Run once the database operation completes.
+  void DeleteBrowserBoundKeys(
+      base::OnceClosure callback,
+      std::vector<BrowserBoundKeyMetadata> stale_bbk_metas);
+
   scoped_refptr<BrowserBoundKeyStore> key_store_;
   scoped_refptr<PaymentManifestWebDataService> web_data_service_;
   std::map<WebDataServiceBase::Handle, base::OnceCallback<void(bool)>>
diff --git a/components/payments/content/browser_binding/passkey_browser_binder_unittest.cc b/components/payments/content/browser_binding/passkey_browser_binder_unittest.cc
index ff42837..804246cb 100644
--- a/components/payments/content/browser_binding/passkey_browser_binder_unittest.cc
+++ b/components/payments/content/browser_binding/passkey_browser_binder_unittest.cc
@@ -7,12 +7,15 @@
 #include <cstdint>
 #include <utility>
 
+#include "base/containers/to_vector.h"
 #include "base/functional/callback_helpers.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/test/bind.h"
+#include "base/test/gmock_callback_support.h"
 #include "base/test/gmock_move_support.h"
 #include "base/test/mock_callback.h"
+#include "components/payments/content/browser_binding/browser_bound_key_metadata.h"
 #include "components/payments/content/browser_binding/fake_browser_bound_key_store.h"
 #include "components/payments/content/mock_payment_manifest_web_data_service.h"
 #include "content/public/test/browser_task_environment.h"
@@ -23,6 +26,37 @@
 namespace payments {
 namespace {
 
+testing::Matcher<BrowserBoundKeyMetadata::RelyingPartyAndCredentialId>
+EqRelyingPartyAndCredentialId(std::string relying_party_id,
+                              std::vector<uint8_t> credential_id) {
+  return testing::AllOf(
+      testing::Field("relying_party_id",
+                     &BrowserBoundKeyMetadata::RelyingPartyAndCredentialId::
+                         relying_party_id,
+                     relying_party_id),
+      testing::Field(
+          "credential_id",
+          &BrowserBoundKeyMetadata::RelyingPartyAndCredentialId::credential_id,
+          credential_id));
+}
+
+BrowserBoundKeyMetadata MakeBrowserBoundKeyMetadata(
+    std::vector<uint8_t> credential_id,
+    std::string relying_party_id,
+    std::vector<uint8_t> bbk_id) {
+  BrowserBoundKeyMetadata meta;
+  meta.passkey = BrowserBoundKeyMetadata::RelyingPartyAndCredentialId(
+      std::move(relying_party_id), std::move(credential_id));
+  meta.browser_bound_key_id = std::move(bbk_id);
+  return meta;
+}
+
+using GetMatchingCredentialIdsCallback = base::RepeatingCallback<void(
+    const std::string& relying_party_id,
+    const std::vector<std::vector<uint8_t>>& credential_ids,
+    bool require_third_party_payment_bit_set,
+    base::OnceCallback<void(std::vector<std::vector<uint8_t>>)>)>;
+
 using ::testing::_;
 using ::testing::AllOf;
 using ::testing::DoAll;
@@ -206,5 +240,165 @@
           WDResultType::BROWSER_BOUND_KEY, std::nullopt));
 }
 
+class PasskeyBrowserBinderDeletionTest : public PasskeyBrowserBinderTest {
+ public:
+  void RespondToGetAllBrowserBoundKeys(
+      std::vector<BrowserBoundKeyMetadata> bbk_metadatas) {
+    std::move(captured_get_all_browser_bound_keys_callback_)
+        .Run(/*handle=*/1234,
+             std::make_unique<WDResult<std::vector<BrowserBoundKeyMetadata>>>(
+                 WDResultType::BROWSER_BOUND_KEY_METADATA,
+                 std::move(bbk_metadatas)));
+  }
+
+  // Implements a fake version of
+  // InternalAuthenticator::GetMatchingCredentialIds() which is passed as a
+  // callback to PasskeyBrowserBinder::DeleteAllUnknownBrowserBoundKeys.
+  void GetMatchingCredentialIds(
+      const std::string& relying_party_id,
+      const std::vector<std::vector<uint8_t>>& credential_ids,
+      bool require_third_party_payment_bit_set,
+      base::OnceCallback<void(std::vector<std::vector<uint8_t>>)> callback) {
+    if (require_third_party_payment_bit_set) {
+      // PasskeyBrowserBinder::DeleteAllUnknownBrowserBoundKeys must call
+      // with `require_third_party_payment_bit_set` set to false, since BBKs
+      // can be created for passkeys in the first party context through the
+      // PaymentRequest API.
+      FAIL();
+    }
+    std::vector<std::vector<uint8_t>> stored_credential_ids =
+        fake_matching_credential_ids_[relying_party_id];
+    std::erase_if(stored_credential_ids,
+                  [&credential_ids](const std::vector<uint8_t>& credential) {
+                    return !base::Contains(credential_ids, credential);
+                  });
+    std::move(callback).Run(std::move(stored_credential_ids));
+  }
+
+ protected:
+  void SetUp() override {
+    PasskeyBrowserBinderTest::SetUp();
+    EXPECT_CALL(*mock_web_data_service_, GetAllBrowserBoundKeys)
+        .WillOnce(MoveArgAndReturn<0>(
+            &captured_get_all_browser_bound_keys_callback_, 1234));
+  }
+
+  WebDataServiceRequestCallback captured_get_all_browser_bound_keys_callback_;
+  base::flat_map</*relying_party*/ std::string,
+                 /*credential_ids*/ std::vector<std::vector<uint8_t>>>
+      fake_matching_credential_ids_;
+};
+
+TEST_F(PasskeyBrowserBinderDeletionTest,
+       DeleteAllUnknownBrowserBoundKeysWhenNoBBKStored) {
+  std::unique_ptr<PasskeyBrowserBinder> binder = CreatePasskeyBrowserBinder();
+  base::MockOnceClosure mock_callback;
+
+  binder->DeleteAllUnknownBrowserBoundKeys(
+      base::BindRepeating(
+          &PasskeyBrowserBinderDeletionTest::GetMatchingCredentialIds,
+          base::Unretained(this)),
+      mock_callback.Get());
+  RespondToGetAllBrowserBoundKeys(/*bbk_metadatas=*/{});
+}
+
+TEST_F(PasskeyBrowserBinderDeletionTest,
+       DeleteAllUnknownBrowserBoundKeysWithInvalidBbkMetadata) {
+  std::unique_ptr<PasskeyBrowserBinder> binder = CreatePasskeyBrowserBinder();
+  base::MockOnceClosure mock_callback;
+  std::vector<BrowserBoundKeyMetadata> bbk_metadatas;
+  bbk_metadatas.push_back(MakeBrowserBoundKeyMetadata(
+      fake_credential_id_, fake_relying_party_, fake_bbk_id_));
+  // This test does not insert any entries into `fake_matching_credential_ids_`
+
+  testing::Sequence sequence;
+  EXPECT_CALL(*mock_web_data_service_,
+              DeleteBrowserBoundKeys(
+                  testing::UnorderedElementsAre(EqRelyingPartyAndCredentialId(
+                      fake_relying_party_, fake_credential_id_)),
+                  _))
+      .InSequence(sequence)
+      .WillOnce(base::test::RunOnceCallbackRepeatedly<1>());
+  EXPECT_CALL(mock_callback, Run()).InSequence(sequence);
+
+  binder->DeleteAllUnknownBrowserBoundKeys(
+      base::BindRepeating(
+          &PasskeyBrowserBinderDeletionTest::GetMatchingCredentialIds,
+          base::Unretained(this)),
+      mock_callback.Get());
+  RespondToGetAllBrowserBoundKeys(std::move(bbk_metadatas));
+}
+
+TEST_F(PasskeyBrowserBinderDeletionTest,
+       DeleteAllUnknownBrowserBoundKeysWithValidBbkMetadata) {
+  std::unique_ptr<PasskeyBrowserBinder> binder = CreatePasskeyBrowserBinder();
+  base::MockOnceClosure mock_callback;
+  std::vector<BrowserBoundKeyMetadata> bbk_metadatas;
+  bbk_metadatas.push_back(MakeBrowserBoundKeyMetadata(
+      fake_credential_id_, fake_relying_party_, fake_bbk_id_));
+  fake_matching_credential_ids_[fake_relying_party_].push_back(
+      fake_credential_id_);
+
+  EXPECT_CALL(*mock_web_data_service_, DeleteBrowserBoundKeys(_, _)).Times(0);
+
+  binder->DeleteAllUnknownBrowserBoundKeys(
+      base::BindRepeating(
+          &PasskeyBrowserBinderDeletionTest::GetMatchingCredentialIds,
+          base::Unretained(this)),
+      mock_callback.Get());
+  RespondToGetAllBrowserBoundKeys(std::move(bbk_metadatas));
+}
+
+TEST_F(
+    PasskeyBrowserBinderDeletionTest,
+    DeleteAllUnknownBrowserBoundKeysWithInvalidBbkMetadataWhenRelyingPartyIsDifferent) {
+  std::unique_ptr<PasskeyBrowserBinder> binder = CreatePasskeyBrowserBinder();
+  base::MockOnceClosure mock_callback;
+  std::vector<BrowserBoundKeyMetadata> bbk_metadatas;
+  bbk_metadatas.push_back(MakeBrowserBoundKeyMetadata(
+      fake_credential_id_, fake_relying_party_, fake_bbk_id_));
+  fake_matching_credential_ids_["another." + fake_relying_party_].push_back(
+      fake_credential_id_);
+
+  EXPECT_CALL(*mock_web_data_service_,
+              DeleteBrowserBoundKeys(
+                  testing::UnorderedElementsAre(EqRelyingPartyAndCredentialId(
+                      fake_relying_party_, fake_credential_id_)),
+                  _))
+      .WillOnce(base::test::RunOnceCallbackRepeatedly<1>());
+
+  binder->DeleteAllUnknownBrowserBoundKeys(
+      base::BindRepeating(
+          &PasskeyBrowserBinderDeletionTest::GetMatchingCredentialIds,
+          base::Unretained(this)),
+      mock_callback.Get());
+  RespondToGetAllBrowserBoundKeys(std::move(bbk_metadatas));
+}
+
+TEST_F(PasskeyBrowserBinderDeletionTest,
+       DeleteAllUnknownBrowserBoundKeysWithMultipleRelyingParties) {
+  std::unique_ptr<PasskeyBrowserBinder> binder = CreatePasskeyBrowserBinder();
+  base::MockOnceClosure mock_callback;
+  std::vector<BrowserBoundKeyMetadata> bbk_metadatas;
+  bbk_metadatas.push_back(MakeBrowserBoundKeyMetadata(
+      fake_credential_id_, fake_relying_party_, fake_bbk_id_));
+  fake_matching_credential_ids_["another." + fake_relying_party_].push_back(
+      fake_credential_id_);
+
+  EXPECT_CALL(*mock_web_data_service_,
+              DeleteBrowserBoundKeys(
+                  testing::UnorderedElementsAre(EqRelyingPartyAndCredentialId(
+                      fake_relying_party_, fake_credential_id_)),
+                  _))
+      .WillOnce(base::test::RunOnceCallbackRepeatedly<1>());
+
+  binder->DeleteAllUnknownBrowserBoundKeys(
+      base::BindRepeating(
+          &PasskeyBrowserBinderDeletionTest::GetMatchingCredentialIds,
+          base::Unretained(this)),
+      mock_callback.Get());
+  RespondToGetAllBrowserBoundKeys(std::move(bbk_metadatas));
+}
+
 }  // namespace
 }  // namespace payments
diff --git a/components/payments/content/mock_payment_manifest_web_data_service.h b/components/payments/content/mock_payment_manifest_web_data_service.h
index 4fe96e1..608d7c9 100644
--- a/components/payments/content/mock_payment_manifest_web_data_service.h
+++ b/components/payments/content/mock_payment_manifest_web_data_service.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_PAYMENTS_CONTENT_MOCK_PAYMENT_MANIFEST_WEB_DATA_SERVICE_H_
 
 #include "base/time/time.h"
+#include "components/payments/content/browser_binding/browser_bound_key_metadata.h"
 #include "components/payments/content/payment_manifest_web_data_service.h"
 #include "components/webdata/common/web_database_service.h"
 #include "components/webdata_services/web_data_service_wrapper.h"
@@ -34,6 +35,16 @@
                std::string relying_party_id,
                WebDataServiceConsumer* consumer),
               (override));
+  MOCK_METHOD(WebDataServiceBase::Handle,
+              GetAllBrowserBoundKeys,
+              (WebDataServiceRequestCallback callback),
+              (override));
+  MOCK_METHOD(void,
+              DeleteBrowserBoundKeys,
+              (std::vector<BrowserBoundKeyMetadata::RelyingPartyAndCredentialId>
+                   passkeys,
+               base::OnceClosure callback),
+              (override));
 
  protected:
   ~MockPaymentManifestWebDataService() override;
diff --git a/components/payments/content/payment_manifest_web_data_service.cc b/components/payments/content/payment_manifest_web_data_service.cc
index e998ddd..c23b64d 100644
--- a/components/payments/content/payment_manifest_web_data_service.cc
+++ b/components/payments/content/payment_manifest_web_data_service.cc
@@ -8,6 +8,7 @@
 
 #include "base/functional/bind.h"
 #include "base/location.h"
+#include "base/task/bind_post_task.h"
 #include "base/task/sequenced_task_runner.h"
 #include "components/payments/content/payment_method_manifest_table.h"
 #include "components/payments/content/web_app_manifest_section_table.h"
@@ -211,6 +212,48 @@
           std::move(credential_id), std::move(relying_party_id)));
 }
 
+WebDataServiceBase::Handle
+PaymentManifestWebDataService::GetAllBrowserBoundKeys(
+    WebDataServiceRequestCallback callback) {
+  return wdbs_->ScheduleDBTaskWithResult(
+      FROM_HERE,
+      base::BindOnce(&PaymentManifestWebDataService::GetAllBrowserBoundKeysImpl,
+                     this),
+      std::move(callback));
+}
+
+std::unique_ptr<WDTypedResult>
+PaymentManifestWebDataService::GetAllBrowserBoundKeysImpl(WebDatabase* db) {
+  return std::make_unique<WDResult<std::vector<BrowserBoundKeyMetadata>>>(
+      BROWSER_BOUND_KEY_METADATA,
+      PaymentMethodManifestTable::FromWebDatabase(db)
+          ->GetAllBrowserBoundKeys());
+}
+
+void PaymentManifestWebDataService::DeleteBrowserBoundKeys(
+    std::vector<BrowserBoundKeyMetadata::RelyingPartyAndCredentialId> passkeys,
+    base::OnceClosure callback) {
+  wdbs_->ScheduleDBTask(
+      FROM_HERE,
+      base::BindOnce(&PaymentManifestWebDataService::DeleteBrowserBoundKeysImpl,
+                     this, std::move(passkeys),
+                     base::BindPostTaskToCurrentDefault(std::move(callback))));
+}
+
+WebDatabase::State PaymentManifestWebDataService::DeleteBrowserBoundKeysImpl(
+    std::vector<BrowserBoundKeyMetadata::RelyingPartyAndCredentialId> passkeys,
+    base::OnceClosure callback,
+    WebDatabase* db) {
+  if (PaymentMethodManifestTable::FromWebDatabase(db)->DeleteBrowserBoundKeys(
+          std::move(passkeys))) {
+    std::move(callback).Run();
+    return WebDatabase::State::COMMIT_NEEDED;
+  }
+
+  std::move(callback).Run();
+  return WebDatabase::State::COMMIT_NOT_NEEDED;
+}
+
 void PaymentManifestWebDataService::ClearSecurePaymentConfirmationCredentials(
     base::Time begin,
     base::Time end,
diff --git a/components/payments/content/payment_manifest_web_data_service.h b/components/payments/content/payment_manifest_web_data_service.h
index 5d3b9f8..5ac52ee5 100644
--- a/components/payments/content/payment_manifest_web_data_service.h
+++ b/components/payments/content/payment_manifest_web_data_service.h
@@ -11,6 +11,7 @@
 #include <vector>
 
 #include "base/memory/ref_counted.h"
+#include "components/payments/content/browser_binding/browser_bound_key_metadata.h"
 #include "components/payments/content/web_app_manifest.h"
 #include "components/webdata/common/web_data_service_base.h"
 #include "components/webdata/common/web_data_service_consumer.h"
@@ -100,6 +101,19 @@
       std::string relying_party_id,
       WebDataServiceConsumer* consumer);
 
+  // Get all browser bound keys. Returns the BrowserBoundKeyMetadata structs in
+  // a vector to the `callback`.
+  virtual WebDataServiceBase::Handle GetAllBrowserBoundKeys(
+      WebDataServiceRequestCallback callback);
+
+  // Deletes the browser bound keys associated to `passkeys` - the list of the
+  // relying party and credential id pairs. `callback` is invoked once the
+  // webdatabase operation has completed.
+  virtual void DeleteBrowserBoundKeys(
+      std::vector<BrowserBoundKeyMetadata::RelyingPartyAndCredentialId>
+          passkeys,
+      base::OnceClosure callback);
+
   // Override WebDataServiceConsumer interface.
   void OnWebDataServiceRequestDone(
       WebDataServiceBase::Handle h,
@@ -146,6 +160,12 @@
       std::vector<uint8_t> credential_id,
       std::string relying_party_id,
       WebDatabase* db);
+  std::unique_ptr<WDTypedResult> GetAllBrowserBoundKeysImpl(WebDatabase* db);
+  WebDatabase::State DeleteBrowserBoundKeysImpl(
+      std::vector<BrowserBoundKeyMetadata::RelyingPartyAndCredentialId>
+          passkeys,
+      base::OnceClosure callback,
+      WebDatabase* db);
 
   std::map<WebDataServiceBase::Handle, base::OnceClosure>
       clearing_credentials_requests_;
diff --git a/components/payments/content/payment_manifest_web_data_service_unittest.cc b/components/payments/content/payment_manifest_web_data_service_unittest.cc
index cc22f71..76ccb58 100644
--- a/components/payments/content/payment_manifest_web_data_service_unittest.cc
+++ b/components/payments/content/payment_manifest_web_data_service_unittest.cc
@@ -4,6 +4,9 @@
 
 #include "components/payments/content/payment_manifest_web_data_service.h"
 
+#include "base/test/bind.h"
+#include "base/test/mock_callback.h"
+#include "base/test/run_until.h"
 #include "base/test/task_environment.h"
 #include "base/test/test_timeouts.h"
 #include "components/os_crypt/async/browser/test_utils.h"
@@ -20,6 +23,26 @@
 
 using ::testing::_;
 
+testing::Matcher<BrowserBoundKeyMetadata> EqualBrowserBoundKeyMetadata(
+    std::vector<uint8_t> credential_id,
+    std::string relying_party_id,
+    std::vector<uint8_t> bbk_id) {
+  return testing::AllOf(
+      testing::Field(
+          "passkey", &BrowserBoundKeyMetadata::passkey,
+          testing::AllOf(
+              testing::Field("credential_id",
+                             &BrowserBoundKeyMetadata::
+                                 RelyingPartyAndCredentialId::credential_id,
+                             credential_id),
+              testing::Field("relying_party_id",
+                             &BrowserBoundKeyMetadata::
+                                 RelyingPartyAndCredentialId::relying_party_id,
+                             relying_party_id))),
+      testing::Field("browser_bound_key_id",
+                     &BrowserBoundKeyMetadata::browser_bound_key_id, bbk_id));
+}
+
 class MockWebDataServiceConsumer : public WebDataServiceConsumer {
  public:
   ~MockWebDataServiceConsumer() override = default;
@@ -54,6 +77,52 @@
   scoped_refptr<PaymentManifestWebDataService>
       payment_manifest_web_data_service_;
 
+  std::unique_ptr<WDTypedResult> RunAndWaitForConsumer(
+      base::OnceCallback<WebDataServiceBase::Handle(
+          WebDataServiceConsumer* consumer)> action) {
+    MockWebDataServiceConsumer mock_web_data_service_consumer;
+    base::RepeatingClosure quit_closure = task_environment_.QuitClosure();
+    WebDataServiceBase::Handle actual_handle;
+    std::unique_ptr<WDTypedResult> actual_result;
+    EXPECT_CALL(mock_web_data_service_consumer,
+                OnWebDataServiceRequestDone(_, _))
+        .WillRepeatedly([&actual_handle, &actual_result, &quit_closure](
+                            WebDataServiceBase::Handle handle,
+                            std::unique_ptr<WDTypedResult> result) {
+          actual_handle = handle;
+          actual_result = std::move(result);
+          quit_closure.Run();
+        });
+    WebDataServiceBase::Handle handle =
+        std::move(action).Run(&mock_web_data_service_consumer);
+    task_environment_.RunUntilQuit();
+    EXPECT_EQ(actual_handle, handle);
+    return actual_result;
+  }
+
+  std::unique_ptr<WDTypedResult> RunAndWaitForCallback(
+      base::OnceCallback<
+          WebDataServiceBase::Handle(WebDataServiceRequestCallback)> action) {
+    base::MockCallback<WebDataServiceRequestCallback>
+        mock_web_data_service_request_callback;
+    base::RepeatingClosure quit_closure = task_environment_.QuitClosure();
+    WebDataServiceBase::Handle actual_handle;
+    std::unique_ptr<WDTypedResult> actual_result;
+    EXPECT_CALL(mock_web_data_service_request_callback, Run(_, _))
+        .WillOnce([&actual_handle, &actual_result, &quit_closure](
+                      WebDataServiceBase::Handle handle,
+                      std::unique_ptr<WDTypedResult> result) {
+          actual_handle = handle;
+          actual_result = std::move(result);
+          quit_closure.Run();
+        });
+    WebDataServiceBase::Handle handle =
+        std::move(action).Run(mock_web_data_service_request_callback.Get());
+    task_environment_.RunUntilQuit();
+    EXPECT_EQ(actual_handle, handle);
+    return actual_result;
+  }
+
  private:
   std::unique_ptr<os_crypt_async::OSCryptAsync> os_crypt_;
   scoped_refptr<WebDatabaseService> web_database_service_;
@@ -66,48 +135,138 @@
   MockWebDataServiceConsumer mock_web_data_service_consumer;
   base::test::ScopedRunLoopTimeout scoped_timeout(
       FROM_HERE, TestTimeouts::action_max_timeout());
-  auto quit_closure = task_environment_.QuitClosure();
-  WebDataServiceBase::Handle actual_handle;
-  std::unique_ptr<WDTypedResult> actual_result;
-
-  // Save the browser bound key id.
-  EXPECT_CALL(mock_web_data_service_consumer, OnWebDataServiceRequestDone(_, _))
-      .WillRepeatedly([&actual_handle, &actual_result, &quit_closure](
-                          WebDataServiceBase::Handle handle,
-                          std::unique_ptr<WDTypedResult> result) {
-        actual_handle = handle;
-        actual_result = std::move(result);
-        quit_closure.Run();
-      });
-  WebDataServiceBase::Handle handle =
-      payment_manifest_web_data_service_->SetBrowserBoundKey(
-          credential_id, relying_party_id, browser_bound_key_id,
-          &mock_web_data_service_consumer);
-  task_environment_.RunUntilQuit();
-
-  EXPECT_EQ(actual_handle, handle);
-  ASSERT_TRUE(actual_result);
-  ASSERT_EQ(actual_result->GetType(), WDResultType::BOOL_RESULT);
-  EXPECT_TRUE(static_cast<WDResult<bool>*>(actual_result.get())->GetValue());
-
-  // Reset the closure and output values.
-  quit_closure = task_environment_.QuitClosure();
-  actual_handle = WebDataServiceBase::Handle();
-  actual_result.reset(nullptr);
+  std::unique_ptr<WDTypedResult> set_bbk_result = RunAndWaitForConsumer(
+      base::BindLambdaForTesting([&](WebDataServiceConsumer* consumer) {
+        return payment_manifest_web_data_service_->SetBrowserBoundKey(
+            credential_id, relying_party_id, browser_bound_key_id, consumer);
+      }));
+  ASSERT_TRUE(set_bbk_result);
+  ASSERT_EQ(set_bbk_result->GetType(), WDResultType::BOOL_RESULT);
+  EXPECT_TRUE(static_cast<WDResult<bool>*>(set_bbk_result.get())->GetValue());
 
   // Retrieve the browser bound key id.
-  handle = payment_manifest_web_data_service_->GetBrowserBoundKey(
-      credential_id, relying_party_id, &mock_web_data_service_consumer);
-  task_environment_.RunUntilQuit();
+  std::unique_ptr<WDTypedResult> get_bbk_result = RunAndWaitForConsumer(
+      base::BindLambdaForTesting([&](WebDataServiceConsumer* consumer) {
+        return payment_manifest_web_data_service_->GetBrowserBoundKey(
+            credential_id, relying_party_id, consumer);
+      }));
 
-  EXPECT_EQ(actual_handle, handle);
-  ASSERT_TRUE(actual_result);
-  ASSERT_EQ(actual_result->GetType(), WDResultType::BROWSER_BOUND_KEY);
+  ASSERT_TRUE(get_bbk_result);
+  ASSERT_EQ(get_bbk_result->GetType(), WDResultType::BROWSER_BOUND_KEY);
   EXPECT_EQ(static_cast<WDResult<std::optional<std::vector<uint8_t>>>*>(
-                actual_result.get())
+                get_bbk_result.get())
                 ->GetValue(),
             std::optional(browser_bound_key_id));
 }
 
+TEST_F(PaymentManifestWebDataServiceTest, GetAllBrowserBoundKey) {
+  const std::vector<uint8_t> credential_id_1({0x01, 0x02, 0x03, 0x04});
+  const std::string relying_party_id_1("relying-party.example");
+  const std::vector<uint8_t> browser_bound_key_id_1({0x11, 0x12, 0x13, 0x14});
+  const std::vector<uint8_t> credential_id_2({0x21, 0x22, 0x23, 0x24});
+  const std::string relying_party_id_2("another-relying-party.example");
+  const std::vector<uint8_t> browser_bound_key_id_2({0x31, 0x32, 0x33, 0x34});
+  RunAndWaitForConsumer(
+      base::BindLambdaForTesting([&](WebDataServiceConsumer* consumer) {
+        return payment_manifest_web_data_service_->SetBrowserBoundKey(
+            credential_id_1, relying_party_id_1, browser_bound_key_id_1,
+            consumer);
+      }));
+  RunAndWaitForConsumer(
+      base::BindLambdaForTesting([&](WebDataServiceConsumer* consumer) {
+        return payment_manifest_web_data_service_->SetBrowserBoundKey(
+            credential_id_2, relying_party_id_2, browser_bound_key_id_2,
+            consumer);
+      }));
+
+  std::unique_ptr<WDTypedResult> result =
+      RunAndWaitForCallback(base::BindLambdaForTesting(
+          [&](WebDataServiceRequestCallback request_callback) {
+            return payment_manifest_web_data_service_->GetAllBrowserBoundKeys(
+                std::move(request_callback));
+          }));
+
+  ASSERT_TRUE(result);
+  ASSERT_EQ(result->GetType(), WDResultType::BROWSER_BOUND_KEY_METADATA);
+  EXPECT_THAT(
+      static_cast<WDResult<std::vector<BrowserBoundKeyMetadata>>*>(result.get())
+          ->GetValue(),
+      testing::UnorderedElementsAre(
+          EqualBrowserBoundKeyMetadata(credential_id_1, relying_party_id_1,
+                                       browser_bound_key_id_1),
+          EqualBrowserBoundKeyMetadata(credential_id_2, relying_party_id_2,
+                                       browser_bound_key_id_2)));
+}
+
+TEST_F(PaymentManifestWebDataServiceTest, DeleteBrowserBoundKey) {
+  const std::vector<uint8_t> credential_id_1({0x01, 0x02, 0x03, 0x04});
+  const std::string relying_party_id_1("relying-party.example");
+  const std::vector<uint8_t> browser_bound_key_id_1({0x11, 0x12, 0x13, 0x14});
+  const std::vector<uint8_t> credential_id_2({0x21, 0x22, 0x23, 0x24});
+  const std::string relying_party_id_2("another-relying-party.example");
+  const std::vector<uint8_t> browser_bound_key_id_2({0x21, 0x22, 0x23, 0x24});
+  const std::vector<uint8_t> credential_id_3({0x41, 0x42, 0x43, 0x44});
+  const std::string relying_party_id_3("yet-another-relying-party.example");
+  const std::vector<uint8_t> browser_bound_key_id_3({0x51, 0x52, 0x53, 0x54});
+  RunAndWaitForConsumer(
+      base::BindLambdaForTesting([&](WebDataServiceConsumer* consumer) {
+        return payment_manifest_web_data_service_->SetBrowserBoundKey(
+            credential_id_1, relying_party_id_1, browser_bound_key_id_1,
+            consumer);
+      }));
+  RunAndWaitForConsumer(
+      base::BindLambdaForTesting([&](WebDataServiceConsumer* consumer) {
+        return payment_manifest_web_data_service_->SetBrowserBoundKey(
+            credential_id_2, relying_party_id_2, browser_bound_key_id_2,
+            consumer);
+      }));
+  RunAndWaitForConsumer(
+      base::BindLambdaForTesting([&](WebDataServiceConsumer* consumer) {
+        return payment_manifest_web_data_service_->SetBrowserBoundKey(
+            credential_id_3, relying_party_id_3, browser_bound_key_id_3,
+            consumer);
+      }));
+  base::MockCallback<base::OnceClosure> mock_callback;
+
+  EXPECT_CALL(mock_callback, Run());
+  payment_manifest_web_data_service_->DeleteBrowserBoundKeys(
+      std::vector<BrowserBoundKeyMetadata::RelyingPartyAndCredentialId>{
+          BrowserBoundKeyMetadata::RelyingPartyAndCredentialId(
+              relying_party_id_1, credential_id_1),
+          BrowserBoundKeyMetadata::RelyingPartyAndCredentialId(
+              relying_party_id_3, credential_id_3),
+      },
+      mock_callback.Get());
+
+  std::unique_ptr<WDTypedResult> result;
+  std::vector<BrowserBoundKeyMetadata> result_data;
+  ASSERT_TRUE(base::test::RunUntil([this, &result, &result_data]() -> bool {
+    result = RunAndWaitForCallback(base::BindLambdaForTesting(
+        [&](WebDataServiceRequestCallback request_callback) {
+          return payment_manifest_web_data_service_->GetAllBrowserBoundKeys(
+              std::move(request_callback));
+        }));
+    if (!result ||
+        result->GetType() != WDResultType::BROWSER_BOUND_KEY_METADATA) {
+      // Bail if the return types are not correct, and let assertions below
+      // fail the test.
+      return true;
+    }
+    result_data =
+        static_cast<WDResult<std::vector<BrowserBoundKeyMetadata>>*>(
+            result.get())
+            ->GetValue();  // GetValue() moves the value out of result.
+    // Wait until there is only 1 element being returned.
+    return result_data.size() == 1;
+  })) << "Timeout waiting for only 1 element to be present.";
+  ASSERT_TRUE(result);
+  ASSERT_EQ(result->GetType(), WDResultType::BROWSER_BOUND_KEY_METADATA);
+  EXPECT_THAT(
+      result_data,
+      testing::UnorderedElementsAre(EqualBrowserBoundKeyMetadata(
+          credential_id_2, relying_party_id_2, browser_bound_key_id_2)));
+}
+
 }  // namespace
+
 }  // namespace payments
diff --git a/components/payments/content/payment_method_manifest_table.cc b/components/payments/content/payment_method_manifest_table.cc
index 07f5560..f141b2c 100644
--- a/components/payments/content/payment_method_manifest_table.cc
+++ b/components/payments/content/payment_method_manifest_table.cc
@@ -11,6 +11,7 @@
 #include "base/feature_list.h"
 #include "base/logging.h"
 #include "base/time/time.h"
+#include "components/payments/content/browser_binding/browser_bound_key_metadata.h"
 #include "components/payments/core/secure_payment_confirmation_credential.h"
 #include "components/webdata/common/web_database.h"
 #include "content/public/common/content_features.h"
@@ -339,6 +340,37 @@
                               browser_bound_key_span.end());
 }
 
+std::vector<BrowserBoundKeyMetadata>
+PaymentMethodManifestTable::GetAllBrowserBoundKeys() {
+  sql::Statement s(db()->GetUniqueStatement(
+      "SELECT relying_party_id, credential_id, browser_bound_key_id "
+      "FROM secure_payment_confirmation_browser_bound_key"));
+  std::vector<BrowserBoundKeyMetadata> browser_bound_keys;
+  while (s.Step()) {
+    BrowserBoundKeyMetadata& entry = browser_bound_keys.emplace_back();
+    entry.passkey.relying_party_id = s.ColumnString(0);
+    s.ColumnBlobAsVector(1, &entry.passkey.credential_id);
+    s.ColumnBlobAsVector(2, &entry.browser_bound_key_id);
+  }
+  return browser_bound_keys;
+}
+
+bool PaymentMethodManifestTable::DeleteBrowserBoundKeys(
+    std::vector<BrowserBoundKeyMetadata::RelyingPartyAndCredentialId>
+        passkeys) {
+  for (auto passkey : passkeys) {
+    sql::Statement s(db()->GetUniqueStatement(
+        "DELETE FROM secure_payment_confirmation_browser_bound_key "
+        "WHERE relying_party_id = ? AND credential_id = ?"));
+    s.BindString(0, passkey.relying_party_id);
+    s.BindBlob(1, passkey.credential_id);
+    if (!s.Run()) {
+      return false;
+    }
+  }
+  return true;
+}
+
 bool PaymentMethodManifestTable::ExecuteForTest(const base::cstring_view sql) {
   return db()->Execute(sql);
 }
diff --git a/components/payments/content/payment_method_manifest_table.h b/components/payments/content/payment_method_manifest_table.h
index 2f3d9faa6..539ae75 100644
--- a/components/payments/content/payment_method_manifest_table.h
+++ b/components/payments/content/payment_method_manifest_table.h
@@ -12,6 +12,7 @@
 
 #include "base/strings/cstring_view.h"
 #include "base/time/time.h"
+#include "components/payments/content/browser_binding/browser_bound_key_metadata.h"
 #include "components/webdata/common/web_database_table.h"
 
 class WebDatabase;
@@ -154,6 +155,18 @@
   std::optional<std::vector<uint8_t>> GetBrowserBoundKey(
       std::vector<uint8_t> credential_id,
       std::string_view relying_party_id);
+
+  // Gets all browser bound key entries.
+  //
+  // Returns the possibly empty vector of entries or an empty vector when a read
+  // error occurs.
+  std::vector<BrowserBoundKeyMetadata> GetAllBrowserBoundKeys();
+
+  // Deletes the given browser bound key entries by relying_party_id and
+  // credential_id.
+  bool DeleteBrowserBoundKeys(
+      std::vector<BrowserBoundKeyMetadata::RelyingPartyAndCredentialId>
+          passkeys);
 };
 
 }  // namespace payments
diff --git a/components/pdf/browser/pdf_document_helper.cc b/components/pdf/browser/pdf_document_helper.cc
index ee8e17c..87d0e91 100644
--- a/components/pdf/browser/pdf_document_helper.cc
+++ b/components/pdf/browser/pdf_document_helper.cc
@@ -238,7 +238,7 @@
 void PDFDocumentHelper::GetPdfBytes(
     uint32_t size_limit,
     pdf::mojom::PdfListener::GetPdfBytesCallback callback) {
-  if (!remote_pdf_client_) {
+  if (!remote_pdf_client_ || !is_document_load_complete_) {
     std::move(callback).Run(pdf::mojom::PdfListener::GetPdfBytesStatus::kFailed,
                             /*bytes=*/{}, /*page_count=*/0);
     return;
@@ -249,7 +249,7 @@
 void PDFDocumentHelper::GetPageText(
     int32_t page_index,
     pdf::mojom::PdfListener::GetPageTextCallback callback) {
-  if (!remote_pdf_client_) {
+  if (!remote_pdf_client_ || !is_document_load_complete_) {
     std::move(callback).Run(std::u16string());
     return;
   }
diff --git a/components/pdf/browser/pdf_document_helper.h b/components/pdf/browser/pdf_document_helper.h
index 9d50d0e..10949a0d 100644
--- a/components/pdf/browser/pdf_document_helper.h
+++ b/components/pdf/browser/pdf_document_helper.h
@@ -96,12 +96,14 @@
 #endif
 
   // Returns whether document is loaded, at which point, the other calls to
-  // document metadata such  as `GetPdfBytes`, `GetPageText` can be made.
+  // document metadata such as `GetPdfBytes`, `GetPageText` can return data.
   bool IsDocumentLoadComplete() const { return is_document_load_complete_; }
-
+  // Get PDF bytes, if they do not exceed the size limit. If called before
+  // document is loaded, the callback will be invoked with an empty vector.
   void GetPdfBytes(uint32_t size_limit,
                    pdf::mojom::PdfListener::GetPdfBytesCallback callback);
-
+  // Returns text of the given page. If called before document is loaded, the
+  // callback will be invoked with an empty string.
   void GetPageText(int32_t page_index,
                    pdf::mojom::PdfListener::GetPageTextCallback callback);
   void GetMostVisiblePageIndex(
@@ -110,8 +112,8 @@
   // Registers `callback` to be run when document load completes successfully.
   // When the PDF is already loaded, `callback` is invoked immediately. Will not
   // be invoked when the load fails. This is useful to wait for document
-  // metadata to be loaded, before calls to `GetPdfBytes`, and `GetPageText` can
-  // be made.
+  // metadata to be loaded, before calls to `GetPdfBytes`, and `GetPageText`
+  // should be made.
   void RegisterForDocumentLoadComplete(base::OnceClosure callback);
 
  private:
diff --git a/components/persistent_cache/backend_params_manager.h b/components/persistent_cache/backend_params_manager.h
index d699e3d..72ab52ef 100644
--- a/components/persistent_cache/backend_params_manager.h
+++ b/components/persistent_cache/backend_params_manager.h
@@ -24,14 +24,12 @@
 //
 // Example:
 //  BackendParamsManager params_manager(GetPath());
-//  if (params_manager.GetParamsSyncOrCreateAsync(BackendType::kSqlite, "key",
-//                                                AccessRights::kReadOnly,
-//                                                std::move(callback))) {
-//    // Params returned synchronously. Result can be used right away.
-//    //  ....
-//  } else {
-//    // `callback` will be invoked asynchronously to return result.
-//  }
+//  params_manager.GetParamsSyncOrCreateAsync(BackendType::kSqlite, "key",
+//      AccessRights::kReadOnly,
+//      std::move(callback));
+//  // `callback` called synchronously and result can be used right away.
+//  // ... or
+//  // `callback` will be invoked asynchronously to return result.
 //
 class COMPONENT_EXPORT(PERSISTENT_CACHE) BackendParamsManager {
  public:
diff --git a/components/policy/resources/templates/policy_definitions/Miscellaneous/PdfAnnotationsEnabled.yaml b/components/policy/resources/templates/policy_definitions/Miscellaneous/PdfAnnotationsEnabled.yaml
index 80de3ee..246eed3 100644
--- a/components/policy/resources/templates/policy_definitions/Miscellaneous/PdfAnnotationsEnabled.yaml
+++ b/components/policy/resources/templates/policy_definitions/Miscellaneous/PdfAnnotationsEnabled.yaml
@@ -22,5 +22,6 @@
   type: boolean
 supported_on:
 - chrome_os:91-
+- chrome.*:138-
 tags: []
 type: main
diff --git a/components/policy/test/data/pref_mapping/PdfAnnotationsEnabled.json b/components/policy/test/data/pref_mapping/PdfAnnotationsEnabled.json
index 722a9de..c0f9980 100644
--- a/components/policy/test/data/pref_mapping/PdfAnnotationsEnabled.json
+++ b/components/policy/test/data/pref_mapping/PdfAnnotationsEnabled.json
@@ -1,6 +1,9 @@
 [
   {
     "os": [
+      "win",
+      "linux",
+      "mac",
       "chromeos"
     ],
     "policy_pref_mapping_tests": [
diff --git a/components/safe_browsing/content/browser/client_side_detection_host.cc b/components/safe_browsing/content/browser/client_side_detection_host.cc
index e714245..05a2591 100644
--- a/components/safe_browsing/content/browser/client_side_detection_host.cc
+++ b/components/safe_browsing/content/browser/client_side_detection_host.cc
@@ -773,7 +773,7 @@
   // that don't call this method on the UI thread.
   // DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
-  trigger_models_request_skipped_ = false;
+  trigger_model_request_sent_as_force_request_ = false;
   MaybeStartPreClassification(ClientSideDetectionType::TRIGGER_MODELS);
 }
 
@@ -799,19 +799,21 @@
 }
 
 void ClientSideDetectionHost::OnAsyncSafeBrowsingCheckCompleted() {
-  // If TRIGGER_MODELS ping is not skipped, do not allow async check to trigger
-  // another request. This is to avoid duplicate pings.
-  if (!trigger_models_request_skipped_) {
-    RecordAsyncCheckTriggerForceRequestResult(
-        AsyncCheckTriggerForceRequestResult::
-            kSkippedTriggerModelsPingNotSkipped);
-    return;
-  }
   if (!HasForceRequestFromRtUrlLookup()) {
     RecordAsyncCheckTriggerForceRequestResult(
         AsyncCheckTriggerForceRequestResult::kSkippedNotForced);
     return;
   }
+
+  // If a TRIGGER_MODELS requested ping is sent as a FORCE_REQUEST, do not allow
+  // async check to trigger another request. This is to avoid duplicate pings.
+  if (trigger_model_request_sent_as_force_request_) {
+    RecordAsyncCheckTriggerForceRequestResult(
+        AsyncCheckTriggerForceRequestResult::
+            kSkippedTriggerModelsPingSentAsForceRequest);
+    return;
+  }
+
   RecordAsyncCheckTriggerForceRequestResult(
       AsyncCheckTriggerForceRequestResult::kTriggered);
   MaybeStartPreClassification(ClientSideDetectionType::FORCE_REQUEST);
@@ -1160,18 +1162,21 @@
     debugging_metadata->set_forced_request(force_request_from_rt_url_lookup);
   }
 
+  trigger_model_request_sent_as_force_request_ =
+      force_request_from_rt_url_lookup;
+
   // We only send a phishing verdict if the verdict is phishing, the client
   // side detection type is |TRIGGER_MODELS|, AND the request is not a sample
   // ping. The detection type can be changed to FORCE_REQUEST from a
   // RTLookupResponse for a SBER/ESB user. This can also be changed when the
   // request is made from a notification permission prompt, keyboard & pointer
   // lock API.
-  trigger_models_request_skipped_ =
+  bool trigger_models_request_skipped =
       !verdict->is_phishing() &&
       verdict->client_side_detection_type() ==
           ClientSideDetectionType::TRIGGER_MODELS &&
       verdict->report_type() == ClientPhishingRequest::FULL_REPORT;
-  if (trigger_models_request_skipped_) {
+  if (trigger_models_request_skipped) {
     return;
   }
 
diff --git a/components/safe_browsing/content/browser/client_side_detection_host.h b/components/safe_browsing/content/browser/client_side_detection_host.h
index fa18f5f0..58c32e3f 100644
--- a/components/safe_browsing/content/browser/client_side_detection_host.h
+++ b/components/safe_browsing/content/browser/client_side_detection_host.h
@@ -61,9 +61,10 @@
   // numeric values should never be reused.
   enum class AsyncCheckTriggerForceRequestResult {
     kTriggered = 0,
-    kSkippedTriggerModelsPingNotSkipped = 1,
+    kSkippedTriggerModelsPingNotSkipped = 1,  // DEPRECATED
     kSkippedNotForced = 2,
-    kMaxValue = kSkippedNotForced,
+    kSkippedTriggerModelsPingSentAsForceRequest = 3,
+    kMaxValue = kSkippedTriggerModelsPingSentAsForceRequest,
   };
 
   // A callback via which the client of this component indicates whether the
@@ -407,9 +408,10 @@
                           permissions::PermissionRequestManager::Observer>
       permission_request_observation_{this};
 
-  // A boolean indicates whether TRIGGER_MODELS request is skipped. This is
-  // used to decide whether async check is allowed to trigger FORCE_REQUEST.
-  bool trigger_models_request_skipped_ = false;
+  // A boolean indicates whether TRIGGER_MODELS request is sent via
+  // FORCE_REQUEST. This is used to decide whether async check is allowed to
+  // trigger FORCE_REQUEST.
+  bool trigger_model_request_sent_as_force_request_ = false;
 
   // Modified through tests only. Initial value is set to the const
   // kProbabilityForAcceptingHCAllowlistTrigger.
diff --git a/components/safe_browsing/core/common/features.cc b/components/safe_browsing/core/common/features.cc
index 730d4d2..cff399b 100644
--- a/components/safe_browsing/core/common/features.cc
+++ b/components/safe_browsing/core/common/features.cc
@@ -54,7 +54,7 @@
 
 BASE_FEATURE(kClientSideDetectionBrandAndIntentForScamDetection,
              "ClientSideDetectionBrandAndIntentForScamDetection",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 BASE_FEATURE(kClientSideDetectionDebuggingMetadataCache,
              "ClientSideDetectionDebuggingMetadataCache",
@@ -98,7 +98,7 @@
 
 BASE_FEATURE(kClientSideDetectionShowScamVerdictWarning,
              "ClientSideDetectionShowScamVerdictWarning",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 BASE_FEATURE(kClientSideDetectionVibrationApi,
              "ClientSideDetectionVibrationApi",
diff --git a/components/saved_tab_groups/internal/BUILD.gn b/components/saved_tab_groups/internal/BUILD.gn
index 04789e90..d821936a 100644
--- a/components/saved_tab_groups/internal/BUILD.gn
+++ b/components/saved_tab_groups/internal/BUILD.gn
@@ -216,10 +216,10 @@
     # This is allowed until usage of SavedTabGroupKeyedService has been migrated
     # to TabGroupSyncService.
     "//chrome/browser",
+    "//chrome/browser/sync/test/integration:*",
     "//chrome/browser/ui",
     "//chrome/browser/ui/tabs:tab_group_impl",
     "//chrome/test:browser_tests",
-    "//chrome/test:sync_integration_tests",
     "//ios/chrome/browser/saved_tab_groups/model",
     "//ios/chrome/test/app:test_support",
   ]
diff --git a/components/saved_tab_groups/internal/shared_tab_group_account_data_sync_bridge.cc b/components/saved_tab_groups/internal/shared_tab_group_account_data_sync_bridge.cc
index cb0adc0..2839986a 100644
--- a/components/saved_tab_groups/internal/shared_tab_group_account_data_sync_bridge.cc
+++ b/components/saved_tab_groups/internal/shared_tab_group_account_data_sync_bridge.cc
@@ -98,34 +98,6 @@
   return entity_data;
 }
 
-// Create a new EntityData object to represent a SavedTabGroupTab. This
-// may return nullptr if the tab has not been seen yet. Tab group must
-// exist and be shared, and tab must have a "last seen" time set.
-std::unique_ptr<syncer::EntityData> CreateEntityDataFromSavedTabGroupTab(
-    const SavedTabGroupModel& model,
-    const SavedTabGroupTab& tab) {
-  const SavedTabGroup* group = model.Get(tab.saved_group_guid());
-  CHECK(group);
-
-  const std::optional<CollaborationId>& collaboration_id =
-      group->collaboration_id();
-  CHECK(collaboration_id.has_value());
-
-  sync_pb::SharedTabGroupAccountDataSpecifics specifics;
-  specifics.set_guid(tab.saved_tab_guid().AsLowercaseString());
-  specifics.set_collaboration_id(collaboration_id->value());
-  specifics.set_version(kCurrentSharedTabGroupAccountDataSpecificsProtoVersion);
-
-  sync_pb::SharedTabDetails* tab_group_details =
-      specifics.mutable_shared_tab_details();
-  tab_group_details->set_shared_tab_group_guid(
-      group->saved_guid().AsLowercaseString());
-  tab_group_details->set_last_seen_timestamp_windows_epoch(
-      SerializeTime(tab.last_seen_time().value()));
-
-  return CreateEntityDataFromSpecifics(specifics);
-}
-
 bool SharedTabExistsForSpecifics(
     const SavedTabGroupModel& model,
     const sync_pb::SharedTabGroupAccountDataSpecifics& specifics) {
@@ -657,4 +629,39 @@
           weak_ptr_factory_.GetWeakPtr()));
 }
 
+std::unique_ptr<syncer::EntityData>
+SharedTabGroupAccountDataSyncBridge::CreateEntityDataFromSavedTabGroupTab(
+    const SavedTabGroupModel& model,
+    const SavedTabGroupTab& tab) {
+  const SavedTabGroup* group = model.Get(tab.saved_group_guid());
+  CHECK(group);
+
+  const std::optional<CollaborationId>& collaboration_id =
+      group->collaboration_id();
+  CHECK(collaboration_id.has_value());
+
+  // WARNING: all fields need to be set or cleared explicitly.
+  // WARNING: if you are adding support for new
+  // `SharedTabGroupAccountDataSpecifics` fields, you need to update the
+  // following functions accordingly: `TrimSpecifics`.
+  sync_pb::SharedTabGroupAccountDataSpecifics specifics =
+      change_processor()
+          ->GetPossiblyTrimmedRemoteSpecifics(
+              CreateClientTagForSharedTab(*group, tab))
+          .shared_tab_group_account_data();
+
+  specifics.set_guid(tab.saved_tab_guid().AsLowercaseString());
+  specifics.set_collaboration_id(collaboration_id->value());
+  specifics.set_version(kCurrentSharedTabGroupAccountDataSpecificsProtoVersion);
+
+  sync_pb::SharedTabDetails* tab_group_details =
+      specifics.mutable_shared_tab_details();
+  tab_group_details->set_shared_tab_group_guid(
+      group->saved_guid().AsLowercaseString());
+  tab_group_details->set_last_seen_timestamp_windows_epoch(
+      SerializeTime(tab.last_seen_time().value()));
+
+  return CreateEntityDataFromSpecifics(specifics);
+}
+
 }  // namespace tab_groups
diff --git a/components/saved_tab_groups/internal/shared_tab_group_account_data_sync_bridge.h b/components/saved_tab_groups/internal/shared_tab_group_account_data_sync_bridge.h
index f5183ae..645ff11a 100644
--- a/components/saved_tab_groups/internal/shared_tab_group_account_data_sync_bridge.h
+++ b/components/saved_tab_groups/internal/shared_tab_group_account_data_sync_bridge.h
@@ -119,6 +119,13 @@
   // cache.
   void RemoveEntitySpecifics(const std::string& storage_key);
 
+  // Conversion method to create a EntityData object for a given
+  // SavedTabGroupTab. Tab group must exist and be shared, and tab must have a
+  // "last seen" time set.
+  std::unique_ptr<syncer::EntityData> CreateEntityDataFromSavedTabGroupTab(
+      const SavedTabGroupModel& model,
+      const SavedTabGroupTab& tab);
+
   SEQUENCE_CHECKER(sequence_checker_);
 
   // In charge of actually persisting changes to disk, or loading previous data.
diff --git a/components/saved_tab_groups/internal/shared_tab_group_account_data_sync_bridge_unittest.cc b/components/saved_tab_groups/internal/shared_tab_group_account_data_sync_bridge_unittest.cc
index c3f7c05..792fe667 100644
--- a/components/saved_tab_groups/internal/shared_tab_group_account_data_sync_bridge_unittest.cc
+++ b/components/saved_tab_groups/internal/shared_tab_group_account_data_sync_bridge_unittest.cc
@@ -35,6 +35,7 @@
 using testing::Matcher;
 using testing::Not;
 using testing::Return;
+using testing::ReturnRef;
 using testing::SaveArgPointee;
 using testing::Sequence;
 using testing::UnorderedElementsAre;
@@ -87,6 +88,38 @@
   return specifics;
 }
 
+sync_pb::SharedTabGroupAccountDataSpecifics AddUnknownFieldsToSpecifics(
+    sync_pb::SharedTabGroupAccountDataSpecifics specifics) {
+  sync_pb::test_utils::SharedTabGroupAccountDataSpecifics extended_specifics;
+  extended_specifics.set_extra_field_for_testing("extra_field");
+  sync_pb::SharedTabGroupAccountDataSpecifics specifics_with_unknown_fields;
+  bool success = specifics_with_unknown_fields.ParseFromString(
+      extended_specifics.SerializeAsString());
+  CHECK(success);
+  specifics.MergeFrom(specifics_with_unknown_fields);
+  return specifics;
+}
+
+bool HasUnknownExtraField(
+    const sync_pb::SharedTabGroupAccountDataSpecifics& specifics) {
+  sync_pb::test_utils::SharedTabGroupAccountDataSpecifics extended_specifics;
+  bool success =
+      extended_specifics.ParseFromString(specifics.SerializeAsString());
+  CHECK(success);
+  return extended_specifics.extra_field_for_testing() == "extra_field";
+}
+
+// Returns the extra (unsupported) field from `specifics` which don't have a
+// corresponding field in proto.
+std::string GetGroupExtraFieldFromSpecifics(
+    const sync_pb::SharedTabGroupAccountDataSpecifics& specifics) {
+  sync_pb::test_utils::SharedTabGroupAccountDataSpecifics extended_specifics;
+  bool success =
+      extended_specifics.ParseFromString(specifics.SerializeAsString());
+  CHECK(success);
+  return extended_specifics.extra_field_for_testing();
+}
+
 syncer::EntityData CreateEntityData(
     const sync_pb::SharedTabGroupAccountDataSpecifics& specifics,
     base::Time creation_time = base::Time::Now()) {
@@ -134,6 +167,16 @@
   return metadata;
 }
 
+MATCHER_P(EntityDataHasGroupUnsupportedFields, extra_field, "") {
+  const sync_pb::SharedTabGroupAccountDataSpecifics& arg_specifics =
+      arg.specifics.shared_tab_group_account_data();
+  return GetGroupExtraFieldFromSpecifics(arg_specifics) == extra_field;
+}
+
+MATCHER_P(GroupSpecificsHasUnsupportedField, extra_field, "") {
+  return GetGroupExtraFieldFromSpecifics(arg) == extra_field;
+}
+
 class SharedTabGroupAccountDataSyncBridgeTest : public testing::Test {
  public:
   SharedTabGroupAccountDataSyncBridgeTest()
@@ -143,8 +186,9 @@
 
   // Creates the bridges and initializes the model. Returns true when succeeds.
   void InitializeBridgeAndModel() {
-    ON_CALL(processor_, IsTrackingMetadata())
-        .WillByDefault(testing::Return(true));
+    ON_CALL(processor_, IsTrackingMetadata()).WillByDefault(Return(true));
+    ON_CALL(processor_, GetPossiblyTrimmedRemoteSpecifics(_))
+        .WillByDefault(ReturnRef(sync_pb::EntitySpecifics::default_instance()));
 
     base::RunLoop run_loop;
     base::RepeatingClosure quit_closure = run_loop.QuitClosure();
@@ -717,6 +761,90 @@
 }
 
 TEST_F(SharedTabGroupAccountDataSyncBridgeTest,
+       UnknownFields_RetainedAcrossRestartAndSentToSync) {
+  // Create a shared tab group with two tabs.
+  const CollaborationId kCollaborationId("collaboration");
+  const SavedTabGroup created_group = CreateGroupWithLocalIds(kCollaborationId);
+  const base::Uuid& group_id = created_group.saved_guid();
+
+  const SavedTabGroupTab& created_tab1 = created_group.saved_tabs()[0];
+  const base::Uuid& tab_id1 = created_tab1.saved_tab_guid();
+
+  InitializeBridgeAndModel();
+  model().AddedLocally(created_group);
+
+  EXPECT_EQ(model().Count(), 1);
+  EXPECT_TRUE(model().Contains(group_id));
+  EXPECT_FALSE(created_tab1.last_seen_time().has_value());
+
+  // Send timestamp update for both tabs from sync.
+  base::Time last_seen_time1 = base::Time::Now();
+  syncer::EntityChangeList change_list1;
+
+  // Let tab 1 have unknown fields from sync.
+  auto remote_specifics1 =
+      AddUnknownFieldsToSpecifics(CreateTabGroupAccountSpecifics(
+          kCollaborationId, created_tab1, last_seen_time1));
+  ASSERT_TRUE(HasUnknownExtraField(remote_specifics1));
+  sync_pb::EntitySpecifics entity_specifics;
+  *entity_specifics.mutable_shared_tab_group_account_data() = remote_specifics1;
+  sync_pb::EntitySpecifics trimmed_specifics1 =
+      bridge().TrimAllSupportedFieldsFromRemoteSpecifics(entity_specifics);
+  ON_CALL(mock_processor(), GetPossiblyTrimmedRemoteSpecifics(_))
+      .WillByDefault(ReturnRef(trimmed_specifics1));
+
+  change_list1.push_back(CreateAddEntityChange(remote_specifics1));
+
+  ASSERT_EQ(GetNumTabDetailsInStore(), 0u);
+
+  bridge().ApplyIncrementalSyncChanges(bridge().CreateMetadataChangeList(),
+                                       std::move(change_list1));
+
+  // Retrieve the tabs from model after the model has been updated. Verify the
+  // last seen timestamps.
+  const SavedTabGroup* group = model().Get(group_id);
+  const SavedTabGroupTab* tab1 = group->GetTab(tab_id1);
+  const std::string storage_key1 = CreateClientTagForSharedTab(*group, *tab1);
+
+  ASSERT_TRUE(tab1->last_seen_time().has_value());
+  ASSERT_EQ(tab1->last_seen_time(), last_seen_time1);
+  ASSERT_EQ(GetNumTabDetailsInStore(), 1u);
+
+  // Update the last seen timestamp for tab1 locally. The updated timestamp
+  // should be sent to sync.
+  base::Time last_seen_time3 = base::Time::Now() + base::Seconds(55);
+
+  syncer::EntityData entity_data;
+  EXPECT_CALL(mock_processor(), Put(Eq(storage_key1), _, _))
+      .WillOnce(SaveArgPointeeMove<1>(&entity_data));
+  model().UpdateTabLastSeenTime(group_id, tab_id1, last_seen_time3,
+                                TriggerSource::LOCAL);
+
+  // Verify the written specifics.
+  const sync_pb::SharedTabGroupAccountDataSpecifics& specifics =
+      entity_data.specifics.shared_tab_group_account_data();
+  EXPECT_EQ(kCurrentSharedTabGroupDataSpecificsProtoVersion,
+            specifics.version());
+  EXPECT_EQ(tab_id1.AsLowercaseString(), specifics.guid());
+  EXPECT_TRUE(specifics.has_shared_tab_details());
+
+  // Verify that unknown field is intact.
+  EXPECT_TRUE(HasUnknownExtraField(specifics));
+
+  // Mock browser restart and reload specifics from storage.
+  StoreMetadataAndReset();
+  ASSERT_EQ(model_.get(), nullptr);
+
+  InitializeBridgeAndModel();
+  task_environment_.RunUntilIdle();
+  std::optional<sync_pb::SharedTabGroupAccountDataSpecifics> specifics1 =
+      bridge().GetSpecificsForStorageKey(storage_key1);
+  // Verify that unknown field is intact after reading from storage.
+  ASSERT_TRUE(specifics1.has_value());
+  EXPECT_TRUE(HasUnknownExtraField(*specifics1));
+}
+
+TEST_F(SharedTabGroupAccountDataSyncBridgeTest,
        TabGroupDeletionLocallyWillDeleteAllTabsFromSync) {
   // Create a shared tab group with two tabs.
   const CollaborationId kCollaborationId("collaboration");
diff --git a/components/search_engines/enterprise/enterprise_search_manager.cc b/components/search_engines/enterprise/enterprise_search_manager.cc
index ff742b3..3a63937d 100644
--- a/components/search_engines/enterprise/enterprise_search_manager.cc
+++ b/components/search_engines/enterprise/enterprise_search_manager.cc
@@ -13,6 +13,8 @@
 #include "components/omnibox/common/omnibox_features.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_value_map.h"
+#include "components/prefs/scoped_user_pref_update.h"
+#include "components/search_engines/default_search_manager.h"
 #include "components/search_engines/template_url_data.h"
 #include "components/search_engines/template_url_data_util.h"
 
@@ -33,6 +35,11 @@
 // policy.
 const char EnterpriseSearchManager::kSiteSearchSettingsPrefName[] =
     "site_search_settings.template_url_data";
+// A list to hold the keywords of site search engines which the user
+// has overridden.
+const char
+    EnterpriseSearchManager::kSiteSearchSettingsOverriddenKeywordsPrefName[] =
+        "site_search_settings.overridden_keywords";
 
 // A dictionary to hold all TemplateURL data related to the enterprise search
 // aggregator defined by policy.
@@ -71,6 +78,7 @@
 void EnterpriseSearchManager::RegisterProfilePrefs(
     user_prefs::PrefRegistrySyncable* registry) {
   registry->RegisterListPref(kSiteSearchSettingsPrefName);
+  registry->RegisterListPref(kSiteSearchSettingsOverriddenKeywordsPrefName);
   registry->RegisterListPref(kEnterpriseSearchAggregatorSettingsPrefName);
   registry->RegisterBooleanPref(
       kEnterpriseSearchAggregatorSettingsRequireShortcutPrefName, false);
@@ -120,8 +128,30 @@
     return LoadingResult::kUnavailable;
   }
 
+  // Get the list of engines from the main policy pref.
+  const base::Value::List& engine_list = pref->GetValue()->GetList();
+  // For site search engines, load the
+  // `kSiteSearchSettingsOverriddenKeywordsPrefName` pref dictionary.
+  if (base::FeatureList::IsEnabled(
+          omnibox::kEnableSiteSearchAllowUserOverridePolicy) &&
+      pref->name() == kSiteSearchSettingsPrefName) {
+    LoadOverriddenKeywordsPref(engine_list);
+  }
+
   LoadingResult result = LoadingResult::kAvailableEmpty;
-  for (const base::Value& engine : pref->GetValue()->GetList()) {
+  for (const base::Value& engine : engine_list) {
+    const base::Value::Dict& engine_dict = engine.GetDict();
+    const std::string* keyword =
+        engine_dict.FindString(DefaultSearchManager::kKeyword);
+    CHECK(keyword);
+    if (base::FeatureList::IsEnabled(
+            omnibox::kEnableSiteSearchAllowUserOverridePolicy) &&
+        pref_service_
+            ->GetList(EnterpriseSearchManager::
+                          kSiteSearchSettingsOverriddenKeywordsPrefName)
+            .contains(*keyword)) {
+      continue;
+    }
     search_engines->emplace_back(DictToTemplateURLData(engine));
     result = LoadingResult::kAvailableNonEmpty;
   }
@@ -152,3 +182,42 @@
       });
   return LoadingResult::kAvailableNonEmpty;
 }
+
+void EnterpriseSearchManager::LoadOverriddenKeywordsPref(
+    const base::Value::List& engine_list) {
+  ScopedListPrefUpdate overridden_keywords_update(
+      pref_service_, kSiteSearchSettingsOverriddenKeywordsPrefName);
+  base::Value::List& overridden_keywords_list =
+      overridden_keywords_update.Get();
+
+  // Keep track of keywords present in the current policy along with whether
+  // they are enforced by policy.
+  base::flat_map<std::string, bool> policy_keywords_enforced_status;
+  for (const base::Value& engine : engine_list) {
+    const base::Value::Dict& engine_dict = engine.GetDict();
+    const std::string* keyword =
+        engine_dict.FindString(DefaultSearchManager::kKeyword);
+    CHECK(keyword);
+    bool enforced_by_policy =
+        engine_dict.FindBool(DefaultSearchManager::kEnforcedByPolicy)
+            .value_or(false);
+    policy_keywords_enforced_status[*keyword] = enforced_by_policy;
+  }
+
+  // Remove keywords from the overridden keywords list for engines that are no
+  // longer present in the policy or that are NOW enforced.
+  overridden_keywords_list.EraseIf(
+      [&policy_keywords_enforced_status](const base::Value& v) {
+        auto it = policy_keywords_enforced_status.find(v.GetString());
+        return it == policy_keywords_enforced_status.end() || it->second;
+      });
+}
+
+void EnterpriseSearchManager::AddOverriddenKeyword(const std::string& keyword) {
+  if (!pref_service_) {
+    return;
+  }
+  ScopedListPrefUpdate overridden_keywords_update(
+      pref_service_, kSiteSearchSettingsOverriddenKeywordsPrefName);
+  overridden_keywords_update.Get().Append(keyword);
+}
diff --git a/components/search_engines/enterprise/enterprise_search_manager.h b/components/search_engines/enterprise/enterprise_search_manager.h
index 36a7370e..3ca16a5 100644
--- a/components/search_engines/enterprise/enterprise_search_manager.h
+++ b/components/search_engines/enterprise/enterprise_search_manager.h
@@ -23,6 +23,7 @@
 class EnterpriseSearchManager {
  public:
   static const char kSiteSearchSettingsPrefName[];
+  static const char kSiteSearchSettingsOverriddenKeywordsPrefName[];
   static const char kEnterpriseSearchAggregatorSettingsPrefName[];
   static const char
       kEnterpriseSearchAggregatorSettingsRequireShortcutPrefName[];
@@ -59,6 +60,10 @@
   // set.
   bool GetRequireShortcutValue() const;
 
+  // Adds a keyword to the `kSiteSearchSettingsOverriddenKeywordsPrefName`
+  // pref, indicating that the user has overridden the associated engine.
+  void AddOverriddenKeyword(const std::string& keyword);
+
  private:
   // Handles changes to managed prefs due to policy updates. Calls
   // NotifyObserver() if search providers may have changed. Invokes
@@ -72,6 +77,10 @@
   LoadingResult LoadSearchAggregator(
       EnterpriseSearchManager::OwnedTemplateURLDataVector* search_engines);
 
+  // Updates the `kSiteSearchSettingsOverriddenKeywordsPrefName` pref based
+  // on the provided list of site search engines.
+  void LoadOverriddenKeywordsPref(const base::Value::List& engine_list);
+
   raw_ptr<PrefService> pref_service_;
   PrefChangeRegistrar pref_change_registrar_;
 
diff --git a/components/search_engines/enterprise/enterprise_search_manager_unittest.cc b/components/search_engines/enterprise/enterprise_search_manager_unittest.cc
index ee0c3f1..384e69b 100644
--- a/components/search_engines/enterprise/enterprise_search_manager_unittest.cc
+++ b/components/search_engines/enterprise/enterprise_search_manager_unittest.cc
@@ -29,13 +29,14 @@
     base::Time::Now().ToDeltaSinceWindowsEpoch().InMicroseconds());
 
 base::Value::Dict GenerateSearchPrefEntry(const std::string& keyword,
-                                          bool featured) {
+                                          bool featured,
+                                          bool enforced_by_policy) {
   base::Value::Dict entry;
   entry.Set(DefaultSearchManager::kShortName, keyword + "name");
   entry.Set(DefaultSearchManager::kKeyword, featured ? "@" + keyword : keyword);
   entry.Set(DefaultSearchManager::kURL,
             std::string("https://") + keyword + ".com/{searchTerms}");
-  entry.Set(DefaultSearchManager::kEnforcedByPolicy, false);
+  entry.Set(DefaultSearchManager::kEnforcedByPolicy, enforced_by_policy);
   entry.Set(DefaultSearchManager::kFeaturedByPolicy, featured);
   entry.Set(DefaultSearchManager::kFaviconURL,
             std::string("https://") + keyword + ".com/favicon.ico");
@@ -45,9 +46,10 @@
   return entry;
 }
 
-base::Value::Dict GenerateSiteSearchPrefEntry(const std::string& keyword) {
+base::Value::Dict GenerateSiteSearchPrefEntry(const std::string& keyword,
+                                              bool enforced_by_policy = true) {
   base::Value::Dict entry =
-      GenerateSearchPrefEntry(keyword, /*featured=*/false);
+      GenerateSearchPrefEntry(keyword, /*featured=*/false, enforced_by_policy);
   entry.Set(DefaultSearchManager::kPolicyOrigin,
             static_cast<int>(TemplateURLData::PolicyOrigin::kSiteSearch));
   return entry;
@@ -55,7 +57,8 @@
 
 base::Value::Dict GenerateSearchAggregatorPrefEntry(const std::string& keyword,
                                                     bool featured) {
-  base::Value::Dict entry = GenerateSearchPrefEntry(keyword, featured);
+  base::Value::Dict entry =
+      GenerateSearchPrefEntry(keyword, featured, /*enforced_by_policy=*/true);
   entry.Set(DefaultSearchManager::kPolicyOrigin,
             static_cast<int>(TemplateURLData::PolicyOrigin::kSearchAggregator));
   entry.Set(DefaultSearchManager::kSuggestionsURL,
@@ -89,13 +92,7 @@
 
   void SetUp() override {
     EnterpriseSearchManagerTestBase::SetUp();
-
-    scoped_feature_list_.InitAndEnableFeature(
-        omnibox::kEnableSearchAggregatorPolicy);
   }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
 TEST_F(EnterpriseSearchManagerTest, EmptyList) {
@@ -110,14 +107,194 @@
       base::Value::List());
 }
 
-TEST_F(EnterpriseSearchManagerTest, SiteSearchOnly) {
+TEST_F(EnterpriseSearchManagerTest,
+       SiteSearchOnly_AllowUserOverrideFeatureOff) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndDisableFeature(
+      omnibox::kEnableSiteSearchAllowUserOverridePolicy);
+
   base::Value::List pref_value;
   pref_value.Append(GenerateSiteSearchPrefEntry("work"));
   pref_value.Append(GenerateSiteSearchPrefEntry("docs"));
+  pref_value.Append(
+      GenerateSiteSearchPrefEntry("mail", /*enforced_by_policy=*/false));
+  pref_value.Append(
+      GenerateSiteSearchPrefEntry("calendar", /*enforced_by_policy=*/false));
 
   base::MockRepeatingCallback<void(
       EnterpriseSearchManager::OwnedTemplateURLDataVector&&)>
       callback;
+  EXPECT_CALL(callback,
+              Run(ElementsAre(
+                  Pointee(Property(&TemplateURLData::keyword, u"work")),
+                  Pointee(Property(&TemplateURLData::keyword, u"docs")),
+                  Pointee(Property(&TemplateURLData::keyword, u"mail")),
+                  Pointee(Property(&TemplateURLData::keyword, u"calendar")))))
+      .Times(1);
+
+  EnterpriseSearchManager manager(pref_service(), callback.Get());
+  pref_service()->SetManagedPref(
+      EnterpriseSearchManager::kSiteSearchSettingsPrefName,
+      std::move(pref_value));
+
+  const base::Value::List& final_overridden_keywords = pref_service()->GetList(
+      EnterpriseSearchManager::kSiteSearchSettingsOverriddenKeywordsPrefName);
+  EXPECT_THAT(final_overridden_keywords, IsEmpty());
+}
+
+TEST_F(EnterpriseSearchManagerTest, SiteSearchOnly_AllowUserOverrideFeatureOn) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      omnibox::kEnableSiteSearchAllowUserOverridePolicy);
+
+  base::Value::List pref_value;
+  pref_value.Append(GenerateSiteSearchPrefEntry("work"));
+  pref_value.Append(GenerateSiteSearchPrefEntry("docs"));
+  pref_value.Append(
+      GenerateSiteSearchPrefEntry("mail", /*enforced_by_policy=*/false));
+  pref_value.Append(
+      GenerateSiteSearchPrefEntry("calendar", /*enforced_by_policy=*/false));
+
+  base::MockRepeatingCallback<void(
+      EnterpriseSearchManager::OwnedTemplateURLDataVector&&)>
+      callback;
+  EXPECT_CALL(callback,
+              Run(ElementsAre(
+                  Pointee(Property(&TemplateURLData::keyword, u"work")),
+                  Pointee(Property(&TemplateURLData::keyword, u"docs")),
+                  Pointee(Property(&TemplateURLData::keyword, u"mail")),
+                  Pointee(Property(&TemplateURLData::keyword, u"calendar")))))
+      .Times(1);
+
+  EnterpriseSearchManager manager(pref_service(), callback.Get());
+  pref_service()->SetManagedPref(
+      EnterpriseSearchManager::kSiteSearchSettingsPrefName,
+      std::move(pref_value));
+
+  const base::Value::List& final_overridden_keywords = pref_service()->GetList(
+      EnterpriseSearchManager::kSiteSearchSettingsOverriddenKeywordsPrefName);
+  EXPECT_THAT(final_overridden_keywords, IsEmpty());
+}
+
+TEST_F(EnterpriseSearchManagerTest,
+       SiteSearch_SetOverriddenKeyword_AllowUserOverrideFeatureOn) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      omnibox::kEnableSiteSearchAllowUserOverridePolicy);
+
+  base::Value::List pref_value;
+  pref_value.Append(GenerateSiteSearchPrefEntry("work"));
+  pref_value.Append(GenerateSiteSearchPrefEntry("docs"));
+  pref_value.Append(
+      GenerateSiteSearchPrefEntry("mail", /*enforced_by_policy=*/false));
+  pref_value.Append(
+      GenerateSiteSearchPrefEntry("calendar", /*enforced_by_policy=*/false));
+
+  base::MockRepeatingCallback<void(
+      EnterpriseSearchManager::OwnedTemplateURLDataVector&&)>
+      callback;
+  EXPECT_CALL(callback,
+              Run(ElementsAre(
+                  Pointee(Property(&TemplateURLData::keyword, u"work")),
+                  Pointee(Property(&TemplateURLData::keyword, u"docs")),
+                  Pointee(Property(&TemplateURLData::keyword, u"mail")),
+                  Pointee(Property(&TemplateURLData::keyword, u"calendar")))))
+      .Times(1);
+
+  EnterpriseSearchManager manager(pref_service(), callback.Get());
+  pref_service()->SetManagedPref(
+      EnterpriseSearchManager::kSiteSearchSettingsPrefName,
+      std::move(pref_value));
+
+  // Mark "mail" as overridden by user.
+  manager.AddOverriddenKeyword("mail");
+
+  const base::Value::List& overridden_keywords_pref = pref_service()->GetList(
+      EnterpriseSearchManager::kSiteSearchSettingsOverriddenKeywordsPrefName);
+  EXPECT_THAT(overridden_keywords_pref.size(), 1);
+  EXPECT_TRUE(overridden_keywords_pref.contains("mail"));
+}
+
+TEST_F(
+    EnterpriseSearchManagerTest,
+    SiteSearch_ResetOverriddenKeywordWhenEnforced_AllowUserOverrideFeatureOn) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      omnibox::kEnableSiteSearchAllowUserOverridePolicy);
+
+  base::Value::List initial_pref_value;
+  initial_pref_value.Append(GenerateSiteSearchPrefEntry("work"));
+  initial_pref_value.Append(GenerateSiteSearchPrefEntry("docs"));
+  initial_pref_value.Append(
+      GenerateSiteSearchPrefEntry("mail", /*enforced_by_policy=*/false));
+
+  base::MockRepeatingCallback<void(
+      EnterpriseSearchManager::OwnedTemplateURLDataVector&&)>
+      callback;
+  EXPECT_CALL(
+      callback,
+      Run(ElementsAre(Pointee(Property(&TemplateURLData::keyword, u"work")),
+                      Pointee(Property(&TemplateURLData::keyword, u"docs")),
+                      Pointee(Property(&TemplateURLData::keyword, u"mail")))))
+      .Times(1);
+  EXPECT_CALL(callback,
+              Run(ElementsAre(
+                  Pointee(Property(&TemplateURLData::keyword, u"work")),
+                  Pointee(Property(&TemplateURLData::keyword, u"docs")),
+                  Pointee(Property(&TemplateURLData::keyword, u"mail")),
+                  Pointee(Property(&TemplateURLData::keyword, u"calendar")))))
+      .Times(1);
+
+  EnterpriseSearchManager manager(pref_service(), callback.Get());
+  pref_service()->SetManagedPref(
+      EnterpriseSearchManager::kSiteSearchSettingsPrefName,
+      std::move(initial_pref_value));
+
+  // Mark "mail" as overridden by user.
+  manager.AddOverriddenKeyword("mail");
+  const base::Value::List& overridden_keywords_pref = pref_service()->GetList(
+      EnterpriseSearchManager::kSiteSearchSettingsOverriddenKeywordsPrefName);
+  EXPECT_TRUE(overridden_keywords_pref.contains("mail"));
+
+  // Update policy to make "mail" enforced and add "calendar" as enforced.
+  base::Value::List updated_pref_value;
+  updated_pref_value.Append(GenerateSiteSearchPrefEntry("work"));
+  updated_pref_value.Append(GenerateSiteSearchPrefEntry("docs"));
+  updated_pref_value.Append(
+      GenerateSiteSearchPrefEntry("mail", /*enforced_by_policy=*/true));
+  updated_pref_value.Append(
+      GenerateSiteSearchPrefEntry("calendar", /*enforced_by_policy=*/true));
+  pref_service()->SetManagedPref(
+      EnterpriseSearchManager::kSiteSearchSettingsPrefName,
+      std::move(updated_pref_value));
+
+  EXPECT_THAT(overridden_keywords_pref, IsEmpty());
+}
+
+TEST_F(EnterpriseSearchManagerTest,
+       SiteSearch_RemoveKeywordWhenNotInPolicy_AllowUserOverrideFeatureOn) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      omnibox::kEnableSiteSearchAllowUserOverridePolicy);
+
+  base::Value::List initial_pref_value;
+  initial_pref_value.Append(GenerateSiteSearchPrefEntry("work"));
+  initial_pref_value.Append(GenerateSiteSearchPrefEntry("docs"));
+  initial_pref_value.Append(
+      GenerateSiteSearchPrefEntry("mail", /*enforced_by_policy=*/false));
+  initial_pref_value.Append(
+      GenerateSiteSearchPrefEntry("calendar", /*enforced_by_policy=*/false));
+
+  base::MockRepeatingCallback<void(
+      EnterpriseSearchManager::OwnedTemplateURLDataVector&&)>
+      callback;
+  EXPECT_CALL(callback,
+              Run(ElementsAre(
+                  Pointee(Property(&TemplateURLData::keyword, u"work")),
+                  Pointee(Property(&TemplateURLData::keyword, u"docs")),
+                  Pointee(Property(&TemplateURLData::keyword, u"mail")),
+                  Pointee(Property(&TemplateURLData::keyword, u"calendar")))))
+      .Times(1);
   EXPECT_CALL(
       callback,
       Run(ElementsAre(Pointee(Property(&TemplateURLData::keyword, u"work")),
@@ -127,10 +304,25 @@
   EnterpriseSearchManager manager(pref_service(), callback.Get());
   pref_service()->SetManagedPref(
       EnterpriseSearchManager::kSiteSearchSettingsPrefName,
-      std::move(pref_value));
+      std::move(initial_pref_value));
+
+  const base::Value::List& overridden_keywords_pref = pref_service()->GetList(
+      EnterpriseSearchManager::kSiteSearchSettingsOverriddenKeywordsPrefName);
+  EXPECT_THAT(overridden_keywords_pref, IsEmpty());
+
+  // Update policy to remove "mail" and "calendar".
+  base::Value::List updated_pref_value;
+  updated_pref_value.Append(GenerateSiteSearchPrefEntry("work"));
+  updated_pref_value.Append(GenerateSiteSearchPrefEntry("docs"));
+  pref_service()->SetManagedPref(
+      EnterpriseSearchManager::kSiteSearchSettingsPrefName,
+      std::move(updated_pref_value));
+
+  EXPECT_THAT(overridden_keywords_pref, IsEmpty());
 }
 
-TEST_F(EnterpriseSearchManagerTest, SearchAggregatorsOnly) {
+TEST_F(EnterpriseSearchManagerTest,
+       SearchAggregatorsOnly_AllowUserOverrideFeatureOff) {
   base::Value::List pref_value;
   pref_value.Append(
       GenerateSearchAggregatorPrefEntry("aggregator", /*featured=*/true));
@@ -150,6 +342,41 @@
   pref_service()->SetManagedPref(
       EnterpriseSearchManager::kEnterpriseSearchAggregatorSettingsPrefName,
       std::move(pref_value));
+
+  const base::Value::List& final_overridden_keywords = pref_service()->GetList(
+      EnterpriseSearchManager::kSiteSearchSettingsOverriddenKeywordsPrefName);
+  EXPECT_THAT(final_overridden_keywords, IsEmpty());
+}
+
+TEST_F(EnterpriseSearchManagerTest,
+       SearchAggregatorsOnly_AllowUserOverrideFeatureOn) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      omnibox::kEnableSiteSearchAllowUserOverridePolicy);
+
+  base::Value::List pref_value;
+  pref_value.Append(
+      GenerateSearchAggregatorPrefEntry("aggregator", /*featured=*/true));
+  pref_value.Append(
+      GenerateSearchAggregatorPrefEntry("aggregator", /*featured=*/false));
+
+  base::MockRepeatingCallback<void(
+      EnterpriseSearchManager::OwnedTemplateURLDataVector&&)>
+      callback;
+  EXPECT_CALL(callback,
+              Run(ElementsAre(
+                  Pointee(Property(&TemplateURLData::keyword, u"@aggregator")),
+                  Pointee(Property(&TemplateURLData::keyword, u"aggregator")))))
+      .Times(1);
+
+  EnterpriseSearchManager manager(pref_service(), callback.Get());
+  pref_service()->SetManagedPref(
+      EnterpriseSearchManager::kEnterpriseSearchAggregatorSettingsPrefName,
+      std::move(pref_value));
+
+  const base::Value::List& final_overridden_keywords = pref_service()->GetList(
+      EnterpriseSearchManager::kSiteSearchSettingsOverriddenKeywordsPrefName);
+  EXPECT_THAT(final_overridden_keywords, IsEmpty());
 }
 
 TEST_F(EnterpriseSearchManagerTest,
diff --git a/components/search_engines/enterprise/site_search_policy_handler.cc b/components/search_engines/enterprise/site_search_policy_handler.cc
index 56a0426..36f0fb8 100644
--- a/components/search_engines/enterprise/site_search_policy_handler.cc
+++ b/components/search_engines/enterprise/site_search_policy_handler.cc
@@ -30,6 +30,18 @@
 
 namespace {
 
+bool IsAllowUserOverrideFieldEnabled() {
+  // Check that FeatureList is available as a protection against early startup
+  // crashes. Some policy providers are initialized very early even before
+  // base::FeatureList is available, but when policies are finally applied, the
+  // feature stack is fully initialized. The instance check ensures that the
+  // final decision is delayed until all features are initialized, without any
+  // other downstream effect.
+  return base::FeatureList::GetInstance() &&
+         base::FeatureList::IsEnabled(
+             omnibox::kEnableSiteSearchAllowUserOverridePolicy);
+}
+
 // Converts a site search policy entry `policy_dict` into a dictionary to be
 // saved to prefs, with fields corresponding to `TemplateURLData`.
 base::Value SiteSearchDictFromPolicyValue(const base::Value::Dict& policy_dict,
@@ -56,7 +68,13 @@
 
   dict.Set(DefaultSearchManager::kPolicyOrigin,
            static_cast<int>(TemplateURLData::PolicyOrigin::kSiteSearch));
-  dict.Set(DefaultSearchManager::kEnforcedByPolicy, true);
+
+  const bool allow_user_override =
+      policy_dict.FindBool(SiteSearchPolicyHandler::kAllowUserOverride)
+          .value_or(false);
+  dict.Set(DefaultSearchManager::kEnforcedByPolicy,
+           !IsAllowUserOverrideFieldEnabled() || !allow_user_override);
+
   dict.Set(DefaultSearchManager::kIsActive,
            static_cast<int>(TemplateURLData::ActiveStatus::kTrue));
 
@@ -113,6 +131,8 @@
 const char SiteSearchPolicyHandler::kShortcut[] = "shortcut";
 const char SiteSearchPolicyHandler::kUrl[] = "url";
 const char SiteSearchPolicyHandler::kFeatured[] = "featured";
+const char SiteSearchPolicyHandler::kAllowUserOverride[] =
+    "allow_user_override";
 
 const int SiteSearchPolicyHandler::kMaxSiteSearchProviders = 100;
 const int SiteSearchPolicyHandler::kMaxFeaturedProviders = 3;
diff --git a/components/search_engines/enterprise/site_search_policy_handler.h b/components/search_engines/enterprise/site_search_policy_handler.h
index 2ef5a70c..b388abf 100644
--- a/components/search_engines/enterprise/site_search_policy_handler.h
+++ b/components/search_engines/enterprise/site_search_policy_handler.h
@@ -21,6 +21,7 @@
   static const char kShortcut[];
   static const char kUrl[];
   static const char kFeatured[];
+  static const char kAllowUserOverride[];
 
   // The maximum number of site search providers to be defined via policy, to
   // avoid issues with very long lists.
diff --git a/components/search_engines/enterprise/site_search_policy_handler_unittest.cc b/components/search_engines/enterprise/site_search_policy_handler_unittest.cc
index 0f835d3..9ce71b6 100644
--- a/components/search_engines/enterprise/site_search_policy_handler_unittest.cc
+++ b/components/search_engines/enterprise/site_search_policy_handler_unittest.cc
@@ -9,6 +9,8 @@
 
 #include "components/search_engines/enterprise/site_search_policy_handler.h"
 
+#include <optional>
+
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
@@ -39,14 +41,14 @@
 namespace {
 
 // Represents field values for SiteSearchSettings policy, used for generating
-// policy value entries. Fields set as nullptr will not be added to the
-// entry dictionary.
+// policy value entries.
 struct TestProvider {
-  const char* name;
-  const char* shortcut;
-  const char* url;
+  std::optional<std::string> name;
+  std::optional<std::string> shortcut;
+  std::optional<std::string> url;
   bool featured_by_policy = false;
-  const char* favicon;
+  std::optional<std::string> favicon;
+  std::optional<bool> allow_user_override;
 };
 
 // Represents field values for EnterpriseSearchAggregatorSettings policy, used
@@ -75,37 +77,43 @@
      .favicon = "https://docs.com/favicon.ico"},
 };
 
-// Used for tests that require providers with missing required fields. Missing
-// fields for test are represented as nullptr.
+// Used for tests that require a list of valid providers.
+TestProvider kValidTestProvidersWithAllowUserOverride[] = {
+    {.name = "work name",
+     .shortcut = "work",
+     .url = "https://work.com/{searchTerms}",
+     .favicon = "https://work.com/favicon.ico",
+     .allow_user_override = true},
+    {.name = "docs name",
+     .shortcut = "docs",
+     .url = "https://docs.com/{searchTerms}",
+     .favicon = "https://docs.com/favicon.ico",
+     .allow_user_override = false},
+    {.name = "mail name",
+     .shortcut = "mail",
+     .url = "https://mail.com/{searchTerms}",
+     .favicon = "https://mail.com/favicon.ico",
+     .allow_user_override = std::nullopt},
+};
+
+// Used for tests that require providers with missing required fields.
 TestProvider kMissingRequiredFieldsTestProviders[] = {
-    {.name = nullptr,
-     .shortcut = "missing_name",
-     .url = "https://missing_name.com/{searchTerms}",
-     .favicon = nullptr},
+    {.shortcut = "missing_name",
+     .url = "https://missing_name.com/{searchTerms}"},
     {.name = "missing_shortcut name",
-     .shortcut = nullptr,
-     .url = "https://missing_shortcut.com/{searchTerms}",
-     .favicon = nullptr},
-    {.name = "missing_url name",
-     .shortcut = "missing_url",
-     .url = nullptr,
-     .favicon = nullptr},
+     .url = "https://missing_shortcut.com/{searchTerms}"},
+    {.name = "missing_url name", .shortcut = "missing_url"},
 };
 
 // Used for tests that require providers with empty required fields.
 TestProvider kEmptyFieldTestProviders[] = {
     {.name = "",
      .shortcut = "empty_name",
-     .url = "https://empty_name.com/{searchTerms}",
-     .favicon = nullptr},
+     .url = "https://empty_name.com/{searchTerms}"},
     {.name = "empty_shortcut name",
      .shortcut = "",
-     .url = "https://empty_shortcut.com/{searchTerms}",
-     .favicon = nullptr},
-    {.name = "empty_url name",
-     .shortcut = "empty_url",
-     .url = "",
-     .favicon = nullptr},
+     .url = "https://empty_shortcut.com/{searchTerms}"},
+    {.name = "empty_url name", .shortcut = "empty_url", .url = ""},
 };
 
 // Used for tests that require a provider with unknown field.
@@ -121,12 +129,10 @@
 TestProvider kShortcutNotUniqueTestProviders[] = {
     {.name = "work name",
      .shortcut = "work",
-     .url = "https://work.com/q={searchTerms}&x",
-     .favicon = nullptr},
+     .url = "https://work.com/q={searchTerms}&x"},
     {.name = "also work name",
      .shortcut = "work",
-     .url = "https://work.com/q={searchTerms}&y",
-     .favicon = nullptr},
+     .url = "https://work.com/q={searchTerms}&y"},
     {.name = "docs name",
      .shortcut = "docs",
      .url = "https://docs.com/{searchTerms}",
@@ -138,12 +144,10 @@
 TestProvider kNoUniqueShortcutTestProviders[] = {
     {.name = "work name",
      .shortcut = "work",
-     .url = "https://work.com/q={searchTerms}&x",
-     .favicon = nullptr},
+     .url = "https://work.com/q={searchTerms}&x"},
     {.name = "also work name",
      .shortcut = "work",
-     .url = "https://work.com/q={searchTerms}&y",
-     .favicon = nullptr},
+     .url = "https://work.com/q={searchTerms}&y"},
 };
 
 // Used for tests that require a provider shortcut containing a space
@@ -151,24 +155,20 @@
 TestProvider kShortcutWithSpacesTestProviders[] = {
     {.name = "work name 1",
      .shortcut = " shortcut",
-     .url = "https://work1.com/q={searchTerms}&x",
-     .favicon = nullptr},
+     .url = "https://work1.com/q={searchTerms}&x"},
     {.name = "work name 2",
      .shortcut = "shortcut ",
-     .url = "https://work2.com/q={searchTerms}&x",
-     .favicon = nullptr},
+     .url = "https://work2.com/q={searchTerms}&x"},
     {.name = "work name 3",
      .shortcut = "short cut",
-     .url = "https://work3.com/q={searchTerms}&x",
-     .favicon = nullptr},
+     .url = "https://work3.com/q={searchTerms}&x"},
 };
 
 // Used for tests that require a provider shortcut that stars with @.
 TestProvider kShortcutStartsWithAtTestProviders[] = {
     {.name = "invalid",
      .shortcut = "@work",
-     .url = "https://work.com/q={searchTerms}&x",
-     .favicon = nullptr},
+     .url = "https://work.com/q={searchTerms}&x"},
     {.name = "valid",
      .shortcut = "wo@rk",
      .url = "https://work.com/q={searchTerms}&y",
@@ -179,16 +179,13 @@
 TestProvider kInvalidUrlTestProviders[] = {
     {.name = "invalid1 name",
      .shortcut = "invalid1",
-     .url = "https://work.com/q=searchTerms",
-     .favicon = nullptr},
+     .url = "https://work.com/q=searchTerms"},
     {.name = "invalid2 name",
      .shortcut = "invalid2",
-     .url = "https://work.com/q=%s",
-     .favicon = nullptr},
+     .url = "https://work.com/q=%s"},
     {.name = "invalid3 name",
      .shortcut = "invalid3",
-     .url = "https://work.com",
-     .favicon = nullptr},
+     .url = "https://work.com"},
 };
 
 constexpr char kDSPKeyword[] = "dsp_keyword";
@@ -277,16 +274,20 @@
 
 base::Value::Dict GenerateSiteSearchPolicyEntry(TestProvider test_case) {
   base::Value::Dict entry;
-  if (test_case.name) {
-    entry.Set(SiteSearchPolicyHandler::kName, test_case.name);
+  if (test_case.name.has_value()) {
+    entry.Set(SiteSearchPolicyHandler::kName, test_case.name.value());
   }
-  if (test_case.shortcut) {
-    entry.Set(SiteSearchPolicyHandler::kShortcut, test_case.shortcut);
+  if (test_case.shortcut.has_value()) {
+    entry.Set(SiteSearchPolicyHandler::kShortcut, test_case.shortcut.value());
   }
-  if (test_case.url) {
-    entry.Set(SiteSearchPolicyHandler::kUrl, test_case.url);
+  if (test_case.url.has_value()) {
+    entry.Set(SiteSearchPolicyHandler::kUrl, test_case.url.value());
   }
   entry.Set(SiteSearchPolicyHandler::kFeatured, test_case.featured_by_policy);
+  if (test_case.allow_user_override.has_value()) {
+    entry.Set(SiteSearchPolicyHandler::kAllowUserOverride,
+              test_case.allow_user_override.value());
+  }
   return entry;
 }
 
@@ -316,24 +317,26 @@
 
 // Returns a matcher that accepts entries for the pref corresponding to the
 // site search policy. Field values are obtained from |test_case|.
-testing::Matcher<const base::Value&> IsSiteSearchEntry(TestProvider test_case,
-                                                       bool featured) {
-  std::string expected_keyword =
-      base::StringPrintf("%s%s", (featured ? "@" : ""), test_case.shortcut);
+testing::Matcher<const base::Value&> IsSiteSearchEntry(
+    TestProvider test_case,
+    bool featured,
+    bool enforced_by_policy) {
+  std::string expected_keyword = base::StringPrintf(
+      "%s%s", (featured ? "@" : ""), test_case.shortcut.value());
   return AllOf(
-      HasStringField(DefaultSearchManager::kShortName,
-                     std::string(test_case.name)),
+      HasStringField(DefaultSearchManager::kShortName, test_case.name.value()),
       HasStringField(DefaultSearchManager::kKeyword, expected_keyword),
-      HasStringField(DefaultSearchManager::kURL, std::string(test_case.url)),
+      HasStringField(DefaultSearchManager::kURL, test_case.url.value()),
       HasBooleanField(DefaultSearchManager::kFeaturedByPolicy, featured),
       HasIntegerField(
           DefaultSearchManager::kPolicyOrigin,
           static_cast<int>(TemplateURLData::PolicyOrigin::kSiteSearch)),
-      HasBooleanField(DefaultSearchManager::kEnforcedByPolicy, true),
+      HasBooleanField(DefaultSearchManager::kEnforcedByPolicy,
+                      enforced_by_policy),
       HasIntegerField(DefaultSearchManager::kIsActive,
                       static_cast<int>(TemplateURLData::ActiveStatus::kTrue)),
       HasStringField(DefaultSearchManager::kFaviconURL,
-                     std::string(test_case.favicon)),
+                     test_case.favicon.value()),
       HasBooleanField(DefaultSearchManager::kSafeForAutoReplace, false),
       HasDoubleField(DefaultSearchManager::kDateCreated),
       HasDoubleField(DefaultSearchManager::kLastModified));
@@ -341,12 +344,20 @@
 
 testing::Matcher<const base::Value&> IsNonFeaturedSiteSearchEntry(
     TestProvider test_case) {
-  return IsSiteSearchEntry(test_case, /*featured=*/false);
+  return IsSiteSearchEntry(test_case, /*featured=*/false,
+                           /*enforced_by_policy=*/true);
 }
 
 testing::Matcher<const base::Value&> IsFeaturedSiteSearchEntry(
     TestProvider test_case) {
-  return IsSiteSearchEntry(test_case, /*featured=*/true);
+  return IsSiteSearchEntry(test_case, /*featured=*/true,
+                           /*enforced_by_policy=*/true);
+}
+
+testing::Matcher<const base::Value&> IsOverridableNonFeaturedSiteSearchEntry(
+    TestProvider test_case) {
+  return IsSiteSearchEntry(test_case, /*featured=*/false,
+                           /*enforced_by_policy=*/false);
 }
 
 MATCHER_P(HasValidationError,
@@ -412,6 +423,94 @@
                   IsNonFeaturedSiteSearchEntry(kValidTestProviders[1])));
 }
 
+TEST(SiteSearchPolicyHandlerTest,
+     ValidSiteSearchEntriesWithAllowUserOverride_FeatureDisabled) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndDisableFeature(
+      omnibox::kEnableSiteSearchAllowUserOverridePolicy);
+
+  SiteSearchPolicyHandler handler(
+      policy::Schema::Wrap(policy::GetChromeSchemaData()));
+
+  policy::PolicyMap policies;
+  PolicyErrorMap errors;
+  PrefValueMap prefs;
+
+  base::Value::List policy_value;
+  policy_value.Append(GenerateSiteSearchPolicyEntry(
+      kValidTestProvidersWithAllowUserOverride[0]));
+  policy_value.Append(GenerateSiteSearchPolicyEntry(
+      kValidTestProvidersWithAllowUserOverride[1]));
+  policy_value.Append(GenerateSiteSearchPolicyEntry(
+      kValidTestProvidersWithAllowUserOverride[2]));
+
+  policies.Set(key::kSiteSearchSettings, policy::POLICY_LEVEL_MANDATORY,
+               policy::POLICY_SCOPE_USER, policy::POLICY_SOURCE_CLOUD,
+               base::Value(std::move(policy_value)), nullptr);
+
+  ASSERT_TRUE(handler.CheckPolicySettings(policies, &errors));
+  EXPECT_TRUE(errors.HasError(key::kSiteSearchSettings));
+
+  handler.ApplyPolicySettings(policies, &prefs);
+  base::Value* providers = nullptr;
+  ASSERT_TRUE(prefs.GetValue(
+      EnterpriseSearchManager::kSiteSearchSettingsPrefName, &providers));
+  ASSERT_NE(providers, nullptr);
+  ASSERT_TRUE(providers->is_list());
+  EXPECT_THAT(providers->GetList(),
+              ElementsAre(IsNonFeaturedSiteSearchEntry(
+                              kValidTestProvidersWithAllowUserOverride[0]),
+                          IsNonFeaturedSiteSearchEntry(
+                              kValidTestProvidersWithAllowUserOverride[1]),
+                          IsNonFeaturedSiteSearchEntry(
+                              kValidTestProvidersWithAllowUserOverride[2])));
+}
+
+TEST(SiteSearchPolicyHandlerTest,
+     ValidSiteSearchEntriesWithAllowUserOverride_FeatureEnabled) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      omnibox::kEnableSiteSearchAllowUserOverridePolicy);
+
+  SiteSearchPolicyHandler handler(
+      policy::Schema::Wrap(policy::GetChromeSchemaData()));
+
+  policy::PolicyMap policies;
+  PolicyErrorMap errors;
+  PrefValueMap prefs;
+
+  base::Value::List policy_value;
+  policy_value.Append(GenerateSiteSearchPolicyEntry(
+      kValidTestProvidersWithAllowUserOverride[0]));
+  policy_value.Append(GenerateSiteSearchPolicyEntry(
+      kValidTestProvidersWithAllowUserOverride[1]));
+  policy_value.Append(GenerateSiteSearchPolicyEntry(
+      kValidTestProvidersWithAllowUserOverride[2]));
+
+  policies.Set(key::kSiteSearchSettings, policy::POLICY_LEVEL_MANDATORY,
+               policy::POLICY_SCOPE_USER, policy::POLICY_SOURCE_CLOUD,
+               base::Value(std::move(policy_value)), nullptr);
+
+  // TODO(crbug.com/417479042): Remove error expectation once policy YAML
+  // includes new `allow_user_override` field.
+  ASSERT_TRUE(handler.CheckPolicySettings(policies, &errors));
+  EXPECT_TRUE(errors.HasError(key::kSiteSearchSettings));
+
+  handler.ApplyPolicySettings(policies, &prefs);
+  base::Value* providers = nullptr;
+  ASSERT_TRUE(prefs.GetValue(
+      EnterpriseSearchManager::kSiteSearchSettingsPrefName, &providers));
+  ASSERT_NE(providers, nullptr);
+  ASSERT_TRUE(providers->is_list());
+  EXPECT_THAT(providers->GetList(),
+              ElementsAre(IsOverridableNonFeaturedSiteSearchEntry(
+                              kValidTestProvidersWithAllowUserOverride[0]),
+                          IsNonFeaturedSiteSearchEntry(
+                              kValidTestProvidersWithAllowUserOverride[1]),
+                          IsNonFeaturedSiteSearchEntry(
+                              kValidTestProvidersWithAllowUserOverride[2])));
+}
+
 TEST(SiteSearchPolicyHandlerTest, InvalidFormat) {
   SiteSearchPolicyHandler handler(
       policy::Schema::Wrap(policy::GetChromeSchemaData()));
@@ -650,10 +749,9 @@
   ASSERT_FALSE(handler.CheckPolicySettings(policies, &errors));
   for (auto* it = std::begin(kShortcutWithSpacesTestProviders);
        it != std::end(kShortcutWithSpacesTestProviders); ++it) {
-    EXPECT_THAT(&errors,
-                HasValidationError(l10n_util::GetStringFUTF16(
-                    IDS_SEARCH_POLICY_SETTINGS_SHORTCUT_CONTAINS_SPACE,
-                    base::UTF8ToUTF16(it->shortcut))));
+    EXPECT_THAT(&errors, HasValidationError(l10n_util::GetStringFUTF16(
+                             IDS_SEARCH_POLICY_SETTINGS_SHORTCUT_CONTAINS_SPACE,
+                             base::UTF8ToUTF16(it->shortcut.value()))));
   }
 }
 
@@ -680,7 +778,8 @@
       &errors,
       HasValidationError(l10n_util::GetStringFUTF16(
           IDS_SEARCH_POLICY_SETTINGS_SHORTCUT_STARTS_WITH_AT,
-          base::UTF8ToUTF16(kShortcutStartsWithAtTestProviders[0].shortcut))));
+          base::UTF8ToUTF16(
+              kShortcutStartsWithAtTestProviders[0].shortcut.value()))));
 
   handler.ApplyPolicySettings(policies, &prefs);
   base::Value* providers = nullptr;
@@ -714,11 +813,10 @@
   ASSERT_FALSE(handler.CheckPolicySettings(policies, &errors));
   for (auto* it = std::begin(kInvalidUrlTestProviders);
        it != std::end(kInvalidUrlTestProviders); ++it) {
-    EXPECT_THAT(
-        &errors,
-        HasValidationError(l10n_util::GetStringFUTF16(
-            IDS_SEARCH_POLICY_SETTINGS_URL_DOESNT_SUPPORT_REPLACEMENT,
-            base::UTF8ToUTF16(it->url))));
+    EXPECT_THAT(&errors,
+                HasValidationError(l10n_util::GetStringFUTF16(
+                    IDS_SEARCH_POLICY_SETTINGS_URL_DOESNT_SUPPORT_REPLACEMENT,
+                    base::UTF8ToUTF16(it->url.value()))));
   }
 }
 
@@ -824,11 +922,12 @@
                base::Value(std::move(policy_value)), nullptr);
 
   ASSERT_TRUE(handler.CheckPolicySettings(policies, &errors));
-  EXPECT_THAT(&errors,
-              HasValidationError(l10n_util::GetStringFUTF16(
-                  IDS_SEARCH_POLICY_SETTINGS_SHORTCUT_EQUALS_DSP_KEYWORD,
-                  base::UTF8ToUTF16(
-                      kShortcutSameAsDSPKeywordTestProviders[0].shortcut))));
+  EXPECT_THAT(
+      &errors,
+      HasValidationError(l10n_util::GetStringFUTF16(
+          IDS_SEARCH_POLICY_SETTINGS_SHORTCUT_EQUALS_DSP_KEYWORD,
+          base::UTF8ToUTF16(
+              kShortcutSameAsDSPKeywordTestProviders[0].shortcut.value()))));
 
   handler.ApplyPolicySettings(policies, &prefs);
   base::Value* providers = nullptr;
@@ -858,10 +957,11 @@
                base::Value(std::move(policy_value)), nullptr);
 
   ASSERT_TRUE(handler.CheckPolicySettings(policies, &errors));
-  EXPECT_THAT(&errors,
-              HasValidationError(l10n_util::GetStringFUTF16(
-                  IDS_SEARCH_POLICY_SETTINGS_URL_NOT_HTTPS,
-                  base::UTF8ToUTF16(kNonHttpsUrlTestProviders[0].url))));
+  EXPECT_THAT(
+      &errors,
+      HasValidationError(l10n_util::GetStringFUTF16(
+          IDS_SEARCH_POLICY_SETTINGS_URL_NOT_HTTPS,
+          base::UTF8ToUTF16(kNonHttpsUrlTestProviders[0].url.value()))));
 
   handler.ApplyPolicySettings(policies, &prefs);
   base::Value* providers = nullptr;
@@ -964,7 +1064,7 @@
       HasValidationError(l10n_util::GetStringFUTF16(
           IDS_POLICY_SITE_SEARCH_SETTINGS_SHORTCUT_EQUALS_SEARCH_AGGREGATOR_KEYWORD,
           base::UTF8ToUTF16(
-              kSiteSearchShortcutSameAsSearchAggregator.shortcut))));
+              kSiteSearchShortcutSameAsSearchAggregator.shortcut.value()))));
 }
 
 TEST(SiteSearchPolicyHandlerTest,
diff --git a/components/security_interstitials/content/BUILD.gn b/components/security_interstitials/content/BUILD.gn
index 11b8e2f..57ed6d98 100644
--- a/components/security_interstitials/content/BUILD.gn
+++ b/components/security_interstitials/content/BUILD.gn
@@ -93,7 +93,6 @@
     "//content/public/browser",
     "//content/public/common",
     "//crypto",
-    "//services/cert_verifier/public/mojom",
     "//services/network/public/cpp:cpp",
     "//third_party/protobuf:protobuf_lite",
     "//third_party/re2",
diff --git a/components/segmentation_platform/embedder/home_modules/constants.h b/components/segmentation_platform/embedder/home_modules/constants.h
index cfa8fd8..4707fab5 100644
--- a/components/segmentation_platform/embedder/home_modules/constants.h
+++ b/components/segmentation_platform/embedder/home_modules/constants.h
@@ -38,6 +38,7 @@
     "send_tab_infobar_received_in_last_session";
 
 // Input Context keys for emphemeral android modules.
+const char kIsUserSignedIn[] = "is_user_signed_in";
 const char kShouldShowNonRoleManagerDefaultBrowserPromo[] =
     "should_show_non_role_manager_default_browser_promo";
 const char kHasDefaultBrowserPromoShownInOtherSurface[] =
diff --git a/components/segmentation_platform/embedder/home_modules/default_browser_promo.cc b/components/segmentation_platform/embedder/home_modules/default_browser_promo.cc
index a5a18db..962dd625 100644
--- a/components/segmentation_platform/embedder/home_modules/default_browser_promo.cc
+++ b/components/segmentation_platform/embedder/home_modules/default_browser_promo.cc
@@ -24,6 +24,11 @@
            .tensor_length = 1,
            .fill_policy = proto::CustomInput::FILL_FROM_INPUT_CONTEXT,
            .name = kHasDefaultBrowserPromoShownInOtherSurface})},
+      {kIsUserSignedIn,
+       FeatureQuery::FromCustomInput(MetadataWriter::CustomInput{
+           .tensor_length = 1,
+           .fill_policy = proto::CustomInput::FILL_FROM_INPUT_CONTEXT,
+           .name = kIsUserSignedIn})},
       {kShouldShowNonRoleManagerDefaultBrowserPromo,
        FeatureQuery::FromCustomInput(MetadataWriter::CustomInput{
            .tensor_length = 1,
@@ -54,18 +59,22 @@
     return result;
   }
 
+  std::optional<float> resultForIsUserSignedIn =
+      signals.GetSignal(kIsUserSignedIn);
   std::optional<float> resultForShouldShowNonRoleManagerDefaultBrowserPromo =
       signals.GetSignal(kShouldShowNonRoleManagerDefaultBrowserPromo);
   std::optional<float> resultForHasDefaultBrowserPromoShownInOtherSurface =
       signals.GetSignal(kHasDefaultBrowserPromoShownInOtherSurface);
 
-  if (!resultForShouldShowNonRoleManagerDefaultBrowserPromo.has_value() ||
+  if (!resultForIsUserSignedIn.has_value() ||
+      !resultForShouldShowNonRoleManagerDefaultBrowserPromo.has_value() ||
       !resultForHasDefaultBrowserPromoShownInOtherSurface.has_value()) {
     result.position = EphemeralHomeModuleRank::kNotShown;
     return result;
   }
 
-  if (*resultForShouldShowNonRoleManagerDefaultBrowserPromo &&
+  if (*resultForIsUserSignedIn &&
+      *resultForShouldShowNonRoleManagerDefaultBrowserPromo &&
       !*resultForHasDefaultBrowserPromoShownInOtherSurface) {
     result.position = EphemeralHomeModuleRank::kLast;
     return result;
diff --git a/components/segmentation_platform/embedder/home_modules/default_browser_promo_unittest.cc b/components/segmentation_platform/embedder/home_modules/default_browser_promo_unittest.cc
index e62e20e..ad202cf12 100644
--- a/components/segmentation_platform/embedder/home_modules/default_browser_promo_unittest.cc
+++ b/components/segmentation_platform/embedder/home_modules/default_browser_promo_unittest.cc
@@ -29,13 +29,14 @@
       bool hasDefaultBrowserPromoInteracted,
       float hasDefaultBrowserPromoShownInOtherSurface,
       float shouldShowNonRoleManagerDefaultBrowserPromo,
+      float isUserSignedIn,
       EphemeralHomeModuleRank position) {
     pref_service_.SetUserPref(
         kDefaultBrowserPromoInteractedPref,
         std::make_unique<base::Value>(hasDefaultBrowserPromoInteracted));
     auto card = std::make_unique<DefaultBrowserPromo>(&pref_service_);
     AllCardSignals all_signals = CreateAllCardSignals(
-        card.get(), {hasDefaultBrowserPromoShownInOtherSurface,
+        card.get(), {hasDefaultBrowserPromoShownInOtherSurface, isUserSignedIn,
                      shouldShowNonRoleManagerDefaultBrowserPromo});
     CardSelectionSignals card_signal(&all_signals, kDefaultBrowserPromo);
     CardSelectionInfo::ShowResult result = card->ComputeCardResult(card_signal);
@@ -50,7 +51,7 @@
 TEST_F(DefaultBrowserPromoTest, GetInputsReturnsExpectedInputs) {
   auto card = std::make_unique<DefaultBrowserPromo>(&pref_service_);
   std::map<SignalKey, FeatureQuery> inputs = card->GetInputs();
-  EXPECT_EQ(inputs.size(), 2u);
+  EXPECT_EQ(inputs.size(), 3u);
   // Verify that the inputs map contains the expected keys.
   EXPECT_NE(
       inputs.find(
@@ -60,6 +61,7 @@
       inputs.find(
           segmentation_platform::kHasDefaultBrowserPromoShownInOtherSurface),
       inputs.end());
+  EXPECT_NE(inputs.find(segmentation_platform::kIsUserSignedIn), inputs.end());
 }
 
 // Validates that ComputeCardResult() returns kLast when default browser promo
@@ -68,6 +70,7 @@
   TestComputeCardResultImpl(/* hasDefaultBrowserPromoInteracted */ false,
                             /* hasDefaultBrowserPromoShownInOtherSurface */ 0,
                             /* shouldShowNonRoleManagerDefaultBrowserPromo */ 1,
+                            /* isUserSignedIn */ 1,
                             EphemeralHomeModuleRank::kLast);
 }
 
@@ -79,6 +82,7 @@
   TestComputeCardResultImpl(/* hasDefaultBrowserPromoInteracted */ false,
                             /* hasDefaultBrowserPromoShownInOtherSurface */ 0,
                             /* shouldShowNonRoleManagerDefaultBrowserPromo */ 0,
+                            /* isUserSignedIn */ 1,
                             EphemeralHomeModuleRank::kNotShown);
 }
 
@@ -90,6 +94,7 @@
   TestComputeCardResultImpl(/* hasDefaultBrowserPromoInteracted */ false,
                             /* hasDefaultBrowserPromoShownInOtherSurface */ 1,
                             /* shouldShowNonRoleManagerDefaultBrowserPromo */ 1,
+                            /* isUserSignedIn */ 1,
                             EphemeralHomeModuleRank::kNotShown);
 }
 
@@ -101,6 +106,18 @@
   TestComputeCardResultImpl(/* hasDefaultBrowserPromoInteracted */ true,
                             /* hasDefaultBrowserPromoShownInOtherSurface */ 0,
                             /* shouldShowNonRoleManagerDefaultBrowserPromo */ 1,
+                            /* isUserSignedIn */ 1,
+                            EphemeralHomeModuleRank::kNotShown);
+}
+
+// Validates that the ComputeCardResult() function returns kNotShown when the
+// default browser promo card is disabled because the user has not signed in.
+TEST_F(DefaultBrowserPromoTest,
+       TestComputeCardResultWithCardDisabledForUserNotSignedIn) {
+  TestComputeCardResultImpl(/* hasDefaultBrowserPromoInteracted */ false,
+                            /* hasDefaultBrowserPromoShownInOtherSurface */ 0,
+                            /* shouldShowNonRoleManagerDefaultBrowserPromo */ 1,
+                            /* isUserSignedIn */ 0,
                             EphemeralHomeModuleRank::kNotShown);
 }
 
diff --git a/components/segmentation_platform/embedder/home_modules/home_modules_card_registry_unittest.cc b/components/segmentation_platform/embedder/home_modules/home_modules_card_registry_unittest.cc
index c34730d..f76dbea8f 100644
--- a/components/segmentation_platform/embedder/home_modules/home_modules_card_registry_unittest.cc
+++ b/components/segmentation_platform/embedder/home_modules/home_modules_card_registry_unittest.cc
@@ -134,7 +134,7 @@
   registry_ = std::make_unique<HomeModulesCardRegistry>(&pref_service_);
 
   EXPECT_THAT(registry_->all_output_labels(), Contains(kDefaultBrowserPromo));
-  EXPECT_GE(registry_->all_cards_input_size(), 2u);
+  EXPECT_GE(registry_->all_cards_input_size(), 3u);
   const std::vector<std::unique_ptr<CardSelectionInfo>>& all_cards =
       registry_->get_all_cards_by_priority();
   std::vector<std::string> card_names = ExtractCardNames(all_cards);
@@ -147,6 +147,7 @@
               Contains("should_show_non_role_manager_default_browser_promo"));
   EXPECT_THAT(signalKeys,
               Contains("has_default_browser_promo_shown_in_other_surface"));
+  EXPECT_THAT(signalKeys, Contains("is_user_signed_in"));
 }
 
 // Tests that the Registry won't register the DefaultBrowserPromo card when it
@@ -174,6 +175,7 @@
   EXPECT_THAT(
       signalKeys,
       Not(Contains("has_default_browser_promo_shown_in_other_surface")));
+  EXPECT_THAT(signalKeys, Not(Contains("is_user_signed_in")));
 }
 
 // Tests that the Registry registers the TabGroupPromo card when its feature is
@@ -183,7 +185,7 @@
   registry_ = std::make_unique<HomeModulesCardRegistry>(&pref_service_);
 
   EXPECT_THAT(registry_->all_output_labels(), Contains(kTabGroupPromo));
-  EXPECT_GE(registry_->all_cards_input_size(), 5u);
+  EXPECT_GE(registry_->all_cards_input_size(), 4u);
   const std::vector<std::unique_ptr<CardSelectionInfo>>& all_cards =
       registry_->get_all_cards_by_priority();
   std::vector<std::string> card_names = ExtractCardNames(all_cards);
@@ -195,6 +197,7 @@
   EXPECT_THAT(signalKeys, Contains("tab_group_exists"));
   EXPECT_THAT(signalKeys, Contains("number_of_tabs"));
   EXPECT_THAT(signalKeys, Contains("tab_group_shown_count"));
+  EXPECT_THAT(signalKeys, Contains("is_user_signed_in"));
 }
 
 // Tests that the Registry won't register the TabGroupPromo card when it is
@@ -218,6 +221,7 @@
   EXPECT_THAT(signalKeys, Not(Contains("tab_group_exists")));
   EXPECT_THAT(signalKeys, Not(Contains("number_of_tabs")));
   EXPECT_THAT(signalKeys, Not(Contains("tab_group_shown_count")));
+  EXPECT_THAT(signalKeys, Not(Contains("is_user_signed_in")));
 }
 
 // Tests that for educational tip cards, except for the default browser promo
@@ -241,7 +245,7 @@
   registry_ = std::make_unique<HomeModulesCardRegistry>(&pref_service_);
 
   EXPECT_THAT(registry_->all_output_labels(), Contains(kTabGroupSyncPromo));
-  EXPECT_GE(registry_->all_cards_input_size(), 7u);
+  EXPECT_GE(registry_->all_cards_input_size(), 2u);
   const std::vector<std::unique_ptr<CardSelectionInfo>>& all_cards =
       registry_->get_all_cards_by_priority();
   std::vector<std::string> card_names = ExtractCardNames(all_cards);
@@ -284,7 +288,7 @@
   registry_ = std::make_unique<HomeModulesCardRegistry>(&pref_service_);
 
   EXPECT_THAT(registry_->all_output_labels(), Contains(kQuickDeletePromo));
-  EXPECT_GE(registry_->all_cards_input_size(), 10u);
+  EXPECT_GE(registry_->all_cards_input_size(), 4u);
   const std::vector<std::unique_ptr<CardSelectionInfo>>& all_cards =
       registry_->get_all_cards_by_priority();
   std::vector<std::string> card_names = ExtractCardNames(all_cards);
@@ -297,6 +301,7 @@
   EXPECT_THAT(signalKeys,
               Contains("count_of_clearing_browsing_data_through_quick_delete"));
   EXPECT_THAT(signalKeys, Contains("quick_delete_shown_count"));
+  EXPECT_THAT(signalKeys, Contains("is_user_signed_in"));
 }
 
 // Tests that the Registry won't register the QuickDeletePromo card when it is
@@ -322,6 +327,7 @@
       signalKeys,
       Not(Contains("count_of_clearing_browsing_data_through_quick_delete")));
   EXPECT_THAT(signalKeys, Not(Contains("quick_delete_shown_count")));
+  EXPECT_THAT(signalKeys, Not(Contains("is_user_signed_in")));
 }
 
 // Tests that the Registry registers the AuxiliarySearchPromo card when its
@@ -373,7 +379,7 @@
   registry_ = std::make_unique<HomeModulesCardRegistry>(&pref_service_);
 
   EXPECT_THAT(registry_->all_output_labels(), Contains(kHistorySyncPromo));
-  EXPECT_GE(registry_->all_cards_input_size(), 12u);
+  EXPECT_GE(registry_->all_cards_input_size(), 2u);
   const std::vector<std::unique_ptr<CardSelectionInfo>>& all_cards =
       registry_->get_all_cards_by_priority();
   std::vector<std::string> card_names = ExtractCardNames(all_cards);
diff --git a/components/segmentation_platform/embedder/home_modules/quick_delete_promo.cc b/components/segmentation_platform/embedder/home_modules/quick_delete_promo.cc
index 24ab44e..272f35a 100644
--- a/components/segmentation_platform/embedder/home_modules/quick_delete_promo.cc
+++ b/components/segmentation_platform/embedder/home_modules/quick_delete_promo.cc
@@ -40,7 +40,12 @@
     : CardSelectionInfo(kQuickDeletePromo), profile_prefs_(profile_prefs) {}
 
 std::map<SignalKey, FeatureQuery> QuickDeletePromo::GetInputs() {
-  std::map<SignalKey, FeatureQuery> map;
+  std::map<SignalKey, FeatureQuery> map = {
+      {kIsUserSignedIn,
+       FeatureQuery::FromCustomInput(MetadataWriter::CustomInput{
+           .tensor_length = 1,
+           .fill_policy = proto::CustomInput::FILL_FROM_INPUT_CONTEXT,
+           .name = kIsUserSignedIn})}};
 
   // Define the number of times user cleared browsing data in the past 30 days.
   DEFINE_UMA_FEATURE_ENUM_COUNT(countOfClearBrowsingData,
@@ -95,6 +100,8 @@
     return result;
   }
 
+  std::optional<float> resultForIsUserSignedIn =
+      signals.GetSignal(kIsUserSignedIn);
   std::optional<float> resultForCountOfClearingBrowsingData =
       signals.GetSignal(kCountOfClearingBrowsingData);
   std::optional<float> resultForCountOfClearingBrowsingDataThroughQuickDelete =
@@ -102,7 +109,8 @@
   std::optional<float> resultForQuickDeletePromoShownCount =
       signals.GetSignal(kQuickDeletePromoShownCount);
 
-  if (!resultForCountOfClearingBrowsingData.has_value() ||
+  if (!resultForIsUserSignedIn.has_value() ||
+      !resultForCountOfClearingBrowsingData.has_value() ||
       !resultForCountOfClearingBrowsingDataThroughQuickDelete.has_value() ||
       !resultForQuickDeletePromoShownCount.has_value()) {
     result.position = EphemeralHomeModuleRank::kNotShown;
@@ -112,7 +120,8 @@
   // Show the promo card if the user has never cleared browsing data or has
   // cleared browsing data but never through quick delete in the past 30 days
   // and the promo card has not been shown more than 3 times in 24 hours.
-  if ((resultForCountOfClearingBrowsingData.value() == 0 ||
+  if (*resultForIsUserSignedIn &&
+      (resultForCountOfClearingBrowsingData.value() == 0 ||
        (resultForCountOfClearingBrowsingData.value() > 0 &&
         resultForCountOfClearingBrowsingDataThroughQuickDelete.value() == 0)) &&
       resultForQuickDeletePromoShownCount.value() < kShownCountLimit) {
diff --git a/components/segmentation_platform/embedder/home_modules/quick_delete_promo_unittest.cc b/components/segmentation_platform/embedder/home_modules/quick_delete_promo_unittest.cc
index 2bbbebfa..568383e 100644
--- a/components/segmentation_platform/embedder/home_modules/quick_delete_promo_unittest.cc
+++ b/components/segmentation_platform/embedder/home_modules/quick_delete_promo_unittest.cc
@@ -30,6 +30,7 @@
       float countOfClearingBrowsingData,
       float countOfClearingBrowsingDataThroughQuickDelete,
       float quickDeletePromoShownCount,
+      float isUserSignedIn,
       EphemeralHomeModuleRank position) {
     pref_service_.SetUserPref(
         kQuickDeletePromoInteractedPref,
@@ -38,7 +39,7 @@
     AllCardSignals all_signals = CreateAllCardSignals(
         card.get(), {countOfClearingBrowsingData,
                      countOfClearingBrowsingDataThroughQuickDelete,
-                     quickDeletePromoShownCount});
+                     isUserSignedIn, quickDeletePromoShownCount});
     CardSelectionSignals card_signal(&all_signals, kQuickDeletePromo);
     CardSelectionInfo::ShowResult result = card->ComputeCardResult(card_signal);
     EXPECT_EQ(position, result.position);
@@ -52,7 +53,7 @@
 TEST_F(QuickDeletePromoTest, GetInputsReturnsExpectedInputs) {
   auto card = std::make_unique<QuickDeletePromo>(&pref_service_);
   std::map<SignalKey, FeatureQuery> inputs = card->GetInputs();
-  EXPECT_EQ(inputs.size(), 3u);
+  EXPECT_EQ(inputs.size(), 4u);
   // Verify that the inputs map contains the expected keys.
   EXPECT_NE(inputs.find(segmentation_platform::kCountOfClearingBrowsingData),
             inputs.end());
@@ -61,6 +62,7 @@
             inputs.end());
   EXPECT_NE(inputs.find(segmentation_platform::kQuickDeletePromoShownCount),
             inputs.end());
+  EXPECT_NE(inputs.find(segmentation_platform::kIsUserSignedIn), inputs.end());
 }
 
 // Validates that ComputeCardResult() returns kLast when quick delete promo card
@@ -72,7 +74,8 @@
       /* hasQuickDeletePromoInteracted */ false,
       /* countOfClearingBrowsingData */ 0,
       /* countOfClearingBrowsingDataThroughQuickDelete */ 0,
-      /* quickDeletePromoShownCount */ 0, EphemeralHomeModuleRank::kLast);
+      /* quickDeletePromoShownCount */ 0,
+      /* isUserSignedIn */ 1, EphemeralHomeModuleRank::kLast);
 }
 
 // Validates that ComputeCardResult() returns kLast when quick delete promo card
@@ -84,7 +87,8 @@
       /* hasQuickDeletePromoInteracted */ false,
       /* countOfClearingBrowsingData */ 5,
       /* countOfClearingBrowsingDataThroughQuickDelete */ 0,
-      /* quickDeletePromoShownCount */ 0, EphemeralHomeModuleRank::kLast);
+      /* quickDeletePromoShownCount */ 0, /* isUserSignedIn */ 1,
+      EphemeralHomeModuleRank::kLast);
 }
 
 // Validates that when the quick delete promo card is disabled because the user
@@ -96,7 +100,8 @@
       /* hasQuickDeletePromoInteracted */ false,
       /* countOfClearingBrowsingData */ 5,
       /* countOfClearingBrowsingDataThroughQuickDelete */ 3,
-      /* quickDeletePromoShownCount */ 0, EphemeralHomeModuleRank::kNotShown);
+      /* quickDeletePromoShownCount */ 0, /* isUserSignedIn */ 1,
+      EphemeralHomeModuleRank::kNotShown);
 }
 
 // Validates that the ComputeCardResult() function returns kNotShown when the
@@ -108,7 +113,8 @@
       /* hasQuickDeletePromoInteracted */ false,
       /* countOfClearingBrowsingData */ 5,
       /* countOfClearingBrowsingDataThroughQuickDelete */ 0,
-      /* quickDeletePromoShownCount */ 3, EphemeralHomeModuleRank::kNotShown);
+      /* quickDeletePromoShownCount */ 3, /* isUserSignedIn */ 1,
+      EphemeralHomeModuleRank::kNotShown);
 }
 
 // Validates that the ComputeCardResult() function returns kNotShown when the
@@ -120,7 +126,20 @@
       /* hasQuickDeletePromoInteracted */ true,
       /* countOfClearingBrowsingData */ 5,
       /* countOfClearingBrowsingDataThroughQuickDelete */ 0,
-      /* quickDeletePromoShownCount */ 0, EphemeralHomeModuleRank::kNotShown);
+      /* quickDeletePromoShownCount */ 0, /* isUserSignedIn */ 1,
+      EphemeralHomeModuleRank::kNotShown);
+}
+
+// Validates that the ComputeCardResult() function returns kNotShown when the
+// quick delete promo card is disabled because the user is not signed in.
+TEST_F(QuickDeletePromoTest,
+       TestComputeCardResultWithCardDisabledForUserNotSignedIn) {
+  TestComputeCardResultImpl(
+      /* hasQuickDeletePromoInteracted */ false,
+      /* countOfClearingBrowsingData */ 0,
+      /* countOfClearingBrowsingDataThroughQuickDelete */ 0,
+      /* quickDeletePromoShownCount */ 0, /* isUserSignedIn */ 0,
+      EphemeralHomeModuleRank::kNotShown);
 }
 
 }  // namespace segmentation_platform::home_modules
diff --git a/components/segmentation_platform/embedder/home_modules/tab_group_promo.cc b/components/segmentation_platform/embedder/home_modules/tab_group_promo.cc
index 2cda765..ec0ae24 100644
--- a/components/segmentation_platform/embedder/home_modules/tab_group_promo.cc
+++ b/components/segmentation_platform/embedder/home_modules/tab_group_promo.cc
@@ -38,6 +38,11 @@
 
 std::map<SignalKey, FeatureQuery> TabGroupPromo::GetInputs() {
   std::map<SignalKey, FeatureQuery> map = {
+      {kIsUserSignedIn,
+       FeatureQuery::FromCustomInput(MetadataWriter::CustomInput{
+           .tensor_length = 1,
+           .fill_policy = proto::CustomInput::FILL_FROM_INPUT_CONTEXT,
+           .name = kIsUserSignedIn})},
       {kNumberOfTabs,
        FeatureQuery::FromCustomInput(MetadataWriter::CustomInput{
            .tensor_length = 1,
@@ -80,20 +85,23 @@
     return result;
   }
 
+  std::optional<float> resultForIsUserSignedIn =
+      signals.GetSignal(kIsUserSignedIn);
   std::optional<float> resultForTabGroupExists =
       signals.GetSignal(kTabGroupExists);
   std::optional<float> resultForNumberOfTabs = signals.GetSignal(kNumberOfTabs);
   std::optional<float> resultForTabGroupPromoShownCount =
       signals.GetSignal(kTabGroupPromoShownCount);
 
-  if (!resultForTabGroupExists.has_value() ||
+  if (!resultForIsUserSignedIn.has_value() ||
+      !resultForTabGroupExists.has_value() ||
       !resultForNumberOfTabs.has_value() ||
       !resultForTabGroupPromoShownCount.has_value()) {
     result.position = EphemeralHomeModuleRank::kNotShown;
     return result;
   }
 
-  if (!*resultForTabGroupExists &&
+  if (*resultForIsUserSignedIn && !*resultForTabGroupExists &&
       resultForNumberOfTabs.value() > kTabCountLimit &&
       resultForTabGroupPromoShownCount.value() < kShownCountLimit) {
     result.position = EphemeralHomeModuleRank::kLast;
diff --git a/components/segmentation_platform/embedder/home_modules/tab_group_promo_unittest.cc b/components/segmentation_platform/embedder/home_modules/tab_group_promo_unittest.cc
index 7bbd86ec..5a473cd 100644
--- a/components/segmentation_platform/embedder/home_modules/tab_group_promo_unittest.cc
+++ b/components/segmentation_platform/embedder/home_modules/tab_group_promo_unittest.cc
@@ -29,13 +29,15 @@
                                  float numberOfTabs,
                                  float tabGroupExists,
                                  float tabGroupPromoShownCount,
+                                 float isUserSignedIn,
                                  EphemeralHomeModuleRank position) {
     pref_service_.SetUserPref(
         kTabGroupPromoInteractedPref,
         std::make_unique<base::Value>(hasTabGroupPromoInteracted));
     auto card = std::make_unique<TabGroupPromo>(&pref_service_);
     AllCardSignals all_signals = CreateAllCardSignals(
-        card.get(), {numberOfTabs, tabGroupExists, tabGroupPromoShownCount});
+        card.get(), {isUserSignedIn, numberOfTabs, tabGroupExists,
+                     tabGroupPromoShownCount});
     CardSelectionSignals card_signal(&all_signals, kTabGroupPromo);
     CardSelectionInfo::ShowResult result = card->ComputeCardResult(card_signal);
     EXPECT_EQ(position, result.position);
@@ -49,12 +51,13 @@
 TEST_F(TabGroupPromoTest, GetInputsReturnsExpectedInputs) {
   auto card = std::make_unique<TabGroupPromo>(&pref_service_);
   std::map<SignalKey, FeatureQuery> inputs = card->GetInputs();
-  EXPECT_EQ(inputs.size(), 3u);
+  EXPECT_EQ(inputs.size(), 4u);
   // Verify that the inputs map contains the expected keys.
   EXPECT_NE(inputs.find(segmentation_platform::kTabGroupExists), inputs.end());
   EXPECT_NE(inputs.find(segmentation_platform::kNumberOfTabs), inputs.end());
   EXPECT_NE(inputs.find(segmentation_platform::kTabGroupPromoShownCount),
             inputs.end());
+  EXPECT_NE(inputs.find(segmentation_platform::kIsUserSignedIn), inputs.end());
 }
 
 // Validates that ComputeCardResult() returns kLast when tab group promo
@@ -64,6 +67,7 @@
                             /* numberOfTabs */ 11,
                             /* tabGroupExists */ 0,
                             /* tabGroupPromoShownCount */ 0,
+                            /* isUserSignedIn */ 1,
                             EphemeralHomeModuleRank::kLast);
 }
 
@@ -75,6 +79,7 @@
                             /* numberOfTabs */ 11,
                             /* tabGroupExists */ 1,
                             /* tabGroupPromoShownCount */ 0,
+                            /* isUserSignedIn */ 1,
                             EphemeralHomeModuleRank::kNotShown);
 }
 
@@ -86,6 +91,7 @@
                             /* numberOfTabs */ 10,
                             /* tabGroupExists */ 0,
                             /* tabGroupPromoShownCount */ 0,
+                            /* isUserSignedIn */ 1,
                             EphemeralHomeModuleRank::kNotShown);
 }
 
@@ -98,6 +104,7 @@
                             /* numberOfTabs */ 11,
                             /* tabGroupExists */ 0,
                             /* tabGroupPromoShownCount */ 3,
+                            /* isUserSignedIn */ 1,
                             EphemeralHomeModuleRank::kNotShown);
 }
 
@@ -110,6 +117,19 @@
                             /* numberOfTabs */ 11,
                             /* tabGroupExists */ 0,
                             /* tabGroupPromoShownCount */ 0,
+                            /* isUserSignedIn */ 1,
+                            EphemeralHomeModuleRank::kNotShown);
+}
+
+// Validates that the ComputeCardResult() function returns kNotShown when the
+// tab group promo card is disabled because the user is not signed in.
+TEST_F(TabGroupPromoTest,
+       TestComputeCardResultWithCardDisabledForUserNotSignedIn) {
+  TestComputeCardResultImpl(/* hasTabGroupPromoInteracted */ false,
+                            /* numberOfTabs */ 11,
+                            /* tabGroupExists */ 0,
+                            /* tabGroupPromoShownCount */ 0,
+                            /* isUserSignedIn */ 0,
                             EphemeralHomeModuleRank::kNotShown);
 }
 
diff --git a/components/segmentation_platform/public/BUILD.gn b/components/segmentation_platform/public/BUILD.gn
index a42cfc7..22a9a3c 100644
--- a/components/segmentation_platform/public/BUILD.gn
+++ b/components/segmentation_platform/public/BUILD.gn
@@ -144,6 +144,7 @@
     sources = [
       "android/java/src/org/chromium/components/segmentation_platform/InputContextTest.java",
       "android/java/src/org/chromium/components/segmentation_platform/PredictionOptionsTest.java",
+      "android/java/src/org/chromium/components/segmentation_platform/ProcessedValueTest.java",
     ]
 
     deps = [
diff --git a/components/segmentation_platform/public/android/java/src/org/chromium/components/segmentation_platform/InputContext.java b/components/segmentation_platform/public/android/java/src/org/chromium/components/segmentation_platform/InputContext.java
index aa1ee68..8158b16 100644
--- a/components/segmentation_platform/public/android/java/src/org/chromium/components/segmentation_platform/InputContext.java
+++ b/components/segmentation_platform/public/android/java/src/org/chromium/components/segmentation_platform/InputContext.java
@@ -4,6 +4,8 @@
 
 package org.chromium.components.segmentation_platform;
 
+import static org.chromium.build.NullUtil.assumeNonNull;
+
 import androidx.annotation.VisibleForTesting;
 
 import org.jni_zero.CalledByNative;
@@ -191,12 +193,19 @@
         if (other == null) return;
 
         for (Entry<String, ProcessedValue> entry : other.mMetadata.entrySet()) {
-            assert !mMetadata.containsKey(entry.getKey());
-            mMetadata.put(entry.getKey(), entry.getValue());
+            String key = entry.getKey();
+            ProcessedValue value = entry.getValue();
+
+            if (!mMetadata.containsKey(key)) {
+                mMetadata.put(key, value);
+            } else {
+                assert assumeNonNull(getEntryValue(key)).equals(value);
+            }
         }
     }
 
-    public @Nullable ProcessedValue getEntryForTesting(String key) {
+    @VisibleForTesting
+    public @Nullable ProcessedValue getEntryValue(String key) {
         return mMetadata.get(key);
     }
 
diff --git a/components/segmentation_platform/public/android/java/src/org/chromium/components/segmentation_platform/InputContextTest.java b/components/segmentation_platform/public/android/java/src/org/chromium/components/segmentation_platform/InputContextTest.java
index 52798a7..12b50789 100644
--- a/components/segmentation_platform/public/android/java/src/org/chromium/components/segmentation_platform/InputContextTest.java
+++ b/components/segmentation_platform/public/android/java/src/org/chromium/components/segmentation_platform/InputContextTest.java
@@ -179,17 +179,18 @@
         another.addEntry("second_int_param", ProcessedValue.fromInt(21));
         another.addEntry("time_param", ProcessedValue.fromTimeMillis(100));
         another.addEntry("third_float_param", ProcessedValue.fromFloat(100));
+        inputContext.addEntry("int_param", ProcessedValue.fromInt(1));
 
         inputContext.mergeFrom(another);
 
         assertEquals(4, another.getSizeForTesting());
 
         assertEquals(7, inputContext.getSizeForTesting());
-        assertEquals(1, inputContext.getEntryForTesting("int_param").intValue);
-        assertEquals(21, inputContext.getEntryForTesting("second_int_param").intValue);
-        assertEquals(-4, inputContext.getEntryForTesting("negative_float_param").floatValue, 0.01);
-        assertEquals(2, inputContext.getEntryForTesting("second_float_param").floatValue, 0.01);
-        assertEquals(100, inputContext.getEntryForTesting("third_float_param").floatValue, 0.01);
-        assertEquals("StringValue", inputContext.getEntryForTesting("string_param").stringValue);
+        assertEquals(1, inputContext.getEntryValue("int_param").intValue);
+        assertEquals(21, inputContext.getEntryValue("second_int_param").intValue);
+        assertEquals(-4, inputContext.getEntryValue("negative_float_param").floatValue, 0.01);
+        assertEquals(2, inputContext.getEntryValue("second_float_param").floatValue, 0.01);
+        assertEquals(100, inputContext.getEntryValue("third_float_param").floatValue, 0.01);
+        assertEquals("StringValue", inputContext.getEntryValue("string_param").stringValue);
     }
 }
diff --git a/components/segmentation_platform/public/android/java/src/org/chromium/components/segmentation_platform/ProcessedValue.java b/components/segmentation_platform/public/android/java/src/org/chromium/components/segmentation_platform/ProcessedValue.java
index aa61104..1676643 100644
--- a/components/segmentation_platform/public/android/java/src/org/chromium/components/segmentation_platform/ProcessedValue.java
+++ b/components/segmentation_platform/public/android/java/src/org/chromium/components/segmentation_platform/ProcessedValue.java
@@ -4,11 +4,15 @@
 
 package org.chromium.components.segmentation_platform;
 
+import static org.chromium.build.NullUtil.assumeNonNull;
+
 import org.chromium.base.Log;
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
 import org.chromium.url.GURL;
 
+import java.util.Objects;
+
 /**
  * Represents a single value to be used as an input to a segmentation model. Its native equivalent
  * is found here: components/segmentation_platform/public/types/processed_value.h.
@@ -101,4 +105,60 @@
         }
         return processedValue;
     }
+
+    @Override
+    public final boolean equals(@Nullable Object other) {
+        if (other == this) return true;
+
+        if (!(other instanceof ProcessedValue)) return false;
+
+        if (type != ((ProcessedValue) other).type) {
+            return false;
+        }
+
+        switch (type) {
+            case ProcessedValueType.BOOL:
+                return booleanValue == ((ProcessedValue) other).booleanValue;
+            case ProcessedValueType.INT:
+                return intValue == ((ProcessedValue) other).intValue;
+            case ProcessedValueType.FLOAT:
+                return floatValue == ((ProcessedValue) other).floatValue;
+            case ProcessedValueType.DOUBLE:
+                return doubleValue == ((ProcessedValue) other).doubleValue;
+            case ProcessedValueType.STRING:
+                return assumeNonNull(stringValue).equals(((ProcessedValue) other).stringValue);
+            case ProcessedValueType.TIME:
+                return timeValue == ((ProcessedValue) other).timeValue;
+            case ProcessedValueType.INT64:
+                return int64Value == ((ProcessedValue) other).int64Value;
+            case ProcessedValueType.URL:
+                return assumeNonNull(urlValue).equals(((ProcessedValue) other).urlValue);
+            default:
+                throw new IllegalArgumentException("Processed value type not supported");
+        }
+    }
+
+    @Override
+    public final int hashCode() {
+        switch (type) {
+            case ProcessedValueType.BOOL:
+                return Objects.hash(type, booleanValue);
+            case ProcessedValueType.INT:
+                return Objects.hash(type, intValue);
+            case ProcessedValueType.FLOAT:
+                return Objects.hash(type, floatValue);
+            case ProcessedValueType.DOUBLE:
+                return Objects.hash(type, doubleValue);
+            case ProcessedValueType.STRING:
+                return Objects.hash(type, stringValue);
+            case ProcessedValueType.TIME:
+                return Objects.hash(type, timeValue);
+            case ProcessedValueType.INT64:
+                return Objects.hash(type, int64Value);
+            case ProcessedValueType.URL:
+                return Objects.hash(type, urlValue);
+            default:
+                throw new IllegalArgumentException("Processed value type not supported");
+        }
+    }
 }
diff --git a/components/segmentation_platform/public/android/java/src/org/chromium/components/segmentation_platform/ProcessedValueTest.java b/components/segmentation_platform/public/android/java/src/org/chromium/components/segmentation_platform/ProcessedValueTest.java
new file mode 100644
index 0000000..8e603cae
--- /dev/null
+++ b/components/segmentation_platform/public/android/java/src/org/chromium/components/segmentation_platform/ProcessedValueTest.java
@@ -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.
+
+package org.chromium.components.segmentation_platform;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.url.GURL;
+import org.chromium.url.JUnitTestGURLs;
+
+/** Tests for {@link ProcessedValue}. */
+@RunWith(BaseRobolectricTestRunner.class)
+public class ProcessedValueTest {
+    @Test
+    public void testEqualsAndHashCode_Boolean() {
+        ProcessedValue processedValue1 = ProcessedValue.fromBoolean(true);
+        ProcessedValue processedValue2 = ProcessedValue.fromBoolean(true);
+        ProcessedValue processedValue3 = ProcessedValue.fromBoolean(false);
+
+        testEqualsAndHashCodeImpl(processedValue1, processedValue2, processedValue3);
+    }
+
+    @Test
+    public void testEqualsAndHashCode_Int() {
+        ProcessedValue processedValue1 = ProcessedValue.fromInt(1);
+        ProcessedValue processedValue2 = ProcessedValue.fromInt(1);
+        ProcessedValue processedValue3 = ProcessedValue.fromInt(2);
+
+        testEqualsAndHashCodeImpl(processedValue1, processedValue2, processedValue3);
+    }
+
+    @Test
+    public void testEqualsAndHashCode_Float() {
+        ProcessedValue processedValue1 = ProcessedValue.fromFloat(1.0f);
+        ProcessedValue processedValue2 = ProcessedValue.fromFloat(1.0f);
+        ProcessedValue processedValue3 = ProcessedValue.fromFloat(2.0f);
+
+        testEqualsAndHashCodeImpl(processedValue1, processedValue2, processedValue3);
+    }
+
+    @Test
+    public void testEqualsAndHashCode_Double() {
+        ProcessedValue processedValue1 = ProcessedValue.fromDouble(1.0);
+        ProcessedValue processedValue2 = ProcessedValue.fromDouble(1.0);
+        ProcessedValue processedValue3 = ProcessedValue.fromDouble(2.0);
+
+        testEqualsAndHashCodeImpl(processedValue1, processedValue2, processedValue3);
+    }
+
+    @Test
+    public void testEqualsAndHashCode_String() {
+        ProcessedValue processedValue1 = ProcessedValue.fromString("hello");
+        ProcessedValue processedValue2 = ProcessedValue.fromString("hello");
+        ProcessedValue processedValue3 = ProcessedValue.fromString("world");
+        testEqualsAndHashCodeImpl(processedValue1, processedValue2, processedValue3);
+    }
+
+    @Test
+    public void testEqualsAndHashCode_TimeMillis() {
+        long time1 = 1000L;
+        long time2 = 2000L;
+        ProcessedValue processedValue1 = ProcessedValue.fromTimeMillis(time1);
+        ProcessedValue processedValue2 = ProcessedValue.fromTimeMillis(time1);
+        ProcessedValue processedValue3 = ProcessedValue.fromTimeMillis(time2);
+
+        testEqualsAndHashCodeImpl(processedValue1, processedValue2, processedValue3);
+    }
+
+    @Test
+    public void testEqualsAndHashCode_Int64() {
+        long val1 = 10000000000L;
+        long val2 = 20000000000L;
+        ProcessedValue processedValue1 = ProcessedValue.fromInt64(val1);
+        ProcessedValue processedValue2 = ProcessedValue.fromInt64(val1);
+        ProcessedValue processedValue3 = ProcessedValue.fromInt64(val2);
+
+        testEqualsAndHashCodeImpl(processedValue1, processedValue2, processedValue3);
+    }
+
+    @Test
+    public void testEqualsAndHashCode_GURL() {
+        GURL url = JUnitTestGURLs.EXAMPLE_URL;
+        GURL url2 = JUnitTestGURLs.GOOGLE_URL;
+        ProcessedValue processedValue1 = ProcessedValue.fromGURL(url);
+        ProcessedValue processedValue2 = ProcessedValue.fromGURL(new GURL(url.getSpec()));
+        ProcessedValue processedValue3 = ProcessedValue.fromGURL(url2);
+        testEqualsAndHashCodeImpl(processedValue1, processedValue2, processedValue3);
+    }
+
+    private void testEqualsAndHashCodeImpl(
+            ProcessedValue processedValue1,
+            ProcessedValue processedValue2,
+            ProcessedValue processedValue3) {
+        assertTrue(processedValue1.equals(processedValue2));
+        assertEquals(processedValue1.hashCode(), processedValue2.hashCode());
+
+        assertFalse(processedValue1.equals(null));
+        assertFalse(processedValue1.equals(processedValue3));
+
+        ProcessedValue differentTypeProcessedValue;
+        if (processedValue1.type != ProcessedValueType.INT) {
+            differentTypeProcessedValue = ProcessedValue.fromInt(999);
+        } else {
+            differentTypeProcessedValue = ProcessedValue.fromBoolean(false);
+        }
+        assertFalse(processedValue1.equals(differentTypeProcessedValue));
+    }
+}
diff --git a/components/services/unzip/BUILD.gn b/components/services/unzip/BUILD.gn
index f8ff3b1..5f256cf 100644
--- a/components/services/unzip/BUILD.gn
+++ b/components/services/unzip/BUILD.gn
@@ -67,6 +67,7 @@
     "//components/test/data/unzip_service/UTF8 (Bug 903664).zip",
     "//components/test/data/unzip_service/Wrong CRC.zip",
     "//components/test/data/unzip_service/bad_archive.zip",
+    "//components/test/data/unzip_service/bad_archive_hang.zip",
     "//components/test/data/unzip_service/bd646",
     "//components/test/data/unzip_service/bd646.xz",
     "//components/test/data/unzip_service/bug953599.zip",
diff --git a/components/services/unzip/public/cpp/unzip_unittest.cc b/components/services/unzip/public/cpp/unzip_unittest.cc
index ea12d9b..1784d13 100644
--- a/components/services/unzip/public/cpp/unzip_unittest.cc
+++ b/components/services/unzip/public/cpp/unzip_unittest.cc
@@ -232,6 +232,12 @@
   EXPECT_FALSE(some_files_empty);
 }
 
+TEST_F(UnzipTest, UnzipBadArchiveHang) {
+  // Don't hang trying to open this bad archive.
+  EXPECT_FALSE(DoUnzip(GetArchivePath("bad_archive_hang.zip"), unzip_dir_));
+  EXPECT_EQ(0, CountFiles(unzip_dir_));
+}
+
 TEST_F(UnzipTest, UnzipZip64) {
   EXPECT_TRUE(DoUnzip(GetArchivePath("good_zip64.zip"), unzip_dir_));
   bool some_files_empty = false;
diff --git a/components/signin/internal/identity_manager/account_capabilities_fetcher_factory_gaia.cc b/components/signin/internal/identity_manager/account_capabilities_fetcher_factory_gaia.cc
index f609a83..3bd25951 100644
--- a/components/signin/internal/identity_manager/account_capabilities_fetcher_factory_gaia.cc
+++ b/components/signin/internal/identity_manager/account_capabilities_fetcher_factory_gaia.cc
@@ -16,8 +16,8 @@
 #include "net/base/network_anonymization_key.h"
 #include "net/base/schemeful_site.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/mojom/connection_change_observer_client.mojom.h"
 #include "services/network/public/mojom/network_context.mojom.h"
-#include "services/network/public/mojom/reconnect_event_observer.mojom.h"
 
 AccountCapabilitiesFetcherFactoryGaia::AccountCapabilitiesFetcherFactoryGaia(
     ProfileOAuth2TokenService* token_service,
diff --git a/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.cc b/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.cc
index 099520a6..08f5ad6 100644
--- a/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.cc
+++ b/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.cc
@@ -539,15 +539,13 @@
 
   if (IsInSubresourceFilterRoot(&navigation_handle) && database_manager_) {
     registry.AddThrottle(std::make_unique<SafeBrowsingPageActivationThrottle>(
-        &navigation_handle, profile_interaction_manager_.get(),
-        database_manager_));
+        registry, profile_interaction_manager_.get(), database_manager_));
   }
 
   if (!dealer_handle_) {
     return;
   }
-  registry.MaybeAddThrottle(
-      MaybeCreateChildNavigationThrottle(&navigation_handle));
+  MaybeCreateAndAddChildNavigationThrottle(registry);
 
   CHECK(!base::Contains(ongoing_activation_throttles_,
                         navigation_handle.GetNavigationId()),
@@ -603,24 +601,25 @@
   UMA_HISTOGRAM_ENUMERATION("SubresourceFilter.Actions2", action);
 }
 
-std::unique_ptr<SafeBrowsingChildNavigationThrottle>
-ContentSubresourceFilterThrottleManager::MaybeCreateChildNavigationThrottle(
-    content::NavigationHandle* navigation_handle) {
-  if (IsInSubresourceFilterRoot(navigation_handle)) {
-    return nullptr;
+void ContentSubresourceFilterThrottleManager::
+    MaybeCreateAndAddChildNavigationThrottle(
+        content::NavigationThrottleRegistry& registry) {
+  content::NavigationHandle& navigation_handle = registry.GetNavigationHandle();
+  if (IsInSubresourceFilterRoot(&navigation_handle)) {
+    return;
   }
   AsyncDocumentSubresourceFilter* parent_filter =
-      GetParentFrameFilter(navigation_handle);
-  return parent_filter ? std::make_unique<SafeBrowsingChildNavigationThrottle>(
-                             navigation_handle, parent_filter,
-                             profile_interaction_manager_->AsWeakPtr(),
-                             base::BindRepeating([](const GURL& url) {
-                               return base::StringPrintf(
-                                   kDisallowChildFrameConsoleMessageFormat,
-                                   url.possibly_invalid_spec().c_str());
-                             }),
-                             EnsureFrameAdEvidence(navigation_handle))
-                       : nullptr;
+      GetParentFrameFilter(&navigation_handle);
+  if (!parent_filter) {
+    return;
+  }
+  registry.AddThrottle(std::make_unique<SafeBrowsingChildNavigationThrottle>(
+      registry, parent_filter, profile_interaction_manager_->AsWeakPtr(),
+      base::BindRepeating([](const GURL& url) {
+        return base::StringPrintf(kDisallowChildFrameConsoleMessageFormat,
+                                  url.possibly_invalid_spec().c_str());
+      }),
+      EnsureFrameAdEvidence(&navigation_handle)));
 }
 
 std::unique_ptr<ActivationStateComputingNavigationThrottle>
diff --git a/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.h b/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.h
index 516f057..c0f99a6 100644
--- a/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.h
+++ b/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.h
@@ -268,9 +268,8 @@
       AdTagCarriesAcrossProcesses);
   FRIEND_TEST_ALL_PREFIXES(ContentSubresourceFilterThrottleManagerTest,
                            FirstDisallowedLoadCalledOutOfOrder);
-  std::unique_ptr<SafeBrowsingChildNavigationThrottle>
-  MaybeCreateChildNavigationThrottle(
-      content::NavigationHandle* navigation_handle);
+  void MaybeCreateAndAddChildNavigationThrottle(
+      content::NavigationThrottleRegistry& registry);
   std::unique_ptr<ActivationStateComputingNavigationThrottle>
   MaybeCreateActivationStateComputingThrottle(
       content::NavigationHandle* navigation_handle);
diff --git a/components/subresource_filter/content/browser/safe_browsing_child_navigation_throttle.cc b/components/subresource_filter/content/browser/safe_browsing_child_navigation_throttle.cc
index b3eb41a..b008eb48 100644
--- a/components/subresource_filter/content/browser/safe_browsing_child_navigation_throttle.cc
+++ b/components/subresource_filter/content/browser/safe_browsing_child_navigation_throttle.cc
@@ -52,14 +52,14 @@
 namespace subresource_filter {
 
 SafeBrowsingChildNavigationThrottle::SafeBrowsingChildNavigationThrottle(
-    content::NavigationHandle* handle,
+    content::NavigationThrottleRegistry& registry,
     AsyncDocumentSubresourceFilter* parent_frame_filter,
     base::WeakPtr<ProfileInteractionManager> profile_interaction_manager,
     base::RepeatingCallback<std::string(const GURL& url)>
         disallow_message_callback,
     std::optional<blink::FrameAdEvidence> ad_evidence)
     : ChildFrameNavigationFilteringThrottle(
-          handle,
+          registry,
           parent_frame_filter,
           /*alias_check_enabled=*/
           base::FeatureList::IsEnabled(
diff --git a/components/subresource_filter/content/browser/safe_browsing_child_navigation_throttle.h b/components/subresource_filter/content/browser/safe_browsing_child_navigation_throttle.h
index 2fcedfe..9a1ff20d 100644
--- a/components/subresource_filter/content/browser/safe_browsing_child_navigation_throttle.h
+++ b/components/subresource_filter/content/browser/safe_browsing_child_navigation_throttle.h
@@ -35,7 +35,7 @@
     : public ChildFrameNavigationFilteringThrottle {
  public:
   SafeBrowsingChildNavigationThrottle(
-      content::NavigationHandle* handle,
+      content::NavigationThrottleRegistry& registry,
       AsyncDocumentSubresourceFilter* parent_frame_filter,
       base::WeakPtr<ProfileInteractionManager> profile_interaction_manager,
       base::RepeatingCallback<std::string(const GURL& url)>
diff --git a/components/subresource_filter/content/browser/safe_browsing_child_navigation_throttle_unittest.cc b/components/subresource_filter/content/browser/safe_browsing_child_navigation_throttle_unittest.cc
index c2240a0..a4b81aa8 100644
--- a/components/subresource_filter/content/browser/safe_browsing_child_navigation_throttle_unittest.cc
+++ b/components/subresource_filter/content/browser/safe_browsing_child_navigation_throttle_unittest.cc
@@ -10,12 +10,14 @@
 #include "base/functional/bind.h"
 #include "base/memory/weak_ptr.h"
 #include "base/strings/stringprintf.h"
+#include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "components/subresource_filter/content/shared/browser/child_frame_navigation_test_utils.h"
 #include "components/subresource_filter/core/browser/subresource_filter_constants.h"
 #include "components/subresource_filter/core/mojom/subresource_filter.mojom.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/test/navigation_simulator.h"
+#include "content/public/test/test_navigation_throttle_inserter.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
@@ -35,27 +37,42 @@
 
   ~SafeBrowsingChildNavigationThrottleTest() override = default;
 
+  void SetUp() override {
+    ChildFrameNavigationFilteringThrottleTestHarness::SetUp();
+    // The |parent_filter_| is the parent frame's filter. Do not register a
+    // throttle if the parent is not activated with a valid filter.
+
+    throttle_inserter_ =
+        std::make_unique<content::TestNavigationThrottleInserter>(
+            content::RenderViewHostTestHarness::web_contents(),
+            base::BindLambdaForTesting(
+                [&](content::NavigationThrottleRegistry& registry) -> void {
+                  if (parent_filter_) {
+                    auto throttle =
+                        std::make_unique<SafeBrowsingChildNavigationThrottle>(
+                            registry, parent_filter_.get(),
+                            /*profile_interaction_manager=*/
+                            base::WeakPtr<ProfileInteractionManager>(),
+                            base::BindRepeating([](const GURL& filtered_url) {
+                              return base::StringPrintf(
+                                  kDisallowChildFrameConsoleMessageFormat,
+                                  filtered_url.possibly_invalid_spec().c_str());
+                            }),
+                            /*ad_evidence=*/std::nullopt);
+                    EXPECT_NE(nullptr, throttle->GetNameForLogging());
+                    registry.AddThrottle(std::move(throttle));
+                  }
+                }));
+  }
+
   // content::WebContentsObserver:
   void DidStartNavigation(
       content::NavigationHandle* navigation_handle) override {
     ASSERT_FALSE(navigation_handle->IsInMainFrame());
-    // The |parent_filter_| is the parent frame's filter. Do not register a
-    // throttle if the parent is not activated with a valid filter.
-    if (parent_filter_) {
-      auto throttle = std::make_unique<SafeBrowsingChildNavigationThrottle>(
-          navigation_handle, parent_filter_.get(),
-          /*profile_interaction_manager=*/
-          base::WeakPtr<ProfileInteractionManager>(),
-          base::BindRepeating([](const GURL& filtered_url) {
-            return base::StringPrintf(
-                kDisallowChildFrameConsoleMessageFormat,
-                filtered_url.possibly_invalid_spec().c_str());
-          }),
-          /*ad_evidence=*/std::nullopt);
-      ASSERT_NE(nullptr, throttle->GetNameForLogging());
-      navigation_handle->RegisterThrottleForTesting(std::move(throttle));
-    }
   }
+
+ private:
+  std::unique_ptr<content::TestNavigationThrottleInserter> throttle_inserter_;
 };
 
 TEST_F(SafeBrowsingChildNavigationThrottleTest, DelayMetrics) {
diff --git a/components/subresource_filter/content/browser/safe_browsing_page_activation_throttle.cc b/components/subresource_filter/content/browser/safe_browsing_page_activation_throttle.cc
index e81caa9a..d0d80d7 100644
--- a/components/subresource_filter/content/browser/safe_browsing_page_activation_throttle.cc
+++ b/components/subresource_filter/content/browser/safe_browsing_page_activation_throttle.cc
@@ -71,17 +71,18 @@
 }  // namespace
 
 SafeBrowsingPageActivationThrottle::SafeBrowsingPageActivationThrottle(
-    content::NavigationHandle* handle,
+    content::NavigationThrottleRegistry& registry,
     SafeBrowsingPageActivationThrottle::Delegate* delegate,
     scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager> database_manager)
-    : NavigationThrottle(handle),
+    : NavigationThrottle(registry),
       database_client_(nullptr),
       delegate_(delegate) {
   database_client_.reset(new SubresourceFilterSafeBrowsingClient(
       std::move(database_manager), this,
       base::SingleThreadTaskRunner::GetCurrentDefault()));
 
-  CHECK(IsInSubresourceFilterRoot(handle), base::NotFatalUntil::M129);
+  CHECK(IsInSubresourceFilterRoot(&registry.GetNavigationHandle()),
+        base::NotFatalUntil::M129);
   CheckCurrentUrl();
   CHECK(!check_results_.empty(), base::NotFatalUntil::M129);
 }
diff --git a/components/subresource_filter/content/browser/safe_browsing_page_activation_throttle.h b/components/subresource_filter/content/browser/safe_browsing_page_activation_throttle.h
index 4170a99..6bbf855 100644
--- a/components/subresource_filter/content/browser/safe_browsing_page_activation_throttle.h
+++ b/components/subresource_filter/content/browser/safe_browsing_page_activation_throttle.h
@@ -66,7 +66,7 @@
   // throttle will not be able to adjust activation decisions made by the
   // throttle.
   SafeBrowsingPageActivationThrottle(
-      content::NavigationHandle* handle,
+      content::NavigationThrottleRegistry& registry,
       Delegate* delegate,
       scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager>
           database_manager);
diff --git a/components/subresource_filter/content/browser/safe_browsing_page_activation_throttle_unittest.cc b/components/subresource_filter/content/browser/safe_browsing_page_activation_throttle_unittest.cc
index 222b8256..8bd3dbe0 100644
--- a/components/subresource_filter/content/browser/safe_browsing_page_activation_throttle_unittest.cc
+++ b/components/subresource_filter/content/browser/safe_browsing_page_activation_throttle_unittest.cc
@@ -16,6 +16,7 @@
 #include "base/functional/callback_helpers.h"
 #include "base/run_loop.h"
 #include "base/task/single_thread_task_runner.h"
+#include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/test_mock_time_task_runner.h"
@@ -49,6 +50,7 @@
 #include "content/public/test/mock_navigation_throttle_registry.h"
 #include "content/public/test/navigation_simulator.h"
 #include "content/public/test/test_navigation_throttle.h"
+#include "content/public/test/test_navigation_throttle_inserter.h"
 #include "content/public/test/test_renderer_host.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -196,6 +198,8 @@
     messages::MessageDispatcherBridge::SetInstanceForTesting(
         &message_dispatcher_bridge_);
 #endif
+
+    SetUpThrottleInserter();
   }
 
   virtual void Configure() {
@@ -228,16 +232,25 @@
 
   // content::WebContentsObserver:
   void DidStartNavigation(
-      content::NavigationHandle* navigation_handle) override {
-    if (IsInSubresourceFilterRoot(navigation_handle)) {
-      navigation_handle->RegisterThrottleForTesting(
-          std::make_unique<SafeBrowsingPageActivationThrottle>(
-              navigation_handle, delegate(), fake_safe_browsing_database_));
-    }
-    content::MockNavigationThrottleRegistry registry(navigation_handle);
-    ContentSubresourceFilterThrottleManager::FromNavigationHandle(
-        *navigation_handle)
-        ->MaybeCreateAndAddNavigationThrottles(registry);
+      content::NavigationHandle* navigation_handle) override {}
+
+  void SetUpThrottleInserter() {
+    throttle_inserter_ =
+        std::make_unique<content::TestNavigationThrottleInserter>(
+            content::RenderViewHostTestHarness::web_contents(),
+            base::BindLambdaForTesting(
+                [&](content::NavigationThrottleRegistry& registry) -> void {
+                  auto& navigation_handle = registry.GetNavigationHandle();
+                  if (IsInSubresourceFilterRoot(&navigation_handle)) {
+                    registry.AddThrottle(
+                        std::make_unique<SafeBrowsingPageActivationThrottle>(
+                            registry, delegate(),
+                            fake_safe_browsing_database_));
+                  }
+                  ContentSubresourceFilterThrottleManager::FromNavigationHandle(
+                      registry.GetNavigationHandle())
+                      ->MaybeCreateAndAddNavigationThrottles(registry);
+                }));
   }
 
   // Returns the frame host the navigation committed in, or nullptr if it did
@@ -367,6 +380,7 @@
   std::unique_ptr<TestSubresourceFilterObserver> observer_;
   scoped_refptr<FakeSafeBrowsingDatabaseManager> fake_safe_browsing_database_;
   base::HistogramTester tester_;
+  std::unique_ptr<content::TestNavigationThrottleInserter> throttle_inserter_;
 };
 
 class SafeBrowsingPageActivationThrottleParamTest
diff --git a/components/subresource_filter/content/shared/browser/child_frame_navigation_filtering_throttle.cc b/components/subresource_filter/content/shared/browser/child_frame_navigation_filtering_throttle.cc
index 3885a60c..d8066c59 100644
--- a/components/subresource_filter/content/shared/browser/child_frame_navigation_filtering_throttle.cc
+++ b/components/subresource_filter/content/shared/browser/child_frame_navigation_filtering_throttle.cc
@@ -30,16 +30,17 @@
 namespace subresource_filter {
 
 ChildFrameNavigationFilteringThrottle::ChildFrameNavigationFilteringThrottle(
-    content::NavigationHandle* handle,
+    content::NavigationThrottleRegistry& registry,
     AsyncDocumentSubresourceFilter* parent_frame_filter,
     bool alias_check_enabled,
     base::RepeatingCallback<std::string(const GURL& url)>
         disallow_message_callback)
-    : content::NavigationThrottle(handle),
+    : content::NavigationThrottle(registry),
       parent_frame_filter_(parent_frame_filter),
       alias_check_enabled_(alias_check_enabled),
       disallow_message_callback_(std::move(disallow_message_callback)) {
-  CHECK(!IsInSubresourceFilterRoot(handle), base::NotFatalUntil::M129);
+  CHECK(!IsInSubresourceFilterRoot(&registry.GetNavigationHandle()),
+        base::NotFatalUntil::M129);
   CHECK(parent_frame_filter_, base::NotFatalUntil::M129);
 }
 
diff --git a/components/subresource_filter/content/shared/browser/child_frame_navigation_filtering_throttle.h b/components/subresource_filter/content/shared/browser/child_frame_navigation_filtering_throttle.h
index cce6da25..30ed8c0 100644
--- a/components/subresource_filter/content/shared/browser/child_frame_navigation_filtering_throttle.h
+++ b/components/subresource_filter/content/shared/browser/child_frame_navigation_filtering_throttle.h
@@ -23,10 +23,6 @@
 BASE_DECLARE_FEATURE(kSendCnameAliasesToSubresourceFilterFromBrowser);
 }  // namespace features
 
-namespace content {
-class NavigationHandle;
-}  // namespace content
-
 namespace subresource_filter {
 
 class AsyncDocumentSubresourceFilter;
@@ -44,7 +40,7 @@
     : public content::NavigationThrottle {
  public:
   ChildFrameNavigationFilteringThrottle(
-      content::NavigationHandle* handle,
+      content::NavigationThrottleRegistry& registry,
       AsyncDocumentSubresourceFilter* parent_frame_filter,
       bool alias_check_enabled,
       base::RepeatingCallback<std::string(const GURL& url)>
diff --git a/components/subresource_filter/content/shared/browser/child_frame_navigation_filtering_throttle_unittest.cc b/components/subresource_filter/content/shared/browser/child_frame_navigation_filtering_throttle_unittest.cc
index ef523bbd..e991766 100644
--- a/components/subresource_filter/content/shared/browser/child_frame_navigation_filtering_throttle_unittest.cc
+++ b/components/subresource_filter/content/shared/browser/child_frame_navigation_filtering_throttle_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
 #include "base/strings/stringprintf.h"
+#include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "child_frame_navigation_filtering_throttle.h"
@@ -20,6 +21,7 @@
 #include "components/subresource_filter/core/browser/subresource_filter_constants.h"
 #include "components/subresource_filter/core/mojom/subresource_filter.mojom.h"
 #include "content/public/browser/navigation_handle.h"
+#include "content/public/test/test_navigation_throttle_inserter.h"
 #include "content/public/test/test_renderer_host.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
@@ -30,13 +32,13 @@
     : public ChildFrameNavigationFilteringThrottle {
  public:
   TestChildFrameNavigationFilteringThrottle(
-      content::NavigationHandle* handle,
+      content::NavigationThrottleRegistry& registry,
       AsyncDocumentSubresourceFilter* parent_frame_filter,
       bool alias_check_enabled,
       base::RepeatingCallback<std::string(const GURL& url)>
           disallow_message_callback)
       : ChildFrameNavigationFilteringThrottle(
-            handle,
+            registry,
             parent_frame_filter,
             alias_check_enabled,
             std::move(disallow_message_callback)) {}
@@ -81,30 +83,43 @@
 
   ~ChildFrameNavigationFilteringThrottleTest() override = default;
 
+  void SetUp() override {
+    ChildFrameNavigationFilteringThrottleTestHarness::SetUp();
+
+    throttle_inserter_ =
+        std::make_unique<content::TestNavigationThrottleInserter>(
+            content::RenderViewHostTestHarness::web_contents(),
+            base::BindLambdaForTesting([&](content::NavigationThrottleRegistry&
+                                        registry) -> void {
+              // The |parent_filter_| is the parent frame's filter. Do not
+              // register a throttle if the parent is not activated with a valid
+              // filter.
+              if (parent_filter_) {
+                auto throttle =
+                    std::make_unique<TestChildFrameNavigationFilteringThrottle>(
+                        registry, parent_filter_.get(),
+                        /*alias_check_enabled=*/alias_check_enabled_,
+                        base::BindRepeating([](const GURL& filtered_url) {
+                          // Same as GetFilterConsoleMessage().
+                          return base::StringPrintf(
+                              kDisallowChildFrameConsoleMessageFormat,
+                              filtered_url.possibly_invalid_spec().c_str());
+                        }));
+                EXPECT_NE(nullptr, throttle->GetNameForLogging());
+                registry.AddThrottle(std::move(throttle));
+              }
+            }));
+  }
+
   // content::WebContentsObserver:
   void DidStartNavigation(
       content::NavigationHandle* navigation_handle) override {
     ASSERT_FALSE(navigation_handle->IsInMainFrame());
-    // The |parent_filter_| is the parent frame's filter. Do not register a
-    // throttle if the parent is not activated with a valid filter.
-    if (parent_filter_) {
-      auto throttle =
-          std::make_unique<TestChildFrameNavigationFilteringThrottle>(
-              navigation_handle, parent_filter_.get(),
-              /*alias_check_enabled=*/alias_check_enabled_,
-              base::BindRepeating([](const GURL& filtered_url) {
-                // Same as GetFilterConsoleMessage().
-                return base::StringPrintf(
-                    kDisallowChildFrameConsoleMessageFormat,
-                    filtered_url.possibly_invalid_spec().c_str());
-              }));
-      ASSERT_NE(nullptr, throttle->GetNameForLogging());
-      navigation_handle->RegisterThrottleForTesting(std::move(throttle));
-    }
   }
 
  protected:
   bool alias_check_enabled_ = false;
+  std::unique_ptr<content::TestNavigationThrottleInserter> throttle_inserter_;
 };
 
 TEST_F(ChildFrameNavigationFilteringThrottleTest, FilterOnStart) {
diff --git a/components/sync/android/sync_service_android_bridge.cc b/components/sync/android/sync_service_android_bridge.cc
index d7bb6b5..9e17c30 100644
--- a/components/sync/android/sync_service_android_bridge.cc
+++ b/components/sync/android/sync_service_android_bridge.cc
@@ -293,6 +293,16 @@
     JNIEnv* env,
     jboolean sync_everything,
     const JavaParamRef<jintArray>& user_selectable_type_array) {
+  if (native_sync_service_->GetAccountInfo().account_id.empty()) {
+    // This function shouldn't be called while signed out, but evidence suggests
+    // it sometimes does get called.
+    // TODO(crbug.com/369301153): Remove workaround and adopt CHECK/NOTREACHED
+    // once crashes are no longer reported. This could also be cleaned up once
+    // crbug.com/40066949 is tackled.
+    DUMP_WILL_BE_NOTREACHED();
+    return;
+  }
+
   std::vector<int> types_vector;
   base::android::JavaIntArrayToIntVector(env, user_selectable_type_array,
                                          &types_vector);
@@ -309,6 +319,16 @@
 void SyncServiceAndroidBridge::SetSelectedType(JNIEnv* env,
                                                jint type,
                                                jboolean is_type_on) {
+  if (native_sync_service_->GetAccountInfo().account_id.empty()) {
+    // This function shouldn't be called while signed out, but evidence suggests
+    // it sometimes does get called.
+    // TODO(crbug.com/369301153): Remove workaround and adopt CHECK/NOTREACHED
+    // once crashes are no longer reported. This could also be cleaned up once
+    // crbug.com/40066949 is tackled.
+    DUMP_WILL_BE_NOTREACHED();
+    return;
+  }
+
   native_sync_service_->GetUserSettings()->SetSelectedType(
       IntToUserSelectableTypeChecked(type), is_type_on);
 }
diff --git a/components/sync/service/sync_user_settings_impl.cc b/components/sync/service/sync_user_settings_impl.cc
index 379e340..a2a1282 100644
--- a/components/sync/service/sync_user_settings_impl.cc
+++ b/components/sync/service/sync_user_settings_impl.cc
@@ -168,10 +168,7 @@
 
   switch (delegate_->GetSyncAccountStateForPrefs()) {
     case SyncPrefs::SyncAccountState::kNotSignedIn:
-      // TODO(crbug.com/40945692): Convert to NOTREACHED.
-      DUMP_WILL_BE_NOTREACHED()
-          << "Must not set selected types while signed out";
-      break;
+      NOTREACHED();
     case SyncPrefs::SyncAccountState::kSignedInNotSyncing:
       for (UserSelectableType type : registered_types) {
         SetSelectedType(type, types.Has(type) || sync_everything);
@@ -190,12 +187,8 @@
   CHECK(registered_types.Has(type));
 
   switch (delegate_->GetSyncAccountStateForPrefs()) {
-    case SyncPrefs::SyncAccountState::kNotSignedIn: {
-      // TODO(crbug.com/40945692): Convert to NOTREACHED.
-      DUMP_WILL_BE_NOTREACHED()
-          << "Must not set selected types while signed out";
-      break;
-    }
+    case SyncPrefs::SyncAccountState::kNotSignedIn:
+      NOTREACHED();
     case SyncPrefs::SyncAccountState::kSignedInNotSyncing: {
       prefs_->SetSelectedTypeForAccount(
           type, is_type_on, delegate_->GetSyncAccountInfoForPrefs().gaia);
@@ -457,8 +450,9 @@
     const std::string& token) {
   const GaiaId& gaia_id = delegate_->GetSyncAccountInfoForPrefs().gaia;
   if (gaia_id.empty()) {
-    // TODO(crbug.com/40945692): Convert to NOTREACHED.
-    DUMP_WILL_BE_NOTREACHED() << "Must not set passphrase while signed out";
+    // The user must be signed in, so the only legit scenario where SyncService
+    // uses no Gaia ID is local sync (roaming profiles).
+    CHECK(prefs_->IsLocalSyncEnabled());
     return;
   }
   prefs_->SetEncryptionBootstrapTokenForAccount(token, gaia_id);
diff --git a/components/sync/service/trusted_vault_synthetic_field_trial.cc b/components/sync/service/trusted_vault_synthetic_field_trial.cc
index 36ebcf7..f0931cd 100644
--- a/components/sync/service/trusted_vault_synthetic_field_trial.cc
+++ b/components/sync/service/trusted_vault_synthetic_field_trial.cc
@@ -11,11 +11,12 @@
 
 #include "base/metrics/histogram_functions.h"
 #include "base/notreached.h"
+#include "base/numerics/byte_conversions.h"
 #include "base/rand_util.h"
 #include "base/strings/strcat.h"
 #include "base/strings/stringprintf.h"
 #include "components/sync/protocol/nigori_specifics.pb.h"
-#include "crypto/secure_hash.h"
+#include "crypto/hash.h"
 #include "google_apis/gaia/gaia_id.h"
 
 namespace syncer {
@@ -78,15 +79,16 @@
   CHECK(!gaia_id.empty());
 
   const std::string_view kSuffix = "TrustedVaultAutoUpgrade";
-  uint64_t value = 0;
 
-  std::unique_ptr<crypto::SecureHash> sha256(
-      crypto::SecureHash::Create(crypto::SecureHash::SHA256));
-  sha256->Update(gaia_id.ToString().data(), gaia_id.ToString().length());
-  sha256->Update(salt.data(), salt.length());
-  sha256->Update(kSuffix.data(), kSuffix.length());
-  sha256->Finish(&value, sizeof(value));
+  crypto::hash::Hasher sha256(crypto::hash::HashKind::kSha256);
+  sha256.Update(gaia_id.ToString());
+  sha256.Update(salt);
+  sha256.Update(kSuffix);
 
+  std::array<uint8_t, crypto::hash::kSha256Size> full_hash;
+  sha256.Finish(full_hash);
+
+  uint64_t value = base::U64FromNativeEndian(base::span(full_hash).first<8>());
   const int kResolution = 100000;
   return 1.0f * (value % kResolution) / kResolution;
 }
diff --git a/components/test/data/unzip_service/bad_archive_hang.zip b/components/test/data/unzip_service/bad_archive_hang.zip
new file mode 100644
index 0000000..10b8c7a5
--- /dev/null
+++ b/components/test/data/unzip_service/bad_archive_hang.zip
Binary files differ
diff --git a/components/test/data/web_database/unit_tests_bundle_data.filelist b/components/test/data/web_database/unit_tests_bundle_data.filelist
index bb25f4d..16bd5c2 100644
--- a/components/test/data/web_database/unit_tests_bundle_data.filelist
+++ b/components/test/data/web_database/unit_tests_bundle_data.filelist
@@ -45,6 +45,7 @@
 //components/test/data/web_database/version_138.sql
 //components/test/data/web_database/version_139.sql
 //components/test/data/web_database/version_140.sql
+//components/test/data/web_database/version_141.sql
 //components/test/data/web_database/version_82.sql
 //components/test/data/web_database/version_83.sql
 //components/test/data/web_database/version_84.sql
diff --git a/components/test/data/web_database/version_141.sql b/components/test/data/web_database/version_141.sql
new file mode 100644
index 0000000..3caff3f
--- /dev/null
+++ b/components/test/data/web_database/version_141.sql
@@ -0,0 +1,44 @@
+PRAGMA foreign_keys=OFF;
+BEGIN TRANSACTION;
+CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY, value LONGVARCHAR);
+INSERT INTO meta VALUES('mmap_status','-1');
+INSERT INTO meta VALUES('version','141');
+INSERT INTO meta VALUES('last_compatible_version','139');
+INSERT INTO meta VALUES('Builtin Keyword Version','145');
+INSERT INTO meta VALUES('Starter Pack Keyword Version','5');
+CREATE TABLE token_service (service VARCHAR PRIMARY KEY NOT NULL,encrypted_token BLOB, binding_key BLOB);
+CREATE TABLE autofill (name VARCHAR, value VARCHAR, value_lower VARCHAR, date_created INTEGER DEFAULT 0, date_last_used INTEGER DEFAULT 0, count INTEGER DEFAULT 1, PRIMARY KEY (name, value));
+CREATE TABLE credit_cards (guid VARCHAR PRIMARY KEY, name_on_card VARCHAR, expiration_month INTEGER, expiration_year INTEGER, card_number_encrypted BLOB, date_modified INTEGER NOT NULL DEFAULT 0, origin VARCHAR DEFAULT '', use_count INTEGER NOT NULL DEFAULT 0, use_date INTEGER NOT NULL DEFAULT 0, billing_address_id VARCHAR, nickname VARCHAR);
+CREATE TABLE local_ibans (guid VARCHAR PRIMARY KEY, use_count INTEGER NOT NULL DEFAULT 0, use_date INTEGER NOT NULL DEFAULT 0, value_encrypted VARCHAR, nickname VARCHAR);
+CREATE TABLE masked_credit_cards (id VARCHAR, name_on_card VARCHAR, network VARCHAR, last_four VARCHAR, exp_month INTEGER DEFAULT 0, exp_year INTEGER DEFAULT 0, bank_name VARCHAR, nickname VARCHAR, card_issuer INTEGER DEFAULT 0, instrument_id INTEGER DEFAULT 0, virtual_card_enrollment_state INTEGER DEFAULT 0, card_art_url VARCHAR, product_description VARCHAR, card_issuer_id VARCHAR, virtual_card_enrollment_type INTEGER DEFAULT 0, product_terms_url VARCHAR, card_info_retrieval_enrollment_state INTEGER DEFAULT 0, card_benefit_source INTEGER DEFAULT 0);
+CREATE TABLE server_card_metadata (id VARCHAR NOT NULL, use_count INTEGER NOT NULL DEFAULT 0, use_date INTEGER NOT NULL DEFAULT 0, billing_address_id VARCHAR);
+CREATE TABLE autofill_sync_metadata (model_type INTEGER NOT NULL, storage_key VARCHAR NOT NULL, value BLOB, PRIMARY KEY (model_type, storage_key));
+CREATE TABLE autofill_model_type_state (model_type INTEGER NOT NULL PRIMARY KEY, value BLOB);
+CREATE TABLE payments_customer_data (customer_id VARCHAR);
+CREATE TABLE server_card_cloud_token_data (id VARCHAR, suffix VARCHAR, exp_month INTEGER DEFAULT 0, exp_year INTEGER DEFAULT 0, card_art_url VARCHAR, instrument_token VARCHAR);
+CREATE TABLE offer_data (offer_id UNSIGNED LONG, offer_reward_amount VARCHAR, expiry UNSIGNED LONG, offer_details_url VARCHAR, merchant_domain VARCHAR, promo_code VARCHAR, value_prop_text VARCHAR, see_details_text VARCHAR, usage_instructions_text VARCHAR);
+CREATE TABLE offer_eligible_instrument (offer_id UNSIGNED LONG, instrument_id UNSIGNED LONG);
+CREATE TABLE offer_merchant_domain (offer_id UNSIGNED LONG, merchant_domain VARCHAR);
+CREATE TABLE addresses (guid VARCHAR PRIMARY KEY, use_count INTEGER NOT NULL DEFAULT 0, use_date INTEGER NOT NULL DEFAULT 0, date_modified INTEGER NOT NULL DEFAULT 0, language_code VARCHAR, label VARCHAR, initial_creator_id INTEGER DEFAULT 0, last_modifier_id INTEGER DEFAULT 0, use_date2 INTEGER, use_date3 INTEGER, record_type INTEGER);
+CREATE TABLE address_type_tokens (guid VARCHAR, type INTEGER, value VARCHAR, verification_status INTEGER DEFAULT 0, observations BLOB, PRIMARY KEY (guid, type));
+CREATE TABLE virtual_card_usage_data (id VARCHAR PRIMARY KEY, instrument_id INTEGER DEFAULT 0, merchant_domain VARCHAR, last_four VARCHAR);
+CREATE TABLE local_stored_cvc (guid VARCHAR PRIMARY KEY NOT NULL, value_encrypted VARCHAR NOT NULL, last_updated_timestamp INTEGER NOT NULL);
+CREATE TABLE server_stored_cvc (instrument_id INTEGER PRIMARY KEY NOT NULL, value_encrypted VARCHAR NOT NULL, last_updated_timestamp INTEGER NOT NULL);
+CREATE TABLE masked_bank_accounts (instrument_id INTEGER PRIMARY KEY NOT NULL, bank_name VARCHAR, account_number_suffix VARCHAR, account_type INTEGER DEFAULT 0, display_icon_url VARCHAR, nickname VARCHAR);
+CREATE TABLE masked_bank_accounts_metadata (instrument_id INTEGER NOT NULL, use_count INTEGER NOT NULL DEFAULT 0, use_date INTEGER NOT NULL DEFAULT 0);
+CREATE TABLE masked_ibans (instrument_id VARCHAR PRIMARY KEY NOT NULL, prefix VARCHAR NOT NULL, suffix VARCHAR NOT NULL, nickname VARCHAR);
+CREATE TABLE masked_ibans_metadata (instrument_id VARCHAR PRIMARY KEY NOT NULL, use_count INTEGER NOT NULL DEFAULT 0, use_date INTEGER NOT NULL DEFAULT 0);
+CREATE TABLE keywords (id INTEGER PRIMARY KEY,short_name VARCHAR NOT NULL,keyword VARCHAR NOT NULL,favicon_url VARCHAR NOT NULL,url VARCHAR NOT NULL,safe_for_autoreplace INTEGER,originating_url VARCHAR,date_created INTEGER DEFAULT 0,usage_count INTEGER DEFAULT 0,input_encodings VARCHAR,suggest_url VARCHAR,prepopulate_id INTEGER DEFAULT 0,created_by_policy INTEGER DEFAULT 0,last_modified INTEGER DEFAULT 0,sync_guid VARCHAR,alternate_urls VARCHAR,image_url VARCHAR,search_url_post_params VARCHAR,suggest_url_post_params VARCHAR,image_url_post_params VARCHAR,new_tab_url VARCHAR,last_visited INTEGER DEFAULT 0, created_from_play_api INTEGER DEFAULT 0, is_active INTEGER DEFAULT 0, starter_pack_id INTEGER DEFAULT 0, enforced_by_policy INTEGER DEFAULT 0, featured_by_policy INTEGER DEFAULT 0, url_hash BLOB);
+CREATE INDEX autofill_name ON autofill(name);
+CREATE INDEX autofill_name_value_lower ON autofill(name, value_lower);
+CREATE TABLE benefit_merchant_domains(benefit_id VARCHAR NOT NULL,merchant_domain VARCHAR NOT NULL);
+CREATE TABLE masked_credit_card_benefits(benefit_id VARCHAR PRIMARY KEY NOT NULL,instrument_id INTEGER NOT NULL DEFAULT 0,benefit_type INTEGER NOT NULL DEFAULT 0,benefit_category INTEGER NOT NULL DEFAULT 0,benefit_description VARCHAR NOT NULL,start_time INTEGER,end_time INTEGER);
+CREATE TABLE plus_addresses (profile_id VARCHAR PRIMARY KEY, facet VARCHAR, plus_address VARCHAR);
+CREATE TABLE plus_address_sync_model_type_state (model_type INTEGER PRIMARY KEY, value BLOB);
+CREATE TABLE plus_address_sync_entity_metadata (model_type INTEGER, storage_key VARCHAR, value BLOB, PRIMARY KEY (model_type, storage_key));
+CREATE TABLE generic_payment_instruments (instrument_id INTEGER PRIMARY KEY NOT NULL, serialized_value_encrypted VARCHAR NOT NULL);
+CREATE TABLE payment_instrument_creation_options (id VARCHAR PRIMARY KEY NOT NULL, serialized_value_encrypted VARCHAR NOT NULL);
+CREATE TABLE autofill_ai_attributes (entity_guid TEXT NOT NULL, attribute_type TEXT NOT NULL, field_type INTEGER NOT NULL, value_encrypted BLOB NOT NULL, verification_status INTEGER NOT NULL, PRIMARY KEY (entity_guid, attribute_type, field_type));
+CREATE TABLE autofill_ai_entities (guid TEXT NOT NULL PRIMARY KEY, entity_type TEXT NOT NULL, nickname TEXT NOT NULL, date_modified INTEGER NOT NULL, use_count INTEGER DEFAULT 0, use_date INTEGER DEFAULT 0);
+CREATE TABLE loyalty_cards (loyalty_card_id TEXT PRIMARY KEY NOT NULL, merchant_name TEXT NOT NULL, program_name TEXT NOT NULL, program_logo TEXT NOT NULL, loyalty_card_number TEXT NOT NULL);
+COMMIT;
diff --git a/components/webdata/common/web_data_results.h b/components/webdata/common/web_data_results.h
index b224377..584dc8f 100644
--- a/components/webdata/common/web_data_results.h
+++ b/components/webdata/common/web_data_results.h
@@ -57,10 +57,13 @@
   // The browser bound key id is retrieved by the payments component
   // during secure payment confirmation requests and payment credential
   // creation.
-  BROWSER_BOUND_KEY,            // WDResult<std::vector<uint8_t>>
-  PAYMENT_WEB_APP_MANIFEST,     // WDResult<std::vector<
-                                //     mojom::WebAppManifestSectionPtr>>
-  PAYMENT_METHOD_MANIFEST,      // WDResult<std::vector<std::string>>
+  BROWSER_BOUND_KEY,  // WDResult<std::vector<uint8_t>>
+  // The browser bound key metadata is retrieved by the payments component
+  // to find stale credentials.
+  BROWSER_BOUND_KEY_METADATA,  // WDResult<std::vector<BrowserBoundKeyMetadata>>
+  PAYMENT_WEB_APP_MANIFEST,    // WDResult<std::vector<
+                               //     mojom::WebAppManifestSectionPtr>>
+  PAYMENT_METHOD_MANIFEST,     // WDResult<std::vector<std::string>>
   SECURE_PAYMENT_CONFIRMATION,  // WDResult<std::vector<std::unique_ptr<
                                 //     SecurePaymentConfirmationInstrument>>>
 #endif                          //
diff --git a/components/webdata/common/web_database.h b/components/webdata/common/web_database.h
index 3c3d777..a83d81a5 100644
--- a/components/webdata/common/web_database.h
+++ b/components/webdata/common/web_database.h
@@ -31,7 +31,7 @@
   // Note: when changing the current version number, corresponding changes must
   // happen in the unit tests, and new migration test added to
   // `WebDatabaseMigrationTest`.
-  static constexpr int kCurrentVersionNumber = 140;
+  static constexpr int kCurrentVersionNumber = 141;
 
   // To support users who are upgrading from older versions of Chrome, we enable
   // migrating from any database version newer than `kDeprecatedVersionNumber`.
diff --git a/components/webdata/common/web_database_migration_unittest.cc b/components/webdata/common/web_database_migration_unittest.cc
index a47b523..621d88c 100644
--- a/components/webdata/common/web_database_migration_unittest.cc
+++ b/components/webdata/common/web_database_migration_unittest.cc
@@ -1693,4 +1693,24 @@
   }
 }
 
+TEST_F(WebDatabaseMigrationTest, MigrateVersion140ToCurrent) {
+  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_140.sql")));
+  {
+    sql::Database connection(sql::test::kTestTag);
+    ASSERT_TRUE(connection.Open(GetDatabasePath()));
+    EXPECT_EQ(140, VersionFromConnection(&connection));
+    EXPECT_FALSE(connection.DoesColumnExist("masked_credit_cards",
+                                            "card_benefit_source"));
+  }
+  DoMigration();
+  {
+    sql::Database connection(sql::test::kTestTag);
+    ASSERT_TRUE(connection.Open(GetDatabasePath()));
+    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
+              VersionFromConnection(&connection));
+    EXPECT_TRUE(connection.DoesColumnExist("masked_credit_cards",
+                                           "card_benefit_source"));
+  }
+}
+
 }  // anonymous namespace
diff --git a/content/app/ios/appex/BUILD.gn b/content/app/ios/appex/BUILD.gn
index b2fc7d85..b748528 100644
--- a/content/app/ios/appex/BUILD.gn
+++ b/content/app/ios/appex/BUILD.gn
@@ -28,6 +28,14 @@
   frameworks = [ "Foundation.framework" ]
 }
 
+source_set("app_side_shim") {
+  sources = [ "child_process.mm" ]
+
+  deps = [ "//base/allocator:early_zone_registration_apple" ]
+
+  public_deps = [ ":child_process_bridge_header" ]
+}
+
 swift_source_set("content_process") {
   sources = [ "content_process.swift" ]
   bridge_header = "child_process_bridge.h"
@@ -39,7 +47,7 @@
     "BrowserEngineKit.framework",
   ]
 
-  deps = [ ":child_process_bridge_header" ]
+  deps = [ ":app_side_shim" ]
 }
 
 swift_source_set("network_process") {
@@ -53,7 +61,7 @@
     "BrowserEngineKit.framework",
   ]
 
-  deps = [ ":child_process_bridge_header" ]
+  deps = [ ":app_side_shim" ]
 }
 
 swift_source_set("gpu_process") {
@@ -67,5 +75,5 @@
     "BrowserEngineKit.framework",
   ]
 
-  deps = [ ":child_process_bridge_header" ]
+  deps = [ ":app_side_shim" ]
 }
diff --git a/content/app/ios/appex/child_process.mm b/content/app/ios/appex/child_process.mm
new file mode 100644
index 0000000..e4637a23
--- /dev/null
+++ b/content/app/ios/appex/child_process.mm
@@ -0,0 +1,25 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import <Foundation/Foundation.h>
+
+#include "base/allocator/early_zone_registration_apple.h"
+
+@interface EarlyInitializationObject : NSObject
+@end
+
+@implementation EarlyInitializationObject
++ (void)load {
+  // Perform malloc zone registration early. See
+  // https://developer.apple.com/documentation/objectivec/nsobject-swift.class/load()?language=objc#Discussion
+  partition_alloc::EarlyMallocZoneRegistration();
+}
+@end
+
+#define IOS_INIT_EXPORT __attribute__((visibility("default")))
+
+extern "C" void IOS_INIT_EXPORT ChildProcessStarted() {
+  // TODO(crbug.com/419046227): Investigate why this call from swift to this
+  // ObjC call is necessarily.
+}
diff --git a/content/app/ios/appex/child_process_bridge.h b/content/app/ios/appex/child_process_bridge.h
index f2c3393..e19bcbe 100644
--- a/content/app/ios/appex/child_process_bridge.h
+++ b/content/app/ios/appex/child_process_bridge.h
@@ -16,6 +16,7 @@
 extern "C" {
 #endif
 
+void ChildProcessStarted();
 void ChildProcessInit(id<ChildProcessExtension> process);
 void GpuProcessInit();
 void ChildProcessHandleNewConnection(xpc_connection_t connection);
diff --git a/content/app/ios/appex/content_process.swift b/content/app/ios/appex/content_process.swift
index 7130679..1d6bad3 100644
--- a/content/app/ios/appex/content_process.swift
+++ b/content/app/ios/appex/content_process.swift
@@ -10,6 +10,7 @@
 class ContentProcess: NSObject, ChildProcessExtension, WebContentExtension {
   override required init() {
     super.init()
+    ChildProcessStarted()
     ChildProcessInit(self)
   }
 
diff --git a/content/app/ios/appex/gpu_process.swift b/content/app/ios/appex/gpu_process.swift
index 4877d74..3a6ab96f 100644
--- a/content/app/ios/appex/gpu_process.swift
+++ b/content/app/ios/appex/gpu_process.swift
@@ -10,6 +10,7 @@
 class GPUProcess: NSObject, ChildProcessExtension, RenderingExtension {
   override required init() {
     super.init()
+    ChildProcessStarted()
     ChildProcessInit(self)
     GpuProcessInit()
   }
diff --git a/content/app/ios/appex/network_process.swift b/content/app/ios/appex/network_process.swift
index 5cc8c49..02b4649 100644
--- a/content/app/ios/appex/network_process.swift
+++ b/content/app/ios/appex/network_process.swift
@@ -10,6 +10,7 @@
 class NetworkProcess: NSObject, ChildProcessExtension, NetworkingExtension {
   override required init() {
     super.init()
+    ChildProcessStarted()
     ChildProcessInit(self)
   }
 
diff --git a/content/browser/attribution_reporting/attribution_internals_handler_impl.cc b/content/browser/attribution_reporting/attribution_internals_handler_impl.cc
index 4265782..de7a496 100644
--- a/content/browser/attribution_reporting/attribution_internals_handler_impl.cc
+++ b/content/browser/attribution_reporting/attribution_internals_handler_impl.cc
@@ -100,7 +100,7 @@
       source.source_time().InMillisecondsFSinceUnixEpoch(),
       source.expiry_time().InMillisecondsFSinceUnixEpoch(),
       source.event_report_windows(),
-      base::ToVector(source.trigger_specs().trigger_data()),
+      base::ToVector(source.trigger_data().trigger_data()),
       source.max_event_level_reports(),
       source.aggregatable_report_window_time().InMillisecondsFSinceUnixEpoch(),
       common_info.source_type(), source.priority(), source.debug_key(),
diff --git a/content/browser/attribution_reporting/attribution_resolver_delegate.h b/content/browser/attribution_reporting/attribution_resolver_delegate.h
index 39a5793..34cbe3c 100644
--- a/content/browser/attribution_reporting/attribution_resolver_delegate.h
+++ b/content/browser/attribution_reporting/attribution_resolver_delegate.h
@@ -26,7 +26,7 @@
 class EventLevelEpsilon;
 class EventReportWindows;
 class MaxEventLevelReports;
-class TriggerSpecs;
+class TriggerDataSet;
 }  // namespace attribution_reporting
 
 namespace base {
@@ -134,7 +134,7 @@
   // limit.
   virtual GetRandomizedResponseResult GetRandomizedResponse(
       attribution_reporting::mojom::SourceType,
-      const attribution_reporting::TriggerSpecs&,
+      const attribution_reporting::TriggerDataSet&,
       const attribution_reporting::EventReportWindows&,
       attribution_reporting::MaxEventLevelReports,
       attribution_reporting::EventLevelEpsilon,
diff --git a/content/browser/attribution_reporting/attribution_resolver_delegate_impl.cc b/content/browser/attribution_reporting/attribution_resolver_delegate_impl.cc
index ea2a065..988ba87c 100644
--- a/content/browser/attribution_reporting/attribution_resolver_delegate_impl.cc
+++ b/content/browser/attribution_reporting/attribution_resolver_delegate_impl.cc
@@ -165,7 +165,7 @@
 AttributionResolverDelegate::GetRandomizedResponseResult
 AttributionResolverDelegateImpl::GetRandomizedResponse(
     SourceType source_type,
-    const attribution_reporting::TriggerSpecs& trigger_specs,
+    const attribution_reporting::TriggerDataSet& trigger_data,
     const attribution_reporting::EventReportWindows& event_report_windows,
     const attribution_reporting::MaxEventLevelReports max_event_level_reports,
     attribution_reporting::EventLevelEpsilon epsilon,
@@ -176,7 +176,7 @@
   ASSIGN_OR_RETURN(
       auto response,
       attribution_reporting::DoRandomizedResponse(
-          trigger_specs, event_report_windows, max_event_level_reports, epsilon,
+          trigger_data, event_report_windows, max_event_level_reports, epsilon,
           source_type, scopes_data, config_.privacy_math_config));
 
   switch (noise_mode_) {
diff --git a/content/browser/attribution_reporting/attribution_resolver_delegate_impl.h b/content/browser/attribution_reporting/attribution_resolver_delegate_impl.h
index 120a5e8..d38f296 100644
--- a/content/browser/attribution_reporting/attribution_resolver_delegate_impl.h
+++ b/content/browser/attribution_reporting/attribution_resolver_delegate_impl.h
@@ -86,7 +86,7 @@
   void ShuffleReports(std::vector<AttributionReport>& reports) override;
   GetRandomizedResponseResult GetRandomizedResponse(
       attribution_reporting::mojom::SourceType,
-      const attribution_reporting::TriggerSpecs&,
+      const attribution_reporting::TriggerDataSet&,
       const attribution_reporting::EventReportWindows&,
       attribution_reporting::MaxEventLevelReports,
       attribution_reporting::EventLevelEpsilon,
diff --git a/content/browser/attribution_reporting/attribution_resolver_delegate_impl_unittest.cc b/content/browser/attribution_reporting/attribution_resolver_delegate_impl_unittest.cc
index d7180d39..1f5c65f 100644
--- a/content/browser/attribution_reporting/attribution_resolver_delegate_impl_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_resolver_delegate_impl_unittest.cc
@@ -75,7 +75,7 @@
     auto result =
         AttributionResolverDelegateImpl(AttributionNoiseMode::kNone)
             .GetRandomizedResponse(
-                source.common_info().source_type(), source.trigger_specs(),
+                source.common_info().source_type(), source.trigger_data(),
                 source.event_report_windows(), source.max_event_level_reports(),
                 source.event_level_epsilon(),
                 /*attribution_scope_data=*/std::nullopt);
@@ -129,7 +129,7 @@
         SourceBuilder().SetSourceType(test_case.source_type).BuildStored();
 
     auto result = delegate->GetRandomizedResponse(
-        test_case.source_type, source.trigger_specs(),
+        test_case.source_type, source.trigger_data(),
         source.event_report_windows(), source.max_event_level_reports(),
         source.event_level_epsilon(),
         /*attribution_scope_data=*/std::nullopt);
diff --git a/content/browser/attribution_reporting/attribution_resolver_impl.cc b/content/browser/attribution_reporting/attribution_resolver_impl.cc
index 4f21341..ce974f5 100644
--- a/content/browser/attribution_reporting/attribution_resolver_impl.cc
+++ b/content/browser/attribution_reporting/attribution_resolver_impl.cc
@@ -223,9 +223,9 @@
   ASSIGN_OR_RETURN(
       const auto randomized_response_data,
       delegate_->GetRandomizedResponse(
-          common_info.source_type(), reg.trigger_specs,
-          reg.event_report_windows, reg.max_event_level_reports,
-          reg.event_level_epsilon, reg.attribution_scopes_data),
+          common_info.source_type(), reg.trigger_data, reg.event_report_windows,
+          reg.max_event_level_reports, reg.event_level_epsilon,
+          reg.attribution_scopes_data),
       [&](auto error) -> StoreSourceResult {
         DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
         switch (error) {
@@ -254,7 +254,7 @@
         }
       });
   DCHECK(attribution_reporting::IsValid(
-      randomized_response_data.response(), reg.trigger_specs,
+      randomized_response_data.response(), reg.trigger_data,
       reg.event_report_windows, reg.max_event_level_reports));
 
   // Force the creation of the database if it doesn't exist, as we need to
@@ -875,7 +875,7 @@
     return CreateReportResult::Deduplicated();
   }
 
-  std::optional<uint32_t> trigger_data = source.trigger_specs().find(
+  std::optional<uint32_t> trigger_data = source.trigger_data().find(
       event_trigger->data, source.trigger_data_matching());
   if (!trigger_data.has_value()) {
     return CreateReportResult::NoMatchingTriggerData();
diff --git a/content/browser/attribution_reporting/attribution_resolver_unittest.cc b/content/browser/attribution_reporting/attribution_resolver_unittest.cc
index 3a5ca12..e7a75fc8 100644
--- a/content/browser/attribution_reporting/attribution_resolver_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_resolver_unittest.cc
@@ -108,7 +108,7 @@
 using ::attribution_reporting::kDefaultFilteringId;
 using ::attribution_reporting::MaxEventLevelReports;
 using ::attribution_reporting::SuitableOrigin;
-using ::attribution_reporting::TriggerSpecs;
+using ::attribution_reporting::TriggerDataSet;
 using ::attribution_reporting::mojom::SourceType;
 using ::attribution_reporting::mojom::TriggerDataMatching;
 using ::blink::mojom::AggregatableReportHistogramContribution;
@@ -4744,7 +4744,7 @@
           SourceBuilder()
               .SetDestinationSites(
                   {net::SchemefulSite::Deserialize("https://d1.test")})
-              .SetTriggerSpecs(TriggerSpecs(SourceType::kEvent))
+              .SetTriggerData(TriggerDataSet(SourceType::kEvent))
               .SetEventReportWindows(
                   *attribution_reporting::EventReportWindows::Create(
                       base::Days(0), {base::Days(1), base::Days(2)}))
diff --git a/content/browser/attribution_reporting/attribution_src_browsertest.cc b/content/browser/attribution_reporting/attribution_src_browsertest.cc
index 576f0a5..f191f1a 100644
--- a/content/browser/attribution_reporting/attribution_src_browsertest.cc
+++ b/content/browser/attribution_reporting/attribution_src_browsertest.cc
@@ -816,13 +816,12 @@
   TestNavigationThrottleInserter throttle_inserter(
       web_contents(),
       base::BindLambdaForTesting(
-          [&](NavigationHandle* handle) -> std::unique_ptr<NavigationThrottle> {
-            auto throttle = std::make_unique<TestNavigationThrottle>(handle);
+          [&](NavigationThrottleRegistry& registry) -> void {
+            auto throttle = std::make_unique<TestNavigationThrottle>(registry);
             throttle->SetResponse(TestNavigationThrottle::WILL_REDIRECT_REQUEST,
                                   TestNavigationThrottle::SYNCHRONOUS,
                                   NavigationThrottle::CANCEL_AND_IGNORE);
-
-            return throttle;
+            registry.AddThrottle(std::move(throttle));
           }));
 
   // Create a separate server as we cannot register a `ControllableHttpResponse`
diff --git a/content/browser/attribution_reporting/attribution_storage_sql.cc b/content/browser/attribution_reporting/attribution_storage_sql.cc
index 013d9ab..770a01d 100644
--- a/content/browser/attribution_reporting/attribution_storage_sql.cc
+++ b/content/browser/attribution_reporting/attribution_storage_sql.cc
@@ -371,7 +371,7 @@
   }
 
   attribution_reporting::MaxEventLevelReports max_event_level_reports;
-  std::optional<attribution_reporting::TriggerSpecs> trigger_specs;
+  std::optional<attribution_reporting::TriggerDataSet> trigger_data;
   std::optional<attribution_reporting::EventReportWindows> event_report_windows;
   attribution_reporting::EventLevelEpsilon event_level_epsilon;
 
@@ -389,11 +389,11 @@
     }
 
     if (source_type.has_value()) {
-      trigger_specs =
-          DeserializeTriggerSpecs(*read_only_source_data_msg, *source_type);
-      if (!trigger_specs.has_value()) {
+      trigger_data =
+          DeserializeTriggerDataSet(*read_only_source_data_msg, *source_type);
+      if (!trigger_data.has_value()) {
         corruption_causes.Put(
-            ReportCorruptionStatus::kSourceInvalidTriggerSpecs);
+            ReportCorruptionStatus::kSourceInvalidTriggerData);
       }
     }
 
@@ -483,7 +483,7 @@
       CommonSourceInfo(*std::move(source_origin), *std::move(reporting_origin),
                        *source_type, cookie_based_debug_allowed),
       source_event_id, *std::move(destination_set), source_time, expiry_time,
-      *std::move(trigger_specs), *std::move(event_report_windows),
+      *std::move(trigger_data), *std::move(event_report_windows),
       max_event_level_reports, aggregatable_report_window_time, priority,
       *std::move(filter_data), debug_key, *std::move(aggregation_keys),
       *attribution_logic, *active_state, source_id,
@@ -729,7 +729,7 @@
   statement.BindBlob(
       17,
       SerializeReadOnlySourceData(
-          reg.trigger_specs, reg.event_report_windows,
+          reg.trigger_data, reg.event_report_windows,
           reg.max_event_level_reports, randomized_response_rate,
           reg.trigger_data_matching, common_info.cookie_based_debug_allowed(),
           reg.aggregatable_debug_reporting_config.config().key_piece));
@@ -770,7 +770,7 @@
   // `StoredSource` is only used within this method.
   return StoredSource::Create(
       source.common_info(), reg.source_event_id, reg.destination_set,
-      source_time, expiry_time, reg.trigger_specs, reg.event_report_windows,
+      source_time, expiry_time, reg.trigger_data, reg.event_report_windows,
       reg.max_event_level_reports, aggregatable_report_window_time,
       reg.priority, reg.filter_data, reg.debug_key, reg.aggregation_keys,
       attribution_logic, *active_state, source_id,
diff --git a/content/browser/attribution_reporting/attribution_storage_sql.h b/content/browser/attribution_reporting/attribution_storage_sql.h
index 886e770..a49549ed 100644
--- a/content/browser/attribution_reporting/attribution_storage_sql.h
+++ b/content/browser/attribution_reporting/attribution_storage_sql.h
@@ -170,7 +170,7 @@
     kSourceDestinationSitesQueryFailed = 26,
     kSourceInvalidDestinationSites = 27,
     kStoredSourceConstructionFailed = 28,
-    kSourceInvalidTriggerSpecs = 29,
+    kSourceInvalidTriggerData = 29,
     kSourceDedupKeyQueryFailed = 30,
     kSourceInvalidRandomizedResponseRate = 31,
     kSourceInvalidAttributionScopesData = 32,
diff --git a/content/browser/attribution_reporting/attribution_storage_sql_migrations_unittest.cc b/content/browser/attribution_reporting/attribution_storage_sql_migrations_unittest.cc
index 3002f4b..8edbeae 100644
--- a/content/browser/attribution_reporting/attribution_storage_sql_migrations_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_storage_sql_migrations_unittest.cc
@@ -599,9 +599,9 @@
         "1,2,'https://a.s.test','https://r.test',5,6,7,8,0,0,0,0,13,"
         "'https://s.test',15,16,17,'','',?,21,22,NULL,NULL)"));
 
-    // Calling `mutable_trigger_data()` forces creation of the field, even
-    // when `trigger_specs.empty()` below, so that the presence check in
-    // `DeserializeTriggerSpecs()` doesn't mistakenly use the defaults
+    // Calling `mutable_trigger_data()` forces creation of the field
+    // so that the presence check in
+    // `DeserializeTriggerDataSet()` doesn't mistakenly use the defaults
     // corresponding to the field being absent, as opposed to its inner list
     // being empty.
     proto::AttributionReadOnlySourceData msg_source;
diff --git a/content/browser/attribution_reporting/attribution_storage_sql_unittest.cc b/content/browser/attribution_reporting/attribution_storage_sql_unittest.cc
index f269045c..7221b0e 100644
--- a/content/browser/attribution_reporting/attribution_storage_sql_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_storage_sql_unittest.cc
@@ -1532,16 +1532,18 @@
   EXPECT_THAT(
       storage()->GetActiveSources(),
       UnorderedElementsAre(
-          AllOf(Property(&StoredSource::source_event_id, 1u),
-                Property(
-                    &StoredSource::trigger_specs,
-                    Property(&attribution_reporting::TriggerSpecs::trigger_data,
-                             ElementsAre(0, 1, 2, 3, 4, 5, 6, 7)))),
-          AllOf(Property(&StoredSource::source_event_id, 2u),
-                Property(
-                    &StoredSource::trigger_specs,
-                    Property(&attribution_reporting::TriggerSpecs::trigger_data,
-                             ElementsAre(0, 1))))));
+          AllOf(
+              Property(&StoredSource::source_event_id, 1u),
+              Property(
+                  &StoredSource::trigger_data,
+                  Property(&attribution_reporting::TriggerDataSet::trigger_data,
+                           ElementsAre(0, 1, 2, 3, 4, 5, 6, 7)))),
+          AllOf(
+              Property(&StoredSource::source_event_id, 2u),
+              Property(
+                  &StoredSource::trigger_data,
+                  Property(&attribution_reporting::TriggerDataSet::trigger_data,
+                           ElementsAre(0, 1))))));
 }
 
 // Having the missing field default to the correct value allows us to avoid a
diff --git a/content/browser/attribution_reporting/attribution_test_utils.cc b/content/browser/attribution_reporting/attribution_test_utils.cc
index 5c89b4d..552fc70b 100644
--- a/content/browser/attribution_reporting/attribution_test_utils.cc
+++ b/content/browser/attribution_reporting/attribution_test_utils.cc
@@ -87,8 +87,8 @@
           {net::SchemefulSite::Deserialize(kDefaultDestinationOrigin)})),
       reporting_origin_(*SuitableOrigin::Deserialize(kDefaultReportOrigin)) {
   registration_.source_event_id = 123;
-  registration_.trigger_specs =
-      attribution_reporting::TriggerSpecs(source_type_);
+  registration_.trigger_data =
+      attribution_reporting::TriggerDataSet(source_type_);
   registration_.max_event_level_reports =
       attribution_reporting::MaxEventLevelReports::Max();
 }
@@ -138,8 +138,8 @@
 
 SourceBuilder& SourceBuilder::SetSourceType(SourceType source_type) {
   source_type_ = source_type;
-  registration_.trigger_specs =
-      attribution_reporting::TriggerSpecs(source_type_);
+  registration_.trigger_data =
+      attribution_reporting::TriggerDataSet(source_type_);
   registration_.max_event_level_reports =
       attribution_reporting::MaxEventLevelReports(source_type);
   return *this;
@@ -225,9 +225,9 @@
   return *this;
 }
 
-SourceBuilder& SourceBuilder::SetTriggerSpecs(
-    attribution_reporting::TriggerSpecs trigger_specs) {
-  registration_.trigger_specs = std::move(trigger_specs);
+SourceBuilder& SourceBuilder::SetTriggerData(
+    attribution_reporting::TriggerDataSet trigger_data) {
+  registration_.trigger_data = std::move(trigger_data);
   return *this;
 }
 
@@ -297,7 +297,7 @@
       CommonSourceInfo(source_origin_, reporting_origin_, source_type_,
                        cookie_based_debug_allowed_),
       registration_.source_event_id, registration_.destination_set,
-      source_time_, expiry_time, registration_.trigger_specs,
+      source_time_, expiry_time, registration_.trigger_data,
       registration_.event_report_windows, registration_.max_event_level_reports,
       source_time_ + registration_.aggregatable_report_window,
       registration_.priority, registration_.filter_data,
@@ -617,7 +617,7 @@
     return std::make_tuple(
         source.common_info(), source.source_event_id(),
         source.destination_sites(), source.source_time(), source.expiry_time(),
-        source.trigger_specs(), source.aggregatable_report_window_time(),
+        source.trigger_data(), source.aggregatable_report_window_time(),
         source.priority(), source.filter_data(), source.debug_key(),
         source.aggregation_keys(), source.attribution_logic(),
         source.active_state(), source.dedup_keys(),
@@ -740,7 +740,7 @@
       << ",destination_sites=" << source.destination_sites()
       << ",source_time=" << source.source_time()
       << ",expiry_time=" << source.expiry_time()
-      << ",trigger_specs=" << source.trigger_specs()
+      << ",trigger_data=" << source.trigger_data()
       << ",aggregatable_report_window_time="
       << source.aggregatable_report_window_time()
       << ",priority=" << source.priority()
diff --git a/content/browser/attribution_reporting/attribution_test_utils.h b/content/browser/attribution_reporting/attribution_test_utils.h
index 804ba3de..a80fe09a 100644
--- a/content/browser/attribution_reporting/attribution_test_utils.h
+++ b/content/browser/attribution_reporting/attribution_test_utils.h
@@ -41,7 +41,7 @@
 class AggregatableValues;
 class AggregationKeys;
 class AttributionScopesData;
-class TriggerSpecs;
+class TriggerDataSet;
 }  // namespace attribution_reporting
 
 namespace net {
@@ -118,7 +118,7 @@
 
   SourceBuilder& SetDebugReporting(bool debug_reporting);
 
-  SourceBuilder& SetTriggerSpecs(attribution_reporting::TriggerSpecs);
+  SourceBuilder& SetTriggerData(attribution_reporting::TriggerDataSet);
 
   SourceBuilder& SetEventReportWindows(
       attribution_reporting::EventReportWindows);
diff --git a/content/browser/attribution_reporting/interop/runner.cc b/content/browser/attribution_reporting/interop/runner.cc
index 8de36423..a8f4ad5 100644
--- a/content/browser/attribution_reporting/interop/runner.cc
+++ b/content/browser/attribution_reporting/interop/runner.cc
@@ -301,7 +301,7 @@
   // AttributionResolverDelegateImpl:
   GetRandomizedResponseResult GetRandomizedResponse(
       const attribution_reporting::mojom::SourceType source_type,
-      const attribution_reporting::TriggerSpecs& trigger_specs,
+      const attribution_reporting::TriggerDataSet& trigger_data,
       const attribution_reporting::EventReportWindows& event_report_windows,
       const attribution_reporting::MaxEventLevelReports max_event_level_reports,
       const attribution_reporting::EventLevelEpsilon epsilon,
@@ -311,7 +311,7 @@
 
     ASSIGN_OR_RETURN(auto response_data,
                      AttributionResolverDelegateImpl::GetRandomizedResponse(
-                         source_type, trigger_specs, event_report_windows,
+                         source_type, trigger_data, event_report_windows,
                          max_event_level_reports, epsilon, scopes_data));
 
     auto it = randomized_responses_.find(base::Time::Now());
@@ -321,11 +321,11 @@
 
     // Avoid crashing in `AttributionStorageSql::StoreSource()` by returning an
     // arbitrary error here, which will manifest as unexpected test output.
-    if (!attribution_reporting::IsValid(it->second, trigger_specs,
+    if (!attribution_reporting::IsValid(it->second, trigger_data,
                                         event_report_windows,
                                         max_event_level_reports)) {
-      LOG(ERROR) << "invalid randomized response with trigger_specs="
-                 << trigger_specs;
+      LOG(ERROR) << "invalid randomized response with trigger_data="
+                 << trigger_data;
       return base::unexpected(attribution_reporting::RandomizedResponseError::
                                   kExceedsChannelCapacityLimit);
     }
diff --git a/content/browser/attribution_reporting/sql_utils.cc b/content/browser/attribution_reporting/sql_utils.cc
index 76eb230..0c1935f5 100644
--- a/content/browser/attribution_reporting/sql_utils.cc
+++ b/content/browser/attribution_reporting/sql_utils.cc
@@ -55,7 +55,7 @@
 using ::attribution_reporting::AggregatableTriggerConfig;
 using ::attribution_reporting::EventReportWindows;
 using ::attribution_reporting::SuitableOrigin;
-using ::attribution_reporting::TriggerSpecs;
+using ::attribution_reporting::TriggerDataSet;
 using ::attribution_reporting::mojom::SourceRegistrationTimeConfig;
 using ::attribution_reporting::mojom::SourceType;
 using ::attribution_reporting::mojom::TriggerDataMatching;
@@ -188,7 +188,7 @@
 }  // namespace
 
 std::string SerializeReadOnlySourceData(
-    const TriggerSpecs& trigger_specs,
+    const TriggerDataSet& trigger_data,
     const EventReportWindows& event_report_windows,
     attribution_reporting::MaxEventLevelReports max_event_level_reports,
     double randomized_response_rate,
@@ -202,17 +202,16 @@
 
   if (
       // Calling `mutable_trigger_data()` forces creation of the field, even
-      // when `trigger_specs.empty()` below, so that the presence check in
-      // `DeserializeTriggerSpecs()` doesn't mistakenly use the defaults
+      // when `trigger_data.trigger_data().empty()` below, so that the presence check in
+      // `DeserializeTriggerDataSet()` doesn't mistakenly use the defaults
       // corresponding to the field being absent, as opposed to its inner list
       // being empty.
       auto* mutable_trigger_data = msg.mutable_trigger_data();
-      !trigger_specs.trigger_data().empty()) {
+      !trigger_data.trigger_data().empty()) {
     SetReadOnlySourceData(&event_report_windows, max_event_level_reports, msg);
 
-    for (uint32_t trigger_data : trigger_specs.trigger_data()) {
-      mutable_trigger_data->add_trigger_data(trigger_data);
-    }
+    mutable_trigger_data->mutable_trigger_data()->Add(
+        trigger_data.trigger_data().begin(), trigger_data.trigger_data().end());
   } else {
     SetReadOnlySourceData(/*event_report_windows=*/nullptr,
                           max_event_level_reports, msg);
@@ -476,16 +475,16 @@
                                      /*source_origin=*/std::nullopt);
 }
 
-std::optional<TriggerSpecs> DeserializeTriggerSpecs(
+std::optional<TriggerDataSet> DeserializeTriggerDataSet(
     const proto::AttributionReadOnlySourceData& msg,
     SourceType source_type) {
   if (!msg.has_trigger_data()) {
-    return TriggerSpecs(source_type);
+    return TriggerDataSet(source_type);
   }
 
-  return TriggerSpecs::Create(
-      TriggerSpecs::TriggerData(msg.trigger_data().trigger_data().begin(),
-                                msg.trigger_data().trigger_data().end()));
+  return TriggerDataSet::Create(
+      TriggerDataSet::TriggerData(msg.trigger_data().trigger_data().begin(),
+                                  msg.trigger_data().trigger_data().end()));
 }
 
 std::optional<EventReportWindows> DeserializeEventReportWindows(
diff --git a/content/browser/attribution_reporting/sql_utils.h b/content/browser/attribution_reporting/sql_utils.h
index 0ab67e7..1d1c4869 100644
--- a/content/browser/attribution_reporting/sql_utils.h
+++ b/content/browser/attribution_reporting/sql_utils.h
@@ -31,7 +31,7 @@
 class FilterData;
 class MaxEventLevelReports;
 class SuitableOrigin;
-class TriggerSpecs;
+class TriggerDataSet;
 }  // namespace attribution_reporting
 
 namespace base {
@@ -60,7 +60,7 @@
     int val);
 
 std::string SerializeReadOnlySourceData(
-    const attribution_reporting::TriggerSpecs&,
+    const attribution_reporting::TriggerDataSet&,
     const attribution_reporting::EventReportWindows&,
     attribution_reporting::MaxEventLevelReports,
     double randomized_response_rate,
@@ -77,7 +77,7 @@
     sql::Statement&,
     int col);
 
-std::optional<attribution_reporting::TriggerSpecs> DeserializeTriggerSpecs(
+std::optional<attribution_reporting::TriggerDataSet> DeserializeTriggerDataSet(
     const proto::AttributionReadOnlySourceData&,
     attribution_reporting::mojom::SourceType);
 
diff --git a/content/browser/attribution_reporting/stored_source.cc b/content/browser/attribution_reporting/stored_source.cc
index 0497966d..acd7db5 100644
--- a/content/browser/attribution_reporting/stored_source.cc
+++ b/content/browser/attribution_reporting/stored_source.cc
@@ -74,7 +74,7 @@
     attribution_reporting::DestinationSet destination_sites,
     base::Time source_time,
     base::Time expiry_time,
-    attribution_reporting::TriggerSpecs trigger_specs,
+    attribution_reporting::TriggerDataSet trigger_data,
     attribution_reporting::EventReportWindows event_report_windows,
     attribution_reporting::MaxEventLevelReports max_event_level_reports,
     base::Time aggregatable_report_window_time,
@@ -104,7 +104,7 @@
 
   return StoredSource(
       std::move(common_info), source_event_id, std::move(destination_sites),
-      source_time, expiry_time, std::move(trigger_specs),
+      source_time, expiry_time, std::move(trigger_data),
       std::move(event_report_windows), max_event_level_reports,
       aggregatable_report_window_time, priority, std::move(filter_data),
       debug_key, std::move(aggregation_keys), attribution_logic, active_state,
@@ -121,7 +121,7 @@
     attribution_reporting::DestinationSet destination_sites,
     base::Time source_time,
     base::Time expiry_time,
-    attribution_reporting::TriggerSpecs trigger_specs,
+    attribution_reporting::TriggerDataSet trigger_data,
     attribution_reporting::EventReportWindows event_report_windows,
     attribution_reporting::MaxEventLevelReports max_event_level_reports,
     base::Time aggregatable_report_window_time,
@@ -146,7 +146,7 @@
       destination_sites_(std::move(destination_sites)),
       source_time_(source_time),
       expiry_time_(expiry_time),
-      trigger_specs_(std::move(trigger_specs)),
+      trigger_data_(std::move(trigger_data)),
       event_report_windows_(std::move(event_report_windows)),
       max_event_level_reports_(max_event_level_reports),
       aggregatable_report_window_time_(aggregatable_report_window_time),
diff --git a/content/browser/attribution_reporting/stored_source.h b/content/browser/attribution_reporting/stored_source.h
index 6e1ec27..041b5548 100644
--- a/content/browser/attribution_reporting/stored_source.h
+++ b/content/browser/attribution_reporting/stored_source.h
@@ -62,7 +62,7 @@
       attribution_reporting::DestinationSet,
       base::Time source_time,
       base::Time expiry_time,
-      attribution_reporting::TriggerSpecs,
+      attribution_reporting::TriggerDataSet,
       attribution_reporting::EventReportWindows,
       attribution_reporting::MaxEventLevelReports,
       base::Time aggregatable_report_window_time,
@@ -106,8 +106,8 @@
     return aggregatable_report_window_time_;
   }
 
-  const attribution_reporting::TriggerSpecs& trigger_specs() const {
-    return trigger_specs_;
+  const attribution_reporting::TriggerDataSet& trigger_data() const {
+    return trigger_data_;
   }
 
   const attribution_reporting::EventReportWindows& event_report_windows()
@@ -187,7 +187,7 @@
                attribution_reporting::DestinationSet,
                base::Time source_time,
                base::Time expiry_time,
-               attribution_reporting::TriggerSpecs,
+               attribution_reporting::TriggerDataSet,
                attribution_reporting::EventReportWindows,
                attribution_reporting::MaxEventLevelReports,
                base::Time aggregatable_report_window_time,
@@ -213,7 +213,7 @@
   attribution_reporting::DestinationSet destination_sites_;
   base::Time source_time_;
   base::Time expiry_time_;
-  attribution_reporting::TriggerSpecs trigger_specs_;
+  attribution_reporting::TriggerDataSet trigger_data_;
   attribution_reporting::EventReportWindows event_report_windows_;
   attribution_reporting::MaxEventLevelReports max_event_level_reports_;
   base::Time aggregatable_report_window_time_;
diff --git a/content/browser/attribution_reporting/test/configurable_storage_delegate.cc b/content/browser/attribution_reporting/test/configurable_storage_delegate.cc
index cc384bca..b93d9aa 100644
--- a/content/browser/attribution_reporting/test/configurable_storage_delegate.cc
+++ b/content/browser/attribution_reporting/test/configurable_storage_delegate.cc
@@ -120,7 +120,7 @@
 AttributionResolverDelegate::GetRandomizedResponseResult
 ConfigurableStorageDelegate::GetRandomizedResponse(
     attribution_reporting::mojom::SourceType,
-    const attribution_reporting::TriggerSpecs&,
+    const attribution_reporting::TriggerDataSet&,
     const attribution_reporting::EventReportWindows&,
     attribution_reporting::MaxEventLevelReports,
     attribution_reporting::EventLevelEpsilon,
diff --git a/content/browser/attribution_reporting/test/configurable_storage_delegate.h b/content/browser/attribution_reporting/test/configurable_storage_delegate.h
index fb006e1..a74a697 100644
--- a/content/browser/attribution_reporting/test/configurable_storage_delegate.h
+++ b/content/browser/attribution_reporting/test/configurable_storage_delegate.h
@@ -42,7 +42,7 @@
   void ShuffleReports(std::vector<AttributionReport>&) override;
   GetRandomizedResponseResult GetRandomizedResponse(
       attribution_reporting::mojom::SourceType,
-      const attribution_reporting::TriggerSpecs&,
+      const attribution_reporting::TriggerDataSet&,
       const attribution_reporting::EventReportWindows&,
       attribution_reporting::MaxEventLevelReports,
       attribution_reporting::EventLevelEpsilon,
diff --git a/content/browser/btm/btm_bounce_detector.cc b/content/browser/btm/btm_bounce_detector.cc
index 6ea8245b..f10c7a5 100644
--- a/content/browser/btm/btm_bounce_detector.cc
+++ b/content/browser/btm/btm_bounce_detector.cc
@@ -537,9 +537,11 @@
                            const GURL& final_url,
                            base::span<BtmRedirectInfoPtr> redirects) {
   const blink::StorageKey initial_url_key =
-      blink::StorageKey::CreateFirstParty(url::Origin::Create(initial_url));
+      blink::StorageKey::CreateFirstParty(url::Origin::Create(
+          initial_url.SchemeIsHTTPOrHTTPS() ? initial_url : GURL()));
   const blink::StorageKey final_url_key =
-      blink::StorageKey::CreateFirstParty(url::Origin::Create(final_url));
+      blink::StorageKey::CreateFirstParty(url::Origin::Create(
+          final_url.SchemeIsHTTPOrHTTPS() ? final_url : GURL()));
   net::CookieSettingOverrides overrides({
       net::CookieSettingOverride::kStorageAccessGrantEligible,
       net::CookieSettingOverride::kTopLevelStorageAccessGrantEligible,
diff --git a/content/browser/child_process_launcher_helper.cc b/content/browser/child_process_launcher_helper.cc
index 2ed7a646..bcb5efa2 100644
--- a/content/browser/child_process_launcher_helper.cc
+++ b/content/browser/child_process_launcher_helper.cc
@@ -315,6 +315,10 @@
     base::Process::Open(process_id_.value()).ForgetPriority();
   }
 #endif  // BUILDFLAG(IS_CHROMEOS)
+#if BUILDFLAG(IS_IOS)
+  GetProcessLauncherTaskRunner()->DeleteSoon(FROM_HERE,
+                                             std::move(scoped_temp_dir_));
+#endif
 }
 
 void ChildProcessLauncherHelper::StartLaunchOnClientThread() {
diff --git a/content/browser/devtools/protocol/storage_handler.cc b/content/browser/devtools/protocol/storage_handler.cc
index b8a64c2a..115e6e2 100644
--- a/content/browser/devtools/protocol/storage_handler.cc
+++ b/content/browser/devtools/protocol/storage_handler.cc
@@ -2050,7 +2050,7 @@
 }
 
 std::unique_ptr<Array<double>> ToTriggerData(
-    const attribution_reporting::TriggerSpecs::TriggerData& trigger_data) {
+    const attribution_reporting::TriggerDataSet::TriggerData& trigger_data) {
   return std::make_unique<Array<double>>(trigger_data.begin(),
                                          trigger_data.end());
 }
@@ -2289,7 +2289,7 @@
               ToAggregationKeysEntries(registration.aggregation_keys))
           .SetExpiry(registration.expiry.InSeconds())
           .SetTriggerData(
-              ToTriggerData(registration.trigger_specs.trigger_data()))
+              ToTriggerData(registration.trigger_data.trigger_data()))
           .SetEventReportWindows(
               ToEventReportWindows(registration.event_report_windows))
           .SetAggregatableReportWindow(
diff --git a/content/browser/interest_group/ad_auction_service_impl.cc b/content/browser/interest_group/ad_auction_service_impl.cc
index d741742..876037a 100644
--- a/content/browser/interest_group/ad_auction_service_impl.cc
+++ b/content/browser/interest_group/ad_auction_service_impl.cc
@@ -67,8 +67,8 @@
 #include "services/metrics/public/cpp/ukm_source_id.h"
 #include "services/network/public/cpp/simple_url_loader.h"
 #include "services/network/public/mojom/client_security_state.mojom.h"
+#include "services/network/public/mojom/connection_change_observer_client.mojom.h"
 #include "services/network/public/mojom/permissions_policy/permissions_policy_feature.mojom.h"
-#include "services/network/public/mojom/reconnect_event_observer.mojom.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
 #include "services/network/public/mojom/url_response_head.mojom.h"
 #include "third_party/blink/public/common/features.h"
diff --git a/content/browser/interest_group/interest_group_browsertest.cc b/content/browser/interest_group/interest_group_browsertest.cc
index a0f2ae8..1a2b84b 100644
--- a/content/browser/interest_group/interest_group_browsertest.cc
+++ b/content/browser/interest_group/interest_group_browsertest.cc
@@ -22390,8 +22390,8 @@
         const net::NetworkAnonymizationKey& network_anonymization_key,
         const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
         const std::optional<net::ConnectionKeepAliveConfig>& keepalive_config,
-        mojo::PendingRemote<network::mojom::ReconnectEventObserver>
-            reconnect_event_observer) override {
+        mojo::PendingRemote<network::mojom::ConnectionChangeObserverClient>
+            observer_client) override {
       EXPECT_EQ(1u, num_streams);
       EXPECT_EQ(expected_url_, url);
       EXPECT_EQ(credentials_mode, network::mojom::CredentialsMode::kInclude);
diff --git a/content/browser/loader/navigation_early_hints_manager.cc b/content/browser/loader/navigation_early_hints_manager.cc
index 99340e6..07ea957 100644
--- a/content/browser/loader/navigation_early_hints_manager.cc
+++ b/content/browser/loader/navigation_early_hints_manager.cc
@@ -25,10 +25,10 @@
 #include "services/network/public/cpp/resource_request.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/public/mojom/connection_change_observer_client.mojom.h"
 #include "services/network/public/mojom/content_security_policy.mojom-shared.h"
 #include "services/network/public/mojom/fetch_api.mojom.h"
 #include "services/network/public/mojom/network_context.mojom.h"
-#include "services/network/public/mojom/reconnect_event_observer.mojom.h"
 #include "services/network/public/mojom/url_loader.mojom.h"
 #include "services/network/public/mojom/url_response_head.mojom.h"
 #include "third_party/blink/public/common/loader/network_utils.h"
diff --git a/content/browser/loader/navigation_early_hints_manager_unittest.cc b/content/browser/loader/navigation_early_hints_manager_unittest.cc
index e73fa8e..d0924d10 100644
--- a/content/browser/loader/navigation_early_hints_manager_unittest.cc
+++ b/content/browser/loader/navigation_early_hints_manager_unittest.cc
@@ -63,8 +63,8 @@
       const net::NetworkAnonymizationKey& network_anonymization_key,
       const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
       const std::optional<net::ConnectionKeepAliveConfig>& keepalive_config,
-      mojo::PendingRemote<network::mojom::ReconnectEventObserver>
-          reconnect_event_observer) override {
+      mojo::PendingRemote<network::mojom::ConnectionChangeObserverClient>
+          observer_client) override {
     preconnect_requests_.emplace_back(
         url,
         credentials_mode == network::mojom::CredentialsMode::kInclude ? true
diff --git a/content/browser/navigation_browsertest.cc b/content/browser/navigation_browsertest.cc
index 9778d6ed..67d1d8a 100644
--- a/content/browser/navigation_browsertest.cc
+++ b/content/browser/navigation_browsertest.cc
@@ -155,8 +155,9 @@
       override {
     intercepted_navigations_.push_back(navigation_request);
     intercepted_messages_.push_back(std::move(*params));
-    if (loop_)
+    if (loop_) {
       loop_->Quit();
+    }
     // Do not send the message to the RenderFrameHostImpl.
     return false;
   }
@@ -178,8 +179,9 @@
                          bool has_user_gesture,
                          std::optional<blink::scheduler::TaskAttributionId>
                              soft_navigation_heuristics_task_id) override {
-    if (quit_handler_)
+    if (quit_handler_) {
       std::move(quit_handler_).Run();
+    }
   }
 
   void set_quit_handler(base::OnceClosure handler) {
@@ -227,11 +229,13 @@
   // WebContentsDelegate's implementation:
   void NavigationStateChanged(WebContents* source,
                               InvalidateTypes changed_flags) override {
-    if (!(changed_flags & INVALIDATE_TYPE_URL))
+    if (!(changed_flags & INVALIDATE_TYPE_URL)) {
       return;
+    }
     url_ = source->GetVisibleURL();
-    if (on_url_invalidated_)
+    if (on_url_invalidated_) {
       std::move(on_url_invalidated_).Run();
+    }
   }
 
   void WaitUntilUrlInvalidated() {
@@ -256,8 +260,9 @@
 
  private:
   void DidStartNavigation(NavigationHandle* navigation_handle) override {
-    if (callback_)
+    if (callback_) {
       std::move(callback_).Run(navigation_handle);
+    }
   }
   base::OnceCallback<void(NavigationHandle*)> callback_;
 };
@@ -273,8 +278,9 @@
 
  private:
   void DidFinishNavigation(NavigationHandle* navigation_handle) override {
-    if (callback_)
+    if (callback_) {
       std::move(callback_).Run(navigation_handle);
+    }
   }
   base::OnceCallback<void(NavigationHandle*)> callback_;
 };
@@ -293,13 +299,12 @@
   return std::make_unique<content::TestNavigationThrottleInserter>(
       web_content,
       base::BindLambdaForTesting(
-          [&](NavigationHandle* handle) -> std::unique_ptr<NavigationThrottle> {
-            auto throttle = std::make_unique<TestNavigationThrottle>(handle);
+          [&](NavigationThrottleRegistry& registry) -> void {
+            auto throttle = std::make_unique<TestNavigationThrottle>(registry);
             throttle->SetResponse(TestNavigationThrottle::WILL_PROCESS_RESPONSE,
                                   TestNavigationThrottle::SYNCHRONOUS,
                                   NavigationThrottle::BLOCK_RESPONSE);
-
-            return throttle;
+            registry.AddThrottle(std::move(throttle));
           }));
 }
 
@@ -1638,7 +1643,8 @@
 #else
 #define MAYBE_IPCFlood_GoToEntryAtOffset IPCFlood_GoToEntryAtOffset
 #endif
-IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, MAYBE_IPCFlood_GoToEntryAtOffset) {
+IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
+                       MAYBE_IPCFlood_GoToEntryAtOffset) {
   GURL url(embedded_test_server()->GetURL("/title1.html"));
   EXPECT_TRUE(NavigateToURL(shell(), url));
 
@@ -1773,13 +1779,12 @@
   content::TestNavigationThrottleInserter throttle_inserter(
       web_contents(),
       base::BindLambdaForTesting(
-          [&](NavigationHandle* handle) -> std::unique_ptr<NavigationThrottle> {
-            auto throttle = std::make_unique<TestNavigationThrottle>(handle);
+          [&](NavigationThrottleRegistry& registry) -> void {
+            auto throttle = std::make_unique<TestNavigationThrottle>(registry);
             throttle->SetResponse(TestNavigationThrottle::WILL_PROCESS_RESPONSE,
                                   TestNavigationThrottle::SYNCHRONOUS,
                                   NavigationThrottle::CANCEL_AND_IGNORE);
-
-            return throttle;
+            registry.AddThrottle(std::move(throttle));
           }));
 
   // Insert enough iframes so that if sockets are not properly released: there
@@ -1808,15 +1813,16 @@
   content::TestNavigationThrottleInserter throttle_inserter(
       web_contents(),
       base::BindLambdaForTesting(
-          [](NavigationHandle* handle) -> std::unique_ptr<NavigationThrottle> {
-            auto throttle = std::make_unique<TestNavigationThrottle>(handle);
-            NavigationRequest* request = NavigationRequest::From(handle);
+          [](NavigationThrottleRegistry& registry) -> void {
+            auto throttle = std::make_unique<TestNavigationThrottle>(registry);
+            NavigationRequest* request =
+                NavigationRequest::From(&registry.GetNavigationHandle());
             throttle->SetCallback(TestNavigationThrottle::WILL_REDIRECT_REQUEST,
                                   base::BindLambdaForTesting([request]() {
                                     request->SetRequestHeader("header_name",
                                                               "header_value");
                                   }));
-            return throttle;
+            registry.AddThrottle(std::move(throttle));
           }));
 
   // 1) There is no "header_name" header in the initial request.
@@ -1851,9 +1857,10 @@
   content::TestNavigationThrottleInserter throttle_inserter(
       web_contents(),
       base::BindLambdaForTesting(
-          [](NavigationHandle* handle) -> std::unique_ptr<NavigationThrottle> {
-            auto throttle = std::make_unique<TestNavigationThrottle>(handle);
-            NavigationRequest* request = NavigationRequest::From(handle);
+          [](NavigationThrottleRegistry& registry) -> void {
+            auto throttle = std::make_unique<TestNavigationThrottle>(registry);
+            NavigationRequest* request =
+                NavigationRequest::From(&registry.GetNavigationHandle());
             throttle->SetCallback(TestNavigationThrottle::WILL_START_REQUEST,
                                   base::BindLambdaForTesting([request]() {
                                     request->SetRequestHeader("header_name",
@@ -1864,7 +1871,7 @@
                                     request->SetRequestHeader("header_name",
                                                               "other_value");
                                   }));
-            return throttle;
+            registry.AddThrottle(std::move(throttle));
           }));
 
   // 1) The header is added to the initial request.
@@ -1894,9 +1901,10 @@
   content::TestNavigationThrottleInserter throttle_inserter(
       web_contents(),
       base::BindLambdaForTesting(
-          [](NavigationHandle* handle) -> std::unique_ptr<NavigationThrottle> {
-            NavigationRequest* request = NavigationRequest::From(handle);
-            auto throttle = std::make_unique<TestNavigationThrottle>(handle);
+          [](NavigationThrottleRegistry& registry) -> void {
+            NavigationRequest* request =
+                NavigationRequest::From(&registry.GetNavigationHandle());
+            auto throttle = std::make_unique<TestNavigationThrottle>(registry);
             throttle->SetCallback(TestNavigationThrottle::WILL_START_REQUEST,
                                   base::BindLambdaForTesting([request]() {
                                     request->SetRequestHeader("header_name",
@@ -1906,7 +1914,7 @@
                                   base::BindLambdaForTesting([request]() {
                                     request->RemoveRequestHeader("header_name");
                                   }));
-            return throttle;
+            registry.AddThrottle(std::move(throttle));
           }));
 
   // 1) The header is added to the initial request.
@@ -2018,18 +2026,19 @@
   const std::string header_value = "value";
   content::TestNavigationThrottleInserter throttle_inserter(
       web_contents(),
-      base::BindLambdaForTesting([header_value](NavigationHandle* handle)
-                                     -> std::unique_ptr<NavigationThrottle> {
-        NavigationRequest* request = NavigationRequest::From(handle);
-        auto throttle = std::make_unique<TestNavigationThrottle>(handle);
-        throttle->SetCallback(
-            TestNavigationThrottle::WILL_START_REQUEST,
-            base::BindLambdaForTesting([request, header_value]() {
-              request->SetCorsExemptRequestHeader(kCorsHeaderName,
-                                                  header_value);
-            }));
-        return throttle;
-      }));
+      base::BindLambdaForTesting(
+          [header_value](NavigationThrottleRegistry& registry) -> void {
+            NavigationRequest* request =
+                NavigationRequest::From(&registry.GetNavigationHandle());
+            auto throttle = std::make_unique<TestNavigationThrottle>(registry);
+            throttle->SetCallback(
+                TestNavigationThrottle::WILL_START_REQUEST,
+                base::BindLambdaForTesting([request, header_value]() {
+                  request->SetCorsExemptRequestHeader(kCorsHeaderName,
+                                                      header_value);
+                }));
+            registry.AddThrottle(std::move(throttle));
+          }));
   shell()->LoadURL(embedded_test_server()->GetURL("/doc"));
   response.WaitForRequest();
   EXPECT_EQ(header_value, response.http_request()->headers.at(kCorsHeaderName));
@@ -2918,8 +2927,9 @@
 IN_PROC_BROWSER_TEST_F(NavigationCookiesBrowserTest,
                        CookiesInheritedAboutBlank) {
   // This test expects several cross-site navigation to happen.
-  if (!AreAllSitesIsolatedForTesting())
+  if (!AreAllSitesIsolatedForTesting()) {
     return;
+  }
 
   using Response = net::test_server::ControllableHttpResponse;
   Response response_1(https_server(), "/response_1");
@@ -3046,8 +3056,9 @@
 IN_PROC_BROWSER_TEST_F(NavigationCookiesBrowserTest,
                        CookiesInheritedAboutBlank2) {
   // This test expects several cross-site navigation to happen.
-  if (!AreAllSitesIsolatedForTesting())
+  if (!AreAllSitesIsolatedForTesting()) {
     return;
+  }
 
   using Response = net::test_server::ControllableHttpResponse;
   Response response_1(https_server(), "/response_1");
@@ -3657,15 +3668,17 @@
  public:
   GURL GetEffectiveURL(content::BrowserContext* browser_context,
                        const GURL& url) override {
-    if (effective_url_)
+    if (effective_url_) {
       return *effective_url_;
+    }
     return url;
   }
 
   bool IsSuitableHost(RenderProcessHost* process_host,
                       const GURL& site_url) override {
-    if (!disallowed_process_id_)
+    if (!disallowed_process_id_) {
       return true;
+    }
     return process_host->GetDeprecatedID() != disallowed_process_id_;
   }
 
@@ -4855,14 +4868,14 @@
   class ShutdownThrottle : public TaskRunnerDeferringThrottle,
                            WebContentsObserver {
    public:
-    explicit ShutdownThrottle(WebContents* web_contents,
-                              NavigationHandle* handle)
+    ShutdownThrottle(WebContents* web_contents,
+                     NavigationThrottleRegistry& registry)
         : TaskRunnerDeferringThrottle(
               base::SingleThreadTaskRunner::GetCurrentDefault(),
               /*defer_start=*/false,
               /*defer_redirect=*/false,
               /*defer_response=*/true,
-              handle),
+              registry),
           web_contents_(web_contents) {
       WebContentsObserver::Observe(web_contents_);
     }
@@ -4883,9 +4896,9 @@
   auto inserter = std::make_unique<TestNavigationThrottleInserter>(
       shell()->web_contents(),
       base::BindLambdaForTesting(
-          [&](NavigationHandle* handle) -> std::unique_ptr<NavigationThrottle> {
-            return std::make_unique<ShutdownThrottle>(shell()->web_contents(),
-                                                      handle);
+          [&](NavigationThrottleRegistry& registry) -> void {
+            registry.AddThrottle(std::make_unique<ShutdownThrottle>(
+                shell()->web_contents(), registry));
           }));
 
   class DoesNotReadyToCommitObserver : public WebContentsObserver {
@@ -5190,8 +5203,9 @@
       WebContents* current_contents =
           WebContents::FromRenderFrameHost(current_frame);
       DCHECK(current_contents);
-      if (base::Contains(visited_contents, current_contents))
+      if (base::Contains(visited_contents, current_contents)) {
         break;
+      }
       visited_contents.insert(current_contents);
 
       // Flush all the frames in the `current_contents's active page.
diff --git a/content/browser/preloading/prefetch/prefetch_type.h b/content/browser/preloading/prefetch/prefetch_type.h
index 1da4bae8..d72da1e 100644
--- a/content/browser/preloading/prefetch/prefetch_type.h
+++ b/content/browser/preloading/prefetch/prefetch_type.h
@@ -30,7 +30,6 @@
   PrefetchType& operator=(const PrefetchType& prefetch_type) = delete;
 
   bool operator==(const PrefetchType& rhs) const = default;
-  bool operator!=(const PrefetchType& rhs) const = default;
 
   PreloadingTriggerType trigger_type() const { return trigger_type_; }
 
diff --git a/content/browser/renderer_host/document_user_data_browsertest.cc b/content/browser/renderer_host/document_user_data_browsertest.cc
index 5ea257c2..3f8708e 100644
--- a/content/browser/renderer_host/document_user_data_browsertest.cc
+++ b/content/browser/renderer_host/document_user_data_browsertest.cc
@@ -677,12 +677,12 @@
   TestNavigationThrottleInserter throttle_inserter(
       shell()->web_contents(),
       base::BindLambdaForTesting(
-          [&](NavigationHandle* handle) -> std::unique_ptr<NavigationThrottle> {
-            auto throttle = std::make_unique<TestNavigationThrottle>(handle);
+          [&](NavigationThrottleRegistry& registry) -> void {
+            auto throttle = std::make_unique<TestNavigationThrottle>(registry);
             throttle->SetResponse(TestNavigationThrottle::WILL_START_REQUEST,
                                   TestNavigationThrottle::SYNCHRONOUS,
                                   NavigationThrottle::CANCEL_AND_IGNORE);
-            return throttle;
+            registry.AddThrottle(std::move(throttle));
           }));
 
   // 4) Try navigating to B.
diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc
index 62f577b2..02a2a827 100644
--- a/content/browser/renderer_host/navigation_request.cc
+++ b/content/browser/renderer_host/navigation_request.cc
@@ -172,10 +172,10 @@
 #include "services/network/public/cpp/supports_loading_mode/supports_loading_mode_parser.h"
 #include "services/network/public/cpp/url_loader_completion_status.h"
 #include "services/network/public/cpp/web_sandbox_flags.h"
+#include "services/network/public/mojom/connection_change_observer_client.mojom.h"
 #include "services/network/public/mojom/device_bound_sessions.mojom.h"
 #include "services/network/public/mojom/fetch_api.mojom.h"
 #include "services/network/public/mojom/permissions_policy/permissions_policy_feature.mojom-shared.h"
-#include "services/network/public/mojom/reconnect_event_observer.mojom.h"
 #include "services/network/public/mojom/supports_loading_mode.mojom.h"
 #include "services/network/public/mojom/url_response_head.mojom-forward.h"
 #include "services/network/public/mojom/url_response_head.mojom-shared.h"
@@ -1839,10 +1839,29 @@
   }
 
   if (from_begin_navigation_) {
-    // This is needed to have data URLs commit in the same SiteInstance as the
-    // initiating renderer.
+    // This is needed to commit data: and about: URLs in the same SiteInstance
+    // as the initiator frame. Note that this may not necessarily match the
+    // SiteInstance of the RenderFrameHost that sent the BeginNavigation IPC
+    // (i.e., `frame_tree_node_->current_frame_host()->GetSiteInstance()`) with
+    // SiteInstanceGroups, because some other frame in a different SiteInstance
+    // but same SiteInstanceGroup could've initiated this navigation.
     source_site_instance_ =
-        frame_tree_node->current_frame_host()->GetSiteInstance();
+        RenderFrameHostImpl::GetSourceSiteInstanceFromFrameToken(
+            base::OptionalToPtr(GetInitiatorFrameToken()),
+            GetInitiatorProcessId(),
+            frame_tree_node_->current_frame_host()->GetStoragePartition());
+
+    // If the lookup above failed (e.g., when no initiator frame token was
+    // provided), fall back to the navigating frame's current SiteInstance. This
+    // ensures that this renderer-initiated navigation still has a valid source
+    // SiteInstance corresponding to the renderer process that initiated the
+    // navigation, which is needed for certain security checks based on
+    // `source_site_instance_`, such as the CanRequestURL() check in
+    // `OnRequestRedirected()`.
+    if (!source_site_instance_) {
+      source_site_instance_ =
+          frame_tree_node_->current_frame_host()->GetSiteInstance();
+    }
 
     DCHECK(navigation_client.is_valid());
     SetNavigationClient(std::move(navigation_client));
diff --git a/content/browser/renderer_host/render_frame_host_impl_browsertest.cc b/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
index 8537028..6319452 100644
--- a/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
+++ b/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
@@ -105,8 +105,8 @@
 #include "services/network/public/cpp/network_switches.h"
 #include "services/network/public/cpp/resource_request.h"
 #include "services/network/public/cpp/simple_url_loader.h"
+#include "services/network/public/mojom/connection_change_observer_client.mojom.h"
 #include "services/network/public/mojom/network_context.mojom.h"
-#include "services/network/public/mojom/reconnect_event_observer.mojom.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
 #include "services/network/test/test_url_loader_factory.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
@@ -8720,8 +8720,8 @@
   TestNavigationThrottleInserter throttle_inserter(
       shell()->web_contents(),
       base::BindLambdaForTesting(
-          [&](NavigationHandle* handle) -> std::unique_ptr<NavigationThrottle> {
-            auto throttle = std::make_unique<TestNavigationThrottle>(handle);
+          [&](NavigationThrottleRegistry& registry) -> void {
+            auto throttle = std::make_unique<TestNavigationThrottle>(registry);
             throttle->SetCallback(
                 TestNavigationThrottle::WILL_PROCESS_RESPONSE,
                 base::BindLambdaForTesting([&]() {
@@ -8750,7 +8750,7 @@
                         EXPECT_FALSE(root->navigation_request());
                       }));
                 }));
-            return throttle;
+            registry.AddThrottle(std::move(throttle));
           }));
 
   // Navigate to another page, which will be cancelled by the shutdown
diff --git a/content/browser/renderer_host/render_frame_host_manager_browsertest.cc b/content/browser/renderer_host/render_frame_host_manager_browsertest.cc
index ddc399e..2cced0b 100644
--- a/content/browser/renderer_host/render_frame_host_manager_browsertest.cc
+++ b/content/browser/renderer_host/render_frame_host_manager_browsertest.cc
@@ -134,8 +134,9 @@
   bool deleted() const { return deleted_; }
 
   void Wait() {
-    if (deleted_)
+    if (deleted_) {
       return;
+    }
 
     message_loop_runner_->Run();
   }
@@ -164,16 +165,18 @@
 // WebUI URLs in tests.
 class RequestBlockingNavigationThrottle : public NavigationThrottle {
  public:
-  explicit RequestBlockingNavigationThrottle(NavigationHandle* handle)
-      : NavigationThrottle(handle) {}
+  explicit RequestBlockingNavigationThrottle(
+      NavigationThrottleRegistry& registry)
+      : NavigationThrottle(registry) {}
 
   RequestBlockingNavigationThrottle(const RequestBlockingNavigationThrottle&) =
       delete;
   RequestBlockingNavigationThrottle& operator=(
       const RequestBlockingNavigationThrottle&) = delete;
 
-  static std::unique_ptr<NavigationThrottle> Create(NavigationHandle* handle) {
-    return std::make_unique<RequestBlockingNavigationThrottle>(handle);
+  static void Create(NavigationThrottleRegistry& registry) {
+    registry.AddThrottle(
+        std::make_unique<RequestBlockingNavigationThrottle>(registry));
   }
 
  private:
@@ -1297,8 +1300,9 @@
     // object.
     switches.erase(switches::kDomAutomationController);
 
-    for (const auto& it : switches)
+    for (const auto& it : switches) {
       new_command_line.AppendSwitchNative(it.first, it.second);
+    }
 
     *command_line = new_command_line;
   }
@@ -1323,8 +1327,9 @@
 
   void Wait() {
     if (auto* entry = web_contents()->GetController().GetVisibleEntry()) {
-      if (entry && !entry->IsInitialEntry())
+      if (entry && !entry->IsInitialEntry()) {
         return;
+      }
     }
     run_loop_.Run();
   }
@@ -4079,8 +4084,9 @@
 
   // Sanity-check test setup: 2 frames share a renderer process, but are not in
   // a related browsing instance.
-  if (!AreAllSitesIsolatedForTesting())
+  if (!AreAllSitesIsolatedForTesting()) {
     EXPECT_EQ(tab1->GetProcess(), tab2->GetProcess());
+  }
   EXPECT_FALSE(
       tab1->GetSiteInstance()->IsRelatedSiteInstance(tab2->GetSiteInstance()));
 
@@ -4379,8 +4385,9 @@
 IN_PROC_BROWSER_TEST_P(RenderFrameHostManagerTest,
                        ErrorPageNavigationInMainFrame) {
   // This test is only valid if error page isolation is enabled.
-  if (!SiteIsolationPolicy::IsErrorPageIsolationEnabled(true))
+  if (!SiteIsolationPolicy::IsErrorPageIsolationEnabled(true)) {
     return;
+  }
 
   StartEmbeddedServer();
   GURL url(embedded_test_server()->GetURL("/title1.html"));
@@ -4505,8 +4512,9 @@
 IN_PROC_BROWSER_TEST_P(RenderFrameHostManagerTest,
                        ErrorPageNavigationInNewWindow) {
   // This test is only valid if error page isolation is enabled.
-  if (!SiteIsolationPolicy::IsErrorPageIsolationEnabled(true))
+  if (!SiteIsolationPolicy::IsErrorPageIsolationEnabled(true)) {
     return;
+  }
 
   StartEmbeddedServer();
   GURL error_url(embedded_test_server()->GetURL("/empty.html"));
@@ -4543,8 +4551,9 @@
 IN_PROC_BROWSER_TEST_P(RenderFrameHostManagerTest,
                        ErrorPageNavigationInUnrelatedWindows) {
   // This test is only valid if error page isolation is enabled.
-  if (!SiteIsolationPolicy::IsErrorPageIsolationEnabled(true))
+  if (!SiteIsolationPolicy::IsErrorPageIsolationEnabled(true)) {
     return;
+  }
 
   StartEmbeddedServer();
   GURL error_url(embedded_test_server()->GetURL("/empty.html"));
@@ -4597,8 +4606,9 @@
 // See https://crbug.com/840485.
 IN_PROC_BROWSER_TEST_P(RenderFrameHostManagerTest, ErrorPageNavigationReload) {
   // This test is only valid if error page isolation is enabled.
-  if (!SiteIsolationPolicy::IsErrorPageIsolationEnabled(true))
+  if (!SiteIsolationPolicy::IsErrorPageIsolationEnabled(true)) {
     return;
+  }
 
   StartEmbeddedServer();
   GURL start_url(embedded_test_server()->GetURL("/title1.html"));
@@ -5052,8 +5062,9 @@
 IN_PROC_BROWSER_TEST_P(RenderFrameHostManagerTest,
                        ErrorPageNavigationAfterError) {
   // This test is only valid if error page isolation is enabled.
-  if (!SiteIsolationPolicy::IsErrorPageIsolationEnabled(true))
+  if (!SiteIsolationPolicy::IsErrorPageIsolationEnabled(true)) {
     return;
+  }
 
   StartEmbeddedServer();
   GURL url(embedded_test_server()->GetURL("/title1.html"));
@@ -5129,8 +5140,9 @@
 IN_PROC_BROWSER_TEST_P(RenderFrameHostManagerTest,
                        ErrorPageNavigationReloadWithTerminatedProcess) {
   // This test is only valid if error page isolation is enabled.
-  if (!SiteIsolationPolicy::IsErrorPageIsolationEnabled(true))
+  if (!SiteIsolationPolicy::IsErrorPageIsolationEnabled(true)) {
     return;
+  }
 
   StartEmbeddedServer();
   GURL url(embedded_test_server()->GetURL("/title1.html"));
@@ -5183,8 +5195,9 @@
 IN_PROC_BROWSER_TEST_P(RenderFrameHostManagerTest,
                        ErrorPageNavigationHistoryNavigationFailure) {
   // This test is only valid if error page isolation is enabled.
-  if (!SiteIsolationPolicy::IsErrorPageIsolationEnabled(true))
+  if (!SiteIsolationPolicy::IsErrorPageIsolationEnabled(true)) {
     return;
+  }
 
   StartEmbeddedServer();
 
@@ -5230,8 +5243,9 @@
 IN_PROC_BROWSER_TEST_P(RenderFrameHostManagerTest,
                        ErrorPageNavigationHistoryNavigationSuccess) {
   // This test is only valid if error page isolation is enabled.
-  if (!SiteIsolationPolicy::IsErrorPageIsolationEnabled(true))
+  if (!SiteIsolationPolicy::IsErrorPageIsolationEnabled(true)) {
     return;
+  }
 
   StartEmbeddedServer();
   WebContents* web_contents = shell()->web_contents();
@@ -5284,8 +5298,9 @@
 IN_PROC_BROWSER_TEST_P(RenderFrameHostManagerTest,
                        ErrorPageNavigationToWebUIResourceWithError) {
   // This test is only valid if error page isolation is enabled.
-  if (!SiteIsolationPolicy::IsErrorPageIsolationEnabled(true))
+  if (!SiteIsolationPolicy::IsErrorPageIsolationEnabled(true)) {
     return;
+  }
 
   GURL webui_url = GetWebUIURL(kChromeUIGpuHost);
   GURL error_url(webui_url.Resolve("/foo"));
@@ -5319,8 +5334,9 @@
 IN_PROC_BROWSER_TEST_P(RenderFrameHostManagerTest,
                        ReloadWebUIErrorPageToValidWebUI) {
   // This test is only valid if error page isolation is enabled.
-  if (!SiteIsolationPolicy::IsErrorPageIsolationEnabled(true))
+  if (!SiteIsolationPolicy::IsErrorPageIsolationEnabled(true)) {
     return;
+  }
 
   GURL webui_url = GetWebUIURL(kChromeUIGpuHost);
 
@@ -5384,8 +5400,9 @@
  private:
   GURL GetEffectiveURL(BrowserContext* browser_context,
                        const GURL& url) override {
-    if (url.EqualsIgnoringRef(url_to_modify_))
+    if (url.EqualsIgnoringRef(url_to_modify_)) {
       return url_to_return_;
+    }
     return url;
   }
 
@@ -5558,8 +5575,9 @@
   // Returns right away if that request was already made.
   void WaitForMonitoredRequest() {
     base::AutoLock lock(lock_);
-    if (saw_request_url_)
+    if (saw_request_url_) {
       return;
+    }
 
     run_loop_ = std::make_unique<base::RunLoop>();
     {
@@ -5578,15 +5596,19 @@
 
   blink::mojom::SuddenTerminationDisablerType DisablerTypeForEvent(
       const std::string& event_name) {
-    if (event_name == "unload")
+    if (event_name == "unload") {
       return blink::mojom::SuddenTerminationDisablerType::kUnloadHandler;
-    if (event_name == "beforeunload")
+    }
+    if (event_name == "beforeunload") {
       return blink::mojom::SuddenTerminationDisablerType::kBeforeUnloadHandler;
-    if (event_name == "pagehide")
+    }
+    if (event_name == "pagehide") {
       return blink::mojom::SuddenTerminationDisablerType::kPageHideHandler;
-    if (event_name == "visibilitychange")
+    }
+    if (event_name == "visibilitychange") {
       return blink::mojom::SuddenTerminationDisablerType::
           kVisibilityChangeHandler;
+    }
     NOTREACHED();
   }
 
@@ -5658,15 +5680,17 @@
     // includes host+port).
     GURL requested_url = request.GetURL();
     auto it = request.headers.find("Host");
-    if (it != request.headers.end())
+    if (it != request.headers.end()) {
       requested_url = GURL("http://" + it->second + request.relative_url);
+    }
 
     base::AutoLock lock(lock_);
     if (!saw_request_url_ && request_url_ == requested_url) {
       saw_request_url_ = true;
       request_content_ = request.content;
-      if (run_loop_)
+      if (run_loop_) {
         run_loop_->Quit();
+      }
     }
   }
 
@@ -6133,8 +6157,9 @@
     RenderFrameHostManagerTest,
     ForegroundNavigationIsNeverBackgroundedWithSpareProcess) {
   // This test applies only when spare RenderProcessHost is enabled and in use.
-  if (!RenderProcessHostImpl::IsSpareProcessKeptAtAllTimes())
+  if (!RenderProcessHostImpl::IsSpareProcessKeptAtAllTimes()) {
     return;
+  }
 
   StartEmbeddedServer();
   WebContentsImpl* web_contents =
@@ -6250,10 +6275,11 @@
   scoped_refptr<SiteInstance> b_site_instance =
       web_contents->GetPrimaryMainFrame()->GetSiteInstance();
 
-  if (CanCrossSiteNavigationsProactivelySwapBrowsingInstances())
+  if (CanCrossSiteNavigationsProactivelySwapBrowsingInstances()) {
     EXPECT_FALSE(a_site_instance->IsRelatedSiteInstance(b_site_instance.get()));
-  else
+  } else {
     EXPECT_TRUE(a_site_instance->IsRelatedSiteInstance(b_site_instance.get()));
+  }
 }
 
 // Tests specific to the "default process" mode (which creates strict
@@ -6372,8 +6398,9 @@
                        CrashFrameReloadAndCheckProxy) {
   // This test explicitly requires multiple processes to be used. It won't mean
   // anything without SiteIsolation.
-  if (!AreAllSitesIsolatedForTesting())
+  if (!AreAllSitesIsolatedForTesting()) {
     return;
+  }
 
   // 1. Navigate to A1(B2, B3(B4), C5).
   StartEmbeddedServer();
@@ -6662,8 +6689,9 @@
 IN_PROC_BROWSER_TEST_P(RenderFrameHostManagerUnloadBrowserTest, NestedUnload) {
   // These tests require site isolation to trigger the (formerly problematic)
   // delayed detach of the remote frame when swapping in the new local frame.
-  if (!AreAllSitesIsolatedForTesting())
+  if (!AreAllSitesIsolatedForTesting()) {
     return;
+  }
 
   SetupCrossSiteRedirector(embedded_test_server());
   StartEmbeddedServer();
diff --git a/content/browser/resources/traces_internals/trace_recorder.css b/content/browser/resources/traces_internals/trace_recorder.css
index 3d7d2d0..bbb1e944 100644
--- a/content/browser/resources/traces_internals/trace_recorder.css
+++ b/content/browser/resources/traces_internals/trace_recorder.css
@@ -17,10 +17,6 @@
   overflow-y: auto;
 }
 
-:host h3 {
-  color: var(--cr-secondary-text-color);
-}
-
 #action-panel {
   display: flex;
   justify-content: flex-end;
diff --git a/content/browser/resources/traces_internals/trace_report.css b/content/browser/resources/traces_internals/trace_report.css
index f834680..edacfa8 100644
--- a/content/browser/resources/traces_internals/trace_report.css
+++ b/content/browser/resources/traces_internals/trace_report.css
@@ -12,7 +12,7 @@
 
 :host {
   display: grid;
-  grid-template-columns: repeat(3, 1fr) 3fr auto 250px auto;
+  grid-template-columns: repeat(3, 1fr) 3fr auto auto auto;
   grid-auto-flow: column;
   grid-column-gap: 12px;
   grid-row-gap: 0;
@@ -52,60 +52,30 @@
   cursor: copy;
 }
 
-.trace-upload-state-container {
-  white-space: nowrap;
-  display: flex;
-  flex-direction: column;
-  font-weight: bold;
-}
-
 .upload-state-card {
+  width: 250px;
+  white-space: nowrap;
+  font-weight: bold;
   border-radius: 1000px;
   font-size: 14px;
   display: flex;
   height: 34px;
   align-items: center;
   padding: 0 16px;
-}
-
-.upload-state-card:disabled {
-  opacity: var(--cr-disabled-opacity);
-  pointer-events: none;
-}
-
-.upload-state-card > iron-icon {
-  --iron-icon-width: 18px;
-  --iron-icon-height: 18px;
-  margin-inline-start: 16px;
-  margin-inline-end: 8px;
+  color: var(--cr-primary-text-color);
 }
 
 .state-default {
   border: none;
-  color: blue;
   background-color: lightblue;
 }
 
-.state-default > iron-icon {
-  --iron-icon-fill-color: blue;
-}
-
 .state-success {
   background-color: rgb(173, 255, 173);
-  color: rgb(6, 95, 6);
-}
-
-.state-success > iron-icon {
-  --iron-icon-fill-color: rgb(6, 95, 6);
 }
 
 .state-pending {
   background-color: orange;
-  color: rgb(204, 85, 0);
-}
-
-.state-pending > iron-icon {
-  --iron-icon-fill-color: rgb(204, 85, 0);
 }
 
 @media (prefers-color-scheme: dark) {
@@ -156,9 +126,7 @@
   color: var(--cr-secondary-text-color);
 }
 
-.date-creation-value,
-.trace-trigger-value,
-.trace-size-value {
+.value {
   color: var(--cr-primary-text-color)
 }
 
diff --git a/content/browser/resources/traces_internals/trace_report.html.ts b/content/browser/resources/traces_internals/trace_report.html.ts
index 58de19db..3e50302 100644
--- a/content/browser/resources/traces_internals/trace_report.html.ts
+++ b/content/browser/resources/traces_internals/trace_report.html.ts
@@ -12,7 +12,7 @@
   return html`
     ${this.isLoading_ ? html`<div class="spinner"></div>` :
     html`
-    <div class="trace-id-container">
+    <div>
       <button class="clickable-field copiable"
           title="${this.getTokenAsUuidString_()}"
           @click="${this.onCopyUuidClick_}">
@@ -20,13 +20,13 @@
       </button>
       <div class="info">Trace ID</div>
     </div>
-    <div class="trace-date-created-container">
-      <div class="date-creation-value">
+    <div>
+      <div class="value">
         ${this.dateToString_(this.trace.creationTime)}
       </div>
       <div class="info">Date created</div>
     </div>
-    <div class="trace-scenario-container">
+    <div>
       <button class="clickable-field copiable"
           title="${this.trace.scenarioName}"
           @click="${this.onCopyScenarioClick_}">
@@ -34,27 +34,25 @@
       </button>
       <div class="info">Scenario</div>
     </div>
-    <div class="trace-trigger-container">
+    <div>
       <button class="clickable-field copiable" title="${this.trace.uploadRuleName}"
           @click="${this.onCopyUploadRuleClick_}">
         ${this.trace.uploadRuleName}
       </button>
       ${this.trace.uploadRuleValue !== null ? html`
-        <div class="trace-trigger-value">
+        <div class="value">
           Value: ${this.trace.uploadRuleValue}
         </div>
       ` : nothing}
       <div class="info">Triggered rule</div>
     </div>
-    <div class="trace-size-container">
-      <div class="trace-size-value">${this.getTraceSize_()}</div>
+    <div>
+      <div class="value">${this.getTraceSize_()}</div>
       <div class="info">Uncompressed size</div>
     </div>
-    <div class="trace-upload-state-container">
-      <div class="upload-state-card ${this.getStateCssClass_()}"
-        title="${this.getStateText_()}">
-        ${this.getStateText_()}
-      </div>
+    <div class="upload-state-card ${this.getStateCssClass_()}"
+      title="${this.getStateText_()}">
+      ${this.getStateText_()}
     </div>
     <div class="actions-container">
       <cr-icon-button class="action-button" title="Upload Trace"
diff --git a/content/browser/resources/traces_internals/trace_report.ts b/content/browser/resources/traces_internals/trace_report.ts
index 6eda2ec..c10e33a 100644
--- a/content/browser/resources/traces_internals/trace_report.ts
+++ b/content/browser/resources/traces_internals/trace_report.ts
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.js';
-import 'chrome://resources/cr_elements/icons.html.js';
+import '//resources/cr_elements/cr_icon_button/cr_icon_button.js';
+import '//resources/cr_elements/icons.html.js';
 import './icons.html.js';
 
-import {assert} from 'chrome://resources/js/assert.js';
-import {CrLitElement} from 'chrome://resources/lit/v3_0/lit.rollup.js';
-import type {BigBuffer} from 'chrome://resources/mojo/mojo/public/mojom/base/big_buffer.mojom-webui.js';
-import type {Time} from 'chrome://resources/mojo/mojo/public/mojom/base/time.mojom-webui.js';
+import {assert} from '//resources/js/assert.js';
+import {CrLitElement} from '//resources/lit/v3_0/lit.rollup.js';
+import type {BigBuffer} from '//resources/mojo/mojo/public/mojom/base/big_buffer.mojom-webui.js';
+import type {Time} from '//resources/mojo/mojo/public/mojom/base/time.mojom-webui.js';
 
 import {getCss} from './trace_report.css.js';
 import {getHtml} from './trace_report.html.js';
diff --git a/content/browser/resources/traces_internals/trace_report_list.css b/content/browser/resources/traces_internals/trace_report_list.css
index a1ef54ac..0c1ac19 100644
--- a/content/browser/resources/traces_internals/trace_report_list.css
+++ b/content/browser/resources/traces_internals/trace_report_list.css
@@ -24,12 +24,7 @@
   border-bottom: var(--cr-separator-line);
 }
 
-.header h1 {
-  font-size: 2.7rem;
-  color: var(--cr-primary-text-color);
-}
-
-h1 > .trace-counter {
+.trace-counter {
   margin-inline-start: 14px;
 }
 
diff --git a/content/browser/resources/traces_internals/trace_report_list.html.ts b/content/browser/resources/traces_internals/trace_report_list.html.ts
index f4193b8..a2fe51b 100644
--- a/content/browser/resources/traces_internals/trace_report_list.html.ts
+++ b/content/browser/resources/traces_internals/trace_report_list.html.ts
@@ -18,7 +18,10 @@
   }
 
   return html`${this.traces_.map((traceReport: ClientTraceReport) => html`
-    <trace-report .trace="${traceReport}"></trace-report>`)}`;
+    <trace-report
+        .trace="${traceReport}"
+        @show-toast="${this.showToastHandler_}">
+    </trace-report>`)}`;
   // clang-format on
 }
 
diff --git a/content/browser/resources/traces_internals/trace_report_list.ts b/content/browser/resources/traces_internals/trace_report_list.ts
index febf8d3..a0e077a 100644
--- a/content/browser/resources/traces_internals/trace_report_list.ts
+++ b/content/browser/resources/traces_internals/trace_report_list.ts
@@ -3,18 +3,18 @@
 // found in the LICENSE file.
 
 import './trace_report.js';
-import 'chrome://resources/cr_elements/cr_toast/cr_toast.js';
-import 'chrome://resources/cr_elements/cr_button/cr_button.js';
-import 'chrome://resources/cr_elements/cr_icon/cr_icon.js';
-import 'chrome://resources/cr_elements/icons.html.js';
+import '//resources/cr_elements/cr_toast/cr_toast.js';
+import '//resources/cr_elements/cr_button/cr_button.js';
+import '//resources/cr_elements/cr_icon/cr_icon.js';
+import '//resources/cr_elements/icons.html.js';
 // <if expr="is_win">
-import 'chrome://resources/cr_elements/cr_toggle/cr_toggle.js';
+import '//resources/cr_elements/cr_toggle/cr_toggle.js';
 
 // </if>
 
-import type {CrToastElement} from 'chrome://resources/cr_elements/cr_toast/cr_toast.js';
-import {assert} from 'chrome://resources/js/assert.js';
-import {CrLitElement} from 'chrome://resources/lit/v3_0/lit.rollup.js';
+import type {CrToastElement} from '//resources/cr_elements/cr_toast/cr_toast.js';
+import {assert} from '//resources/js/assert.js';
+import {CrLitElement} from '//resources/lit/v3_0/lit.rollup.js';
 
 import type {ClientTraceReport} from './trace_report.mojom-webui.js';
 import {TraceReportBrowserProxy} from './trace_report_browser_proxy.js';
diff --git a/content/browser/resources/traces_internals/tracing_scenarios_config.css b/content/browser/resources/traces_internals/tracing_scenarios_config.css
index 2080790..fc7c5a1 100644
--- a/content/browser/resources/traces_internals/tracing_scenarios_config.css
+++ b/content/browser/resources/traces_internals/tracing_scenarios_config.css
@@ -15,9 +15,6 @@
   padding: 8px 12px;
 }
 
-:host h1 {
-}
-
 :host h3 {
   color: var(--cr-secondary-text-color);
 }
diff --git a/content/browser/resources/traces_internals/tracing_scenarios_config.ts b/content/browser/resources/traces_internals/tracing_scenarios_config.ts
index 530b012..95988a5 100644
--- a/content/browser/resources/traces_internals/tracing_scenarios_config.ts
+++ b/content/browser/resources/traces_internals/tracing_scenarios_config.ts
@@ -2,18 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://resources/cr_elements/cr_button/cr_button.js';
-import 'chrome://resources/cr_elements/cr_input/cr_input.js';
-import 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.js';
-import 'chrome://resources/cr_elements/cr_toast/cr_toast.js';
-import 'chrome://resources/cr_elements/cr_toggle/cr_toggle.js';
+import '//resources/cr_elements/cr_button/cr_button.js';
+import '//resources/cr_elements/cr_input/cr_input.js';
+import '//resources/cr_elements/cr_checkbox/cr_checkbox.js';
+import '//resources/cr_elements/cr_toast/cr_toast.js';
+import '//resources/cr_elements/cr_toggle/cr_toggle.js';
 
-import type {CrToastElement} from 'chrome://resources/cr_elements/cr_toast/cr_toast.js';
+import type {CrToastElement} from '//resources/cr_elements/cr_toast/cr_toast.js';
 // <if expr="is_win">
-import type {CrToggleElement} from 'chrome://resources/cr_elements/cr_toggle/cr_toggle.js';
+import type {CrToggleElement} from '//resources/cr_elements/cr_toggle/cr_toggle.js';
 // </if>
-import {CrLitElement} from 'chrome://resources/lit/v3_0/lit.rollup.js';
-import type {BigBuffer} from 'chrome://resources/mojo/mojo/public/mojom/base/big_buffer.mojom-webui.js';
+import {CrLitElement} from '//resources/lit/v3_0/lit.rollup.js';
+import type {BigBuffer} from '//resources/mojo/mojo/public/mojom/base/big_buffer.mojom-webui.js';
 
 import {TraceReportBrowserProxy} from './trace_report_browser_proxy.js';
 import {getCss} from './tracing_scenarios_config.css.js';
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index 1f54fa1..d4c3a185 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -9069,8 +9069,9 @@
   TestNavigationThrottleInserter navigation_throttle_inserter(
       web_contents(),
       base::BindRepeating(
-          [](NavigationHandle* handle) -> std::unique_ptr<NavigationThrottle> {
-            auto throttle = std::make_unique<TestNavigationThrottle>(handle);
+          [](NavigationThrottleRegistry& registry) -> void {
+            auto throttle = std::make_unique<TestNavigationThrottle>(registry);
+            auto* handle = &registry.GetNavigationHandle();
             throttle->SetCallback(
                 TestNavigationThrottle::WILL_PROCESS_RESPONSE,
                 base::BindLambdaForTesting([handle]() {
@@ -9085,7 +9086,7 @@
                       ->url_loader_client =
                       remote_to_be_dropped.BindNewPipeAndPassReceiver();
                 }));
-            return throttle;
+            registry.AddThrottle(std::move(throttle));
           }));
 
   // <object> fallback handling should never reach ReadyToCommitNavigation.
@@ -9153,8 +9154,9 @@
   TestNavigationThrottleInserter navigation_throttle_inserter(
       web_contents(),
       base::BindRepeating(
-          [](NavigationHandle* handle) -> std::unique_ptr<NavigationThrottle> {
-            auto throttle = std::make_unique<TestNavigationThrottle>(handle);
+          [](NavigationThrottleRegistry& registry) -> void {
+            auto throttle = std::make_unique<TestNavigationThrottle>(registry);
+            auto* handle = &registry.GetNavigationHandle();
             throttle->SetCallback(
                 TestNavigationThrottle::WILL_PROCESS_RESPONSE,
                 base::BindLambdaForTesting([handle]() {
@@ -9169,7 +9171,7 @@
                       ->url_loader_client =
                       remote_to_be_dropped.BindNewPipeAndPassReceiver();
                 }));
-            return throttle;
+            registry.AddThrottle(std::move(throttle));
           }));
 
   // <object> fallback handling should never reach ReadyToCommitNavigation.
diff --git a/content/browser/tracing/test_tracing_session.cc b/content/browser/tracing/test_tracing_session.cc
new file mode 100644
index 0000000..d84e32b
--- /dev/null
+++ b/content/browser/tracing/test_tracing_session.cc
@@ -0,0 +1,145 @@
+// 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/tracing/test_tracing_session.h"
+
+#include "base/task/thread_pool.h"
+#include "third_party/perfetto/protos/perfetto/config/data_source_config.gen.h"
+#include "third_party/perfetto/protos/perfetto/config/trace_config.gen.h"
+
+namespace content {
+
+TestTracingSession::TestTracingSession() = default;
+TestTracingSession::~TestTracingSession() = default;
+
+void TestTracingSession::Setup(const perfetto::TraceConfig& config, int fd) {
+  if (!config.data_sources().empty()) {
+    start_should_fail_ = config.data_sources()[0].config().name() == "Invalid";
+    should_spuriously_stop = config.data_sources()[0].config().name() == "Stop";
+  }
+}
+
+void TestTracingSession::Start() {
+  if (start_should_fail_) {
+    base::ThreadPool::PostTask(
+        FROM_HERE,
+        {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
+         base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
+        base::BindOnce(
+            [](std::function<void(perfetto::TracingError)>  // nocheck
+                   on_error_callback) {
+              on_error_callback(
+                  {perfetto::TracingError::kTracingFailed, "error"});
+            },
+            on_error_callback_));
+    return;
+  }
+  if (should_spuriously_stop) {
+    Stop();
+    return;
+  }
+  // perfetto::TracingSession runs callbacks from its own background thread.
+  base::ThreadPool::PostTask(
+      FROM_HERE,
+      {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
+       base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
+      base::BindOnce(
+          [](std::function<void()> on_start_callback) {  // nocheck
+            on_start_callback();
+          },
+          on_start_callback_));
+}
+
+void TestTracingSession::StartBlocking() {
+  NOTIMPLEMENTED();
+}
+
+void TestTracingSession::SetOnStartCallback(
+    std::function<void()> on_start) {  // nocheck
+  on_start_callback_ = on_start;
+}
+
+void TestTracingSession::SetOnErrorCallback(
+    std::function<void(perfetto::TracingError)> on_error)  // nocheck
+{
+  on_error_callback_ = on_error;
+}
+
+void TestTracingSession::Flush(std::function<void(bool)>,  // nocheck
+                               uint32_t timeout_ms) {
+  NOTIMPLEMENTED();
+}
+
+void TestTracingSession::Stop() {
+  // perfetto::TracingSession runs callbacks from its own background thread.
+  base::ThreadPool::PostTask(
+      FROM_HERE,
+      {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
+       base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
+      base::BindOnce(
+          [](std::function<void()> on_stop_callback) {  // nocheck
+            on_stop_callback();
+          },
+          on_stop_callback_));
+}
+
+void TestTracingSession::CloneTrace(CloneTraceArgs args,
+                                    CloneTraceCallback on_session_cloned) {
+  // perfetto::TracingSession runs callbacks from its own background thread.
+  base::ThreadPool::PostTask(
+      FROM_HERE,
+      {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
+       base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
+      base::BindOnce(
+          [](CloneTraceCallback on_session_cloned) {  // nocheck
+            on_session_cloned(
+                {true, "", kClonedSessionId.low(), kClonedSessionId.high()});
+          },
+          on_session_cloned));
+}
+
+void TestTracingSession::StopBlocking() {
+  NOTIMPLEMENTED();
+}
+
+void TestTracingSession::SetOnStopCallback(
+    std::function<void()> on_stop) {  // nocheck
+  on_stop_callback_ = on_stop;
+}
+
+void TestTracingSession::ChangeTraceConfig(const perfetto::TraceConfig&) {
+  NOTIMPLEMENTED();
+}
+void TestTracingSession::ReadTrace(ReadTraceCallback read_callback) {
+  base::ThreadPool::PostTask(
+      FROM_HERE,
+      {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
+       base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
+      base::BindOnce(
+          [](ReadTraceCallback read_callback) {  // nocheck
+            std::string trace_content = "this is a trace";
+            read_callback({trace_content.data(), trace_content.size(),
+                           /*has_more=*/false});
+          },
+          read_callback));
+}
+void TestTracingSession::GetTraceStats(GetTraceStatsCallback stats_callback) {
+  base::ThreadPool::PostTask(
+      FROM_HERE,
+      {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
+       base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
+      base::BindOnce(
+          [](GetTraceStatsCallback stats_callback) {  // nocheck
+            GetTraceStatsCallbackArgs args;
+            args.success = true;
+            stats_callback(args);
+          },
+          stats_callback));
+}
+
+void TestTracingSession::QueryServiceState(QueryServiceStateCallback) {
+  NOTIMPLEMENTED();
+}
+
+}  // namespace content
diff --git a/content/browser/tracing/test_tracing_session.h b/content/browser/tracing/test_tracing_session.h
new file mode 100644
index 0000000..8ea18346
--- /dev/null
+++ b/content/browser/tracing/test_tracing_session.h
@@ -0,0 +1,60 @@
+// 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_TRACING_TEST_TRACING_SESSION_H_
+#define CONTENT_BROWSER_TRACING_TEST_TRACING_SESSION_H_
+
+#include <functional>
+
+#include "base/token.h"
+#include "third_party/perfetto/include/perfetto/tracing/tracing.h"
+
+namespace content {
+
+// Fake perfetto::TracingSession.
+class TestTracingSession : public perfetto::TracingSession {
+ public:
+  static constexpr base::Token kClonedSessionId = base::Token(0xAB, 0xCD);
+
+  TestTracingSession();
+  ~TestTracingSession() override;
+
+  void Setup(const perfetto::TraceConfig& config, int fd = -1) override;
+  void Start() override;
+  void StartBlocking() override;
+
+  void SetOnStartCallback(std::function<void()> on_start) override;  // nocheck
+
+  void SetOnErrorCallback(
+      std::function<void(perfetto::TracingError)> on_error)  // nocheck
+      override;
+
+  void Flush(std::function<void(bool)>,  // nocheck
+             uint32_t timeout_ms = 0) override;
+
+  void Stop() override;
+
+  void CloneTrace(CloneTraceArgs args,
+                  CloneTraceCallback on_session_cloned) override;
+
+  void StopBlocking() override;
+
+  void SetOnStopCallback(std::function<void()> on_stop) override;  // nocheck
+
+  void ChangeTraceConfig(const perfetto::TraceConfig&) override;
+  void ReadTrace(ReadTraceCallback read_callback) override;
+  void GetTraceStats(GetTraceStatsCallback) override;
+  void QueryServiceState(QueryServiceStateCallback) override;
+
+ private:
+  std::function<void()> on_start_callback_;                        // nocheck
+  std::function<void()> on_stop_callback_;                         // nocheck
+  std::function<void(perfetto::TracingError)> on_error_callback_;  // nocheck
+  bool start_should_fail_ = false;
+  bool should_spuriously_stop = false;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_TRACING_TEST_TRACING_SESSION_H_
diff --git a/content/browser/tracing/trace_report/trace_report.mojom b/content/browser/tracing/trace_report/trace_report.mojom
index d908dff2..99c65615 100644
--- a/content/browser/tracing/trace_report/trace_report.mojom
+++ b/content/browser/tracing/trace_report/trace_report.mojom
@@ -80,6 +80,25 @@
 
 // Browser-side handler for requests from WebUI page.
 interface PageHandler {
+  // Starts the tracing session from a serialized proto config defined as
+  // `perfetto.protos.TraceConfig`. This isn't using ProtoWrapper
+  // because it's not compatible with cppgen proto headers used by perfetto.
+  // PageHandler supports only one tracing session at a time.
+  StartTraceSession(mojo_base.mojom.BigBuffer config_pb) => (bool success);
+
+  // Clones the current tracing session in read-only mode and returns the
+  // captured trace snapshot, or null if the operation fails.
+  CloneTraceSession() => (mojo_base.mojom.BigBuffer? trace);
+
+  // Stops the current tracing session. If successful, this will read the trace
+  // payload and return it via Page::OnTraceComplete().
+  StopTraceSession() => (bool success);
+
+  // Returns buffer usage stats for the current tracing session. PageHandler
+  // supports only one request at a time.
+  GetBufferUsage()
+      => (bool success, float percent_full, bool data_loss);
+
   // Get visual data for all the traces currently stored locally.
   GetAllTraceReports() => (array<ClientTraceReport> reports);
 
@@ -150,4 +169,7 @@
 
 // WebUI-side handler for requests from the browser.
 interface Page {
+  // When a tracing session completes, either from calling PageHandler::Stop()
+  // or by itself, this is invoked with the serialized trace payload.
+  OnTraceComplete(mojo_base.mojom.BigBuffer? trace);
 };
diff --git a/content/browser/tracing/trace_report/trace_report_handler.cc b/content/browser/tracing/trace_report/trace_report_handler.cc
index 17b4b6b..16f68999 100644
--- a/content/browser/tracing/trace_report/trace_report_handler.cc
+++ b/content/browser/tracing/trace_report/trace_report_handler.cc
@@ -21,6 +21,8 @@
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
+#include "services/tracing/public/cpp/perfetto/perfetto_session.h"
+#include "third_party/perfetto/protos/perfetto/config/trace_config.gen.h"
 #include "third_party/snappy/src/snappy.h"
 
 #if BUILDFLAG(IS_WIN)
@@ -36,10 +38,73 @@
 
 namespace content {
 
+namespace {
+
+std::optional<perfetto::protos::gen::TraceConfig> ParseSerializedTraceConfig(
+    const base::span<const uint8_t>& config_bytes) {
+  perfetto::protos::gen::TraceConfig config;
+  if (config_bytes.empty()) {
+    return std::nullopt;
+  }
+  if (config.ParseFromArray(config_bytes.data(), config_bytes.size())) {
+    return config;
+  }
+  return std::nullopt;
+}
+
+class TraceReader : public base::RefCountedThreadSafe<TraceReader> {
+ public:
+  explicit TraceReader(
+      std::unique_ptr<perfetto::TracingSession> tracing_session,
+      base::OnceCallback<void(std::optional<mojo_base::BigBuffer>)>
+          on_trace_data_complete,
+      scoped_refptr<base::SequencedTaskRunner> task_runner)
+      : tracing_session(std::move(tracing_session)),
+        on_trace_data_complete(std::move(on_trace_data_complete)),
+        task_runner(std::move(task_runner)) {}
+
+  std::unique_ptr<perfetto::TracingSession> tracing_session;
+  std::string serialized_trace;
+  base::OnceCallback<void(std::optional<mojo_base::BigBuffer>)>
+      on_trace_data_complete;
+  scoped_refptr<base::SequencedTaskRunner> task_runner;
+
+  static void ReadTrace(scoped_refptr<TraceReader> reader) {
+    reader->tracing_session->ReadTrace(
+        [reader](perfetto::TracingSession::ReadTraceCallbackArgs args) mutable {
+          if (args.size) {
+            reader->serialized_trace.append(args.data, args.size);
+          }
+          if (!args.has_more) {
+            reader->task_runner->PostTask(
+                FROM_HERE,
+                base::BindOnce(
+                    [](base::OnceCallback<void(
+                           std::optional<mojo_base::BigBuffer>)> callback,
+                       std::string&& serialized_trace) {
+                      base::span<const char> trace_span(serialized_trace);
+                      std::move(callback).Run(
+                          mojo_base::BigBuffer(base::as_bytes(trace_span)));
+                    },
+                    std::move(reader->on_trace_data_complete),
+                    std::move(reader->serialized_trace)));
+          }
+        });
+  }
+
+ private:
+  friend class base::RefCountedThreadSafe<TraceReader>;
+
+  ~TraceReader() = default;
+};
+
+}  // namespace
+
 TraceReportHandler::TraceReportHandler(
     mojo::PendingReceiver<trace_report::mojom::PageHandler> receiver,
     mojo::PendingRemote<trace_report::mojom::Page> page)
-    : receiver_(this, std::move(receiver)),
+    : task_runner_(base::SequencedTaskRunner::GetCurrentDefault()),
+      receiver_(this, std::move(receiver)),
       page_(std::move(page)),
       trace_upload_list_(BackgroundTracingManagerImpl::GetInstance()),
       background_tracing_manager_(BackgroundTracingManagerImpl::GetInstance()),
@@ -55,7 +120,8 @@
     TraceUploadList& trace_upload_list,
     BackgroundTracingManagerImpl& background_tracing_manager,
     TracingDelegate* tracing_delegate)
-    : receiver_(this, std::move(receiver)),
+    : task_runner_(base::SequencedTaskRunner::GetCurrentDefault()),
+      receiver_(this, std::move(receiver)),
       page_(std::move(page)),
       trace_upload_list_(trace_upload_list),
       background_tracing_manager_(background_tracing_manager),
@@ -97,6 +163,134 @@
                 std::move(callback)));
 }
 
+void TraceReportHandler::StartTraceSession(mojo_base::BigBuffer config_pb,
+                                           StartTraceSessionCallback callback) {
+  if (tracing_session_) {
+    std::move(callback).Run(false);
+    return;
+  }
+
+  start_callback_ = std::move(callback);
+  tracing_session_ = CreateTracingSession();
+
+  auto trace_config = ParseSerializedTraceConfig(base::span(config_pb));
+  if (!trace_config ||
+      !tracing::AdaptPerfettoConfigForChrome(&(*trace_config))) {
+    std::move(start_callback_).Run(false);
+    return;
+  }
+  session_unguessable_name_ = base::UnguessableToken::Create();
+  trace_config->set_unique_session_name(session_unguessable_name_.ToString());
+  tracing_session_->Setup(*trace_config);
+  tracing_session_->SetOnStartCallback(
+      [task_runner = task_runner_, weak_ptr = weak_factory_.GetWeakPtr()]() {
+        task_runner->PostTask(
+            FROM_HERE,
+            base::BindOnce(&TraceReportHandler::OnTracingStart, weak_ptr));
+      });
+  tracing_session_->SetOnErrorCallback(
+      [task_runner = task_runner_,
+       weak_ptr = weak_factory_.GetWeakPtr()](perfetto::TracingError error) {
+        task_runner->PostTask(
+            FROM_HERE, base::BindOnce(&TraceReportHandler::OnTracingError,
+                                      weak_ptr, error));
+      });
+  tracing_session_->SetOnStopCallback(
+      [task_runner = task_runner_, weak_ptr = weak_factory_.GetWeakPtr()]() {
+        task_runner->PostTask(
+            FROM_HERE,
+            base::BindOnce(&TraceReportHandler::OnTracingStop, weak_ptr));
+      });
+  tracing_session_->Start();
+}
+
+void TraceReportHandler::CloneTraceSession(CloneTraceSessionCallback callback) {
+  if (!tracing_session_) {
+    std::move(callback).Run(std::nullopt);
+    return;
+  }
+  auto cloned_session = CreateTracingSession();
+  auto trace_reader = base::MakeRefCounted<TraceReader>(
+      std::move(cloned_session), std::move(callback), task_runner_);
+  perfetto::TracingSession::CloneTraceArgs args{
+      .unique_session_name = session_unguessable_name_.ToString()};
+  trace_reader->tracing_session->CloneTrace(
+      args,
+      [trace_reader](perfetto::TracingSession::CloneTraceCallbackArgs args) {
+        if (!args.success) {
+          std::move(trace_reader->on_trace_data_complete).Run(std::nullopt);
+          return;
+        }
+        TraceReader::ReadTrace(std::move(trace_reader));
+      });
+}
+
+void TraceReportHandler::StopTraceSession(StopTraceSessionCallback callback) {
+  if (!tracing_session_) {
+    std::move(callback).Run(false);
+    return;
+  }
+  stop_callback_ = std::move(callback);
+  tracing_session_->Stop();
+}
+
+void TraceReportHandler::GetBufferUsage(GetBufferUsageCallback callback) {
+  if (!tracing_session_ || on_buffer_usage_callback_) {
+    std::move(callback).Run(false, 0, false);
+    return;
+  }
+
+  on_buffer_usage_callback_ = std::move(callback);
+  tracing_session_->GetTraceStats(
+      [task_runner = task_runner_, weak_ptr = weak_factory_.GetWeakPtr()](
+          perfetto::TracingSession::GetTraceStatsCallbackArgs args) {
+        tracing::ReadTraceStats(
+            args, base::BindOnce(&TraceReportHandler::OnBufferUsage, weak_ptr),
+            task_runner);
+      });
+}
+
+void TraceReportHandler::OnBufferUsage(bool success,
+                                       float percent_full,
+                                       bool data_loss) {
+  if (on_buffer_usage_callback_) {
+    std::move(on_buffer_usage_callback_).Run(success, percent_full, data_loss);
+  }
+}
+
+void TraceReportHandler::OnTracingError(perfetto::TracingError error) {
+  if (start_callback_) {
+    std::move(start_callback_).Run(false);
+  }
+  if (stop_callback_) {
+    std::move(stop_callback_).Run(false);
+  }
+  page_->OnTraceComplete(std::nullopt);
+}
+
+void TraceReportHandler::OnTracingStop() {
+  if (stop_callback_) {
+    std::move(stop_callback_).Run(true);
+  }
+  auto trace_reader = base::MakeRefCounted<TraceReader>(
+      std::move(tracing_session_),
+      base::BindOnce(&TraceReportHandler::OnTraceComplete,
+                     weak_factory_.GetWeakPtr()),
+      task_runner_);
+  TraceReader::ReadTrace(std::move(trace_reader));
+}
+
+void TraceReportHandler::OnTracingStart() {
+  if (start_callback_) {
+    std::move(start_callback_).Run(true);
+  }
+}
+
+void TraceReportHandler::OnTraceComplete(
+    std::optional<mojo_base::BigBuffer> serialized_trace) {
+  page_->OnTraceComplete(std::move(serialized_trace));
+}
+
 void TraceReportHandler::GetAllTraceReports(
     GetAllTraceReportsCallback callback) {
   trace_upload_list_->GetAllTraceReports(
@@ -171,7 +365,7 @@
     mojo_base::BigBuffer config_pb,
     SetScenariosConfigFromBufferCallback callback) {
   auto field_tracing_config =
-      tracing::ParseSerializedTracingScenariosConfig(config_pb.byte_span());
+      tracing::ParseSerializedTracingScenariosConfig(base::span(config_pb));
   if (!field_tracing_config) {
     std::move(callback).Run(false);
     return;
@@ -259,4 +453,9 @@
 }
 #endif  // BUILDFLAG(IS_WIN)
 
+std::unique_ptr<perfetto::TracingSession>
+TraceReportHandler::CreateTracingSession() {
+  return perfetto::Tracing::NewTrace(perfetto::BackendType::kCustomBackend);
+}
+
 }  // namespace content
diff --git a/content/browser/tracing/trace_report/trace_report_handler.h b/content/browser/tracing/trace_report/trace_report_handler.h
index 354f29e..f861f1d 100644
--- a/content/browser/tracing/trace_report/trace_report_handler.h
+++ b/content/browser/tracing/trace_report/trace_report_handler.h
@@ -8,6 +8,7 @@
 #include "base/memory/raw_ref.h"
 #include "base/task/task_runner.h"
 #include "base/token.h"
+#include "base/unguessable_token.h"
 #include "build/build_config.h"
 #include "content/browser/tracing/background_tracing_manager_impl.h"
 #include "content/browser/tracing/trace_report/trace_report.mojom.h"
@@ -28,19 +29,17 @@
       mojo::PendingReceiver<trace_report::mojom::PageHandler> receiver,
       mojo::PendingRemote<trace_report::mojom::Page> page);
 
-  TraceReportHandler(
-      mojo::PendingReceiver<trace_report::mojom::PageHandler> receiver,
-      mojo::PendingRemote<trace_report::mojom::Page> page,
-      TraceUploadList& trace_upload_list,
-      BackgroundTracingManagerImpl& background_tracing_manager,
-      TracingDelegate* tracing_delegate);
-
   TraceReportHandler(const TraceReportHandler&) = delete;
   TraceReportHandler& operator=(const TraceReportHandler&) = delete;
   ~TraceReportHandler() override;
 
   // trace_report::mojom::TraceReportHandler:
   // Get all the trace report currently stored locally
+  void StartTraceSession(mojo_base::BigBuffer config_pb,
+                         StartTraceSessionCallback callback) override;
+  void CloneTraceSession(CloneTraceSessionCallback callback) override;
+  void StopTraceSession(StopTraceSessionCallback callback) override;
+  void GetBufferUsage(GetBufferUsageCallback callback) override;
   void GetAllTraceReports(GetAllTraceReportsCallback callback) override;
   void DeleteSingleTrace(const base::Token& uuid,
                          DeleteSingleTraceCallback callback) override;
@@ -73,6 +72,16 @@
   void DisableSystemTracing(DisableSystemTracingCallback callback) override;
 #endif  // BUILDFLAG(IS_WIN)
 
+ protected:
+  TraceReportHandler(
+      mojo::PendingReceiver<trace_report::mojom::PageHandler> receiver,
+      mojo::PendingRemote<trace_report::mojom::Page> page,
+      TraceUploadList& trace_upload_list,
+      BackgroundTracingManagerImpl& background_tracing_manager,
+      TracingDelegate* tracing_delegate);
+
+  virtual std::unique_ptr<perfetto::TracingSession> CreateTracingSession();
+
  private:
   void OnGetAllReportsTaskComplete(GetAllTraceReportsCallback callback,
                                    std::vector<ClientTraceReport> results);
@@ -80,14 +89,27 @@
       const perfetto::protos::gen::ChromeFieldTracingConfig& config);
   void MaybeSetupPresetTracingFromFieldTrial();
 
+  void OnTracingError(perfetto::TracingError error);
+  void OnTracingStop();
+  void OnTracingStart();
+  void OnTraceComplete(std::optional<mojo_base::BigBuffer>);
+  void OnBufferUsage(bool success, float percent_full, bool data_loss);
+
+  scoped_refptr<base::SequencedTaskRunner> task_runner_;
   mojo::Receiver<trace_report::mojom::PageHandler> receiver_;
-  mojo::PendingRemote<trace_report::mojom::Page> page_;
+  mojo::Remote<trace_report::mojom::Page> page_;
 
   // Used to perform actions with on a single trace_report_database instance.
   const raw_ref<TraceUploadList> trace_upload_list_;
   const raw_ref<BackgroundTracingManagerImpl> background_tracing_manager_;
   const raw_ptr<TracingDelegate> tracing_delegate_;
 
+  base::UnguessableToken session_unguessable_name_;
+  std::unique_ptr<perfetto::TracingSession> tracing_session_;
+  StartTraceSessionCallback start_callback_;
+  StopTraceSessionCallback stop_callback_;
+  GetBufferUsageCallback on_buffer_usage_callback_;
+
   base::WeakPtrFactory<TraceReportHandler> weak_factory_{this};
 };
 
diff --git a/content/browser/tracing/trace_report/trace_report_handler_unittest.cc b/content/browser/tracing/trace_report/trace_report_handler_unittest.cc
index 3fdb51c8..6f188a6a 100644
--- a/content/browser/tracing/trace_report/trace_report_handler_unittest.cc
+++ b/content/browser/tracing/trace_report/trace_report_handler_unittest.cc
@@ -4,8 +4,13 @@
 
 #include "content/browser/tracing/trace_report/trace_report_handler.h"
 
+#include "base/base_paths.h"
+#include "base/path_service.h"
+#include "base/test/gmock_callback_support.h"
 #include "base/test/mock_callback.h"
+#include "base/test/test_proto_loader.h"
 #include "base/token.h"
+#include "content/browser/tracing/test_tracing_session.h"
 #include "content/browser/tracing/trace_report/trace_report.mojom.h"
 #include "content/browser/tracing/trace_report/trace_upload_list.h"
 #include "content/public/browser/background_tracing_manager.h"
@@ -20,6 +25,22 @@
 
 namespace content {
 
+using testing::_;
+
+perfetto::protos::gen::TraceConfig ParseTraceConfigFromText(
+    const std::string& proto_text) {
+  base::TestProtoLoader config_loader(
+      base::PathService::CheckedGet(base::DIR_GEN_TEST_DATA_ROOT)
+          .Append(FILE_PATH_LITERAL("third_party/perfetto/protos/perfetto/"
+                                    "config/config.descriptor")),
+      "perfetto.protos.TraceConfig");
+  std::string serialized_message;
+  config_loader.ParseFromText(proto_text, serialized_message);
+  perfetto::protos::gen::TraceConfig destination;
+  destination.ParseFromString(serialized_message);
+  return destination;
+}
+
 class FakeTraceUploadList : public TraceUploadList {
  public:
   // Functions we want to intercept.
@@ -50,6 +71,11 @@
     return receiver_.BindNewPipeAndPassRemote();
   }
 
+  MOCK_METHOD(void,
+              OnTraceComplete,
+              (std::optional<mojo_base::BigBuffer>),
+              (override));
+
   mojo::Receiver<trace_report::mojom::Page> receiver_{this};
 };
 
@@ -73,6 +99,26 @@
 #endif
 };
 
+class TraceReportHandlerForTesting : public TraceReportHandler {
+ public:
+  TraceReportHandlerForTesting(
+      mojo::PendingReceiver<trace_report::mojom::PageHandler> receiver,
+      mojo::PendingRemote<trace_report::mojom::Page> page,
+      TraceUploadList& trace_upload_list,
+      BackgroundTracingManagerImpl& background_tracing_manager,
+      TracingDelegate* tracing_delegate)
+      : TraceReportHandler(std::move(receiver),
+                           std::move(page),
+                           trace_upload_list,
+                           background_tracing_manager,
+                           tracing_delegate) {}
+
+ protected:
+  std::unique_ptr<perfetto::TracingSession> CreateTracingSession() override {
+    return std::make_unique<TestTracingSession>();
+  }
+};
+
 // A fixture to test TraceReportHandler.
 class TraceReportHandlerTest : public testing::Test {
  public:
@@ -84,7 +130,7 @@
         std::make_unique<BackgroundTracingManagerImpl>();
     // Expect the Database to be opened before executing each test.
     EXPECT_CALL(fake_trace_upload_list_, OpenDatabaseIfExists());
-    handler_ = std::make_unique<TraceReportHandler>(
+    handler_ = std::make_unique<TraceReportHandlerForTesting>(
         mojo::PendingReceiver<trace_report::mojom::PageHandler>(),
         mock_page_.BindAndGetRemote(), fake_trace_upload_list_,
         *background_tracing_manager_, &mock_tracing_delegate_);
@@ -99,6 +145,154 @@
   std::unique_ptr<TraceReportHandler> handler_;
 };
 
+TEST_F(TraceReportHandlerTest, TracingStartStop) {
+  auto trace_config =
+      ParseTraceConfigFromText(R"pb(
+        data_sources: { config: { name: "org.chromium.trace_metadata" } }
+      )pb")
+          .SerializeAsString();
+  base::MockCallback<TraceReportHandler::StartTraceSessionCallback>
+      start_callback;
+  handler_->StartTraceSession(mojo_base::BigBuffer(base::as_bytes(
+                                  base::span<const char>(trace_config))),
+                              start_callback.Get());
+
+  {
+    base::RunLoop run_loop_start;
+    EXPECT_CALL(start_callback, Run(true))
+        .WillOnce(base::test::RunOnceClosure(run_loop_start.QuitClosure()));
+    run_loop_start.Run();
+  }
+
+  base::MockCallback<TraceReportHandler::StopTraceSessionCallback>
+      stop_callback;
+  handler_->StopTraceSession(stop_callback.Get());
+  {
+    base::RunLoop run_loop_stop;
+    EXPECT_CALL(stop_callback, Run(true)).Times(1);
+
+    EXPECT_CALL(mock_page_,
+                OnTraceComplete(testing::Truly(
+                    [](const std::optional<mojo_base::BigBuffer>& trace) {
+                      return base::as_string_view(base::as_chars(
+                                 base::span(*trace))) == "this is a trace";
+                    })))
+        .WillOnce(base::test::RunOnceClosure(run_loop_stop.QuitClosure()));
+
+    run_loop_stop.Run();
+  }
+}
+
+TEST_F(TraceReportHandlerTest, TracingTimer) {
+  auto trace_config = ParseTraceConfigFromText(R"pb(
+                        data_sources: { config: { name: "Stop" } }
+                      )pb")
+                          .SerializeAsString();
+  handler_->StartTraceSession(mojo_base::BigBuffer(base::as_bytes(
+                                  base::span<const char>(trace_config))),
+                              base::DoNothing());
+
+  base::RunLoop run_loop;
+
+  EXPECT_CALL(
+      mock_page_,
+      OnTraceComplete(
+          testing::Truly([](const std::optional<mojo_base::BigBuffer>& trace) {
+            return base::as_string_view(base::as_chars(base::span(*trace))) ==
+                   "this is a trace";
+          })))
+      .WillOnce(base::test::RunOnceClosure(run_loop.QuitClosure()));
+  run_loop.Run();
+}
+
+TEST_F(TraceReportHandlerTest, TracingStartFail) {
+  auto trace_config = ParseTraceConfigFromText(R"pb(
+                        data_sources: { config: { name: "Invalid" } }
+                      )pb")
+                          .SerializeAsString();
+  base::MockCallback<TraceReportHandler::StartTraceSessionCallback>
+      start_callback;
+  handler_->StartTraceSession(mojo_base::BigBuffer(base::as_bytes(
+                                  base::span<const char>(trace_config))),
+                              start_callback.Get());
+
+  {
+    base::RunLoop run_loop_stop;
+    EXPECT_CALL(start_callback, Run(false))
+        .WillOnce(base::test::RunOnceClosure(run_loop_stop.QuitClosure()));
+    run_loop_stop.Run();
+  }
+}
+
+TEST_F(TraceReportHandlerTest, TracingClone) {
+  auto trace_config =
+      ParseTraceConfigFromText(R"pb(
+        data_sources: { config: { name: "org.chromium.trace_metadata" } }
+      )pb")
+          .SerializeAsString();
+  base::MockCallback<TraceReportHandler::StartTraceSessionCallback>
+      start_callback;
+  handler_->StartTraceSession(mojo_base::BigBuffer(base::as_bytes(
+                                  base::span<const char>(trace_config))),
+                              start_callback.Get());
+
+  {
+    base::RunLoop run_loop_start;
+    EXPECT_CALL(start_callback, Run(true))
+        .WillOnce(base::test::RunOnceClosure(run_loop_start.QuitClosure()));
+    run_loop_start.Run();
+  }
+
+  base::MockCallback<TraceReportHandler::CloneTraceSessionCallback>
+      clone_callback;
+  handler_->CloneTraceSession(clone_callback.Get());
+  {
+    base::RunLoop run_loop_clone;
+
+    EXPECT_CALL(clone_callback,
+                Run(testing::Truly(
+                    [](const std::optional<mojo_base::BigBuffer>& trace) {
+                      return base::as_string_view(base::as_chars(
+                                 base::span(*trace))) == "this is a trace";
+                    })))
+        .WillOnce(base::test::RunOnceClosure(run_loop_clone.QuitClosure()));
+
+    run_loop_clone.Run();
+  }
+}
+
+TEST_F(TraceReportHandlerTest, TracingBufferUsage) {
+  auto trace_config =
+      ParseTraceConfigFromText(R"pb(
+        data_sources: { config: { name: "org.chromium.trace_metadata" } }
+      )pb")
+          .SerializeAsString();
+  base::MockCallback<TraceReportHandler::StartTraceSessionCallback>
+      start_callback;
+  handler_->StartTraceSession(mojo_base::BigBuffer(base::as_bytes(
+                                  base::span<const char>(trace_config))),
+                              start_callback.Get());
+
+  {
+    base::RunLoop run_loop_start;
+    EXPECT_CALL(start_callback, Run(true))
+        .WillOnce(base::test::RunOnceClosure(run_loop_start.QuitClosure()));
+    run_loop_start.Run();
+  }
+
+  base::MockCallback<TraceReportHandler::GetBufferUsageCallback>
+      buffer_callback;
+  handler_->GetBufferUsage(buffer_callback.Get());
+  {
+    base::RunLoop run_loop_buffer;
+
+    EXPECT_CALL(buffer_callback, Run(true, _, _))
+        .WillOnce(base::test::RunOnceClosure(run_loop_buffer.QuitClosure()));
+
+    run_loop_buffer.Run();
+  }
+}
+
 TEST_F(TraceReportHandlerTest, GetAllTraceReports) {
   base::MockCallback<TraceReportHandler::GetAllTraceReportsCallback> callback;
 
diff --git a/content/browser/webid/federated_auth_request_impl.cc b/content/browser/webid/federated_auth_request_impl.cc
index 541c74f..01bc163 100644
--- a/content/browser/webid/federated_auth_request_impl.cc
+++ b/content/browser/webid/federated_auth_request_impl.cc
@@ -38,8 +38,6 @@
 #include "content/browser/webid/flags.h"
 #include "content/browser/webid/identity_registry.h"
 #include "content/browser/webid/idp_network_request_manager.h"
-#include "content/browser/webid/jwt_signer.h"
-#include "content/browser/webid/sd_jwt.h"
 #include "content/browser/webid/webid_utils.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/content_browser_client.h"
@@ -55,7 +53,6 @@
 #include "services/network/public/cpp/is_potentially_trustworthy.h"
 #include "third_party/blink/public/common/webid/login_status_account.h"
 #include "third_party/blink/public/common/webid/login_status_options.h"
-#include "third_party/blink/public/mojom/devtools/inspector_issue.mojom.h"
 #include "third_party/blink/public/mojom/webid/federated_auth_request.mojom.h"
 #include "url/gurl.h"
 
@@ -63,8 +60,6 @@
 using blink::mojom::DisconnectStatus;
 using blink::mojom::FederatedAuthRequestResult;
 using blink::mojom::IdentityProviderConfig;
-using blink::mojom::IdentityProviderConfigPtr;
-using blink::mojom::IdentityProviderRequestOptions;
 using blink::mojom::IdentityProviderRequestOptionsPtr;
 using blink::mojom::RegisterIdpStatus;
 using blink::mojom::RequestTokenStatus;
@@ -238,10 +233,10 @@
       permission_context, identity_registry, std::move(receiver));
 }
 
-std::vector<blink::mojom::IdentityProviderRequestOptionsPtr>
+std::vector<IdentityProviderRequestOptionsPtr>
 FederatedAuthRequestImpl::MaybeAddRegisteredProviders(
-    std::vector<blink::mojom::IdentityProviderRequestOptionsPtr>& providers) {
-  std::vector<blink::mojom::IdentityProviderRequestOptionsPtr> result;
+    std::vector<IdentityProviderRequestOptionsPtr>& providers) {
+  std::vector<IdentityProviderRequestOptionsPtr> result;
 
   std::vector<GURL> registered_config_urls =
       permission_delegate_->GetRegisteredIdPs();
@@ -260,7 +255,7 @@
     }
 
     for (auto& configURL : registered_config_urls) {
-      blink::mojom::IdentityProviderRequestOptionsPtr idp = provider->Clone();
+      IdentityProviderRequestOptionsPtr idp = provider->Clone();
       // Keep `from_idp_registration_api` so it is clear this is a registered
       // provider.
       idp->config->config_url = configURL;
@@ -294,7 +289,7 @@
   // Expand the providers list with registered providers.
   if (IsFedCmIdPRegistrationEnabled()) {
     for (auto& idp_get_params_ptr : idp_get_params_ptrs) {
-      std::vector<blink::mojom::IdentityProviderRequestOptionsPtr> providers =
+      std::vector<IdentityProviderRequestOptionsPtr> providers =
           MaybeAddRegisteredProviders(idp_get_params_ptr->providers);
       if (providers.empty()) {
         render_frame_host().AddMessageToConsole(
@@ -1762,7 +1757,7 @@
           (accounts_dialog_display_time_ -
            ready_to_display_accounts_dialog_time_));
 
-  const blink::mojom::IdentityProviderRequestOptionsPtr& provider =
+  const IdentityProviderRequestOptionsPtr& provider =
       idp_infos_[idp_config_url]->provider;
   DCHECK(provider);
 
@@ -1867,7 +1862,8 @@
   }
 
   if (result == FederatedAuthRequestResult::kSuccess) {
-    DCHECK(selected_idp_config_url);
+    CHECK(selected_idp_config_url);
+    CHECK(fedcm_accounts_fetcher_);
     if (IsFedCmMetricsEndpointEnabled()) {
       fedcm_accounts_fetcher_->SendSuccessfulTokenRequestMetrics(
           *selected_idp_config_url,
@@ -1885,7 +1881,9 @@
     AddDevToolsIssue(result);
     AddConsoleErrorMessage(result);
 
-    if (IsFedCmMetricsEndpointEnabled()) {
+    // fedcm_accounts_fetcher_ could be null if configs were not fetched, e.g.
+    // because of cooldown.
+    if (IsFedCmMetricsEndpointEnabled() && fedcm_accounts_fetcher_) {
       fedcm_accounts_fetcher_->SendAllFailedTokenRequestMetrics(result,
                                                                 did_show_ui_);
     }
@@ -2128,7 +2126,7 @@
   fedcm_metrics_->RecordContinueOnPopupResult(
       FedCmContinueOnPopupResult::kTokenReceived);
 
-  const blink::mojom::IdentityProviderRequestOptionsPtr& provider =
+  const IdentityProviderRequestOptionsPtr& provider =
       idp_infos_[idp_config_url]->provider;
   DCHECK(provider);
 
diff --git a/content/browser/webid/federated_auth_request_impl.h b/content/browser/webid/federated_auth_request_impl.h
index a2440138..5e78994 100644
--- a/content/browser/webid/federated_auth_request_impl.h
+++ b/content/browser/webid/federated_auth_request_impl.h
@@ -23,8 +23,6 @@
 #include "content/browser/webid/identity_registry.h"
 #include "content/browser/webid/identity_registry_delegate.h"
 #include "content/browser/webid/idp_network_request_manager.h"
-#include "content/browser/webid/jwt_signer.h"
-#include "content/browser/webid/sd_jwt.h"
 #include "content/common/content_export.h"
 #include "content/public/browser/document_service.h"
 #include "content/public/browser/federated_auth_autofill_source.h"
@@ -33,27 +31,16 @@
 #include "content/public/browser/identity_request_dialog_controller.h"
 #include "content/public/browser/web_contents.h"
 #include "third_party/blink/public/mojom/credentialmanagement/credential_manager.mojom.h"
-#include "third_party/blink/public/mojom/devtools/inspector_issue.mojom.h"
 #include "third_party/blink/public/mojom/webid/federated_auth_request.mojom.h"
 #include "url/gurl.h"
 
-namespace gfx {
-class Image;
-}
-
-namespace blink::common::webid {
-struct LoginStatusOptions;
-}  // namespace blink::common::webid
-
 namespace content {
 
 class FederatedAuthDisconnectRequest;
 class FederatedAuthUserInfoRequest;
-class FederatedIdentityApiPermissionContextDelegate;
 class FederatedIdentityAutoReauthnPermissionContextDelegate;
 class FederatedIdentityPermissionContextDelegate;
 class RenderFrameHost;
-class FederatedSdJwtHandler;
 
 using blink::mojom::IdentityProviderGetParametersPtr;
 using IdentityProviderDataPtr = scoped_refptr<content::IdentityProviderData>;
@@ -367,13 +354,6 @@
   void MarkUserAsSignedIn(const GURL& idp_config_url,
                           const std::string& account_id);
 
-  void ProcessSdJwt(const GURL& selected_idp_config_url,
-                    const std::string& token);
-  void OnDisclosureParsed(base::RepeatingClosure cb,
-                          const std::string& json,
-                          data_decoder::DataDecoder::ValueOrError result);
-  void OnSdJwtParsed(const GURL& selected_idp_config_url,
-                     const sdjwt::Jwt& token);
   void CompleteUserInfoRequest(
       FederatedAuthUserInfoRequest* request,
       RequestUserInfoCallback callback,
diff --git a/content/browser/webid/federated_auth_request_impl_unittest.cc b/content/browser/webid/federated_auth_request_impl_unittest.cc
index 7658968..cc6c5a0 100644
--- a/content/browser/webid/federated_auth_request_impl_unittest.cc
+++ b/content/browser/webid/federated_auth_request_impl_unittest.cc
@@ -5316,6 +5316,33 @@
   std::vector<GURL> metrics_endpoints_notified_failure_;
 };
 
+// Test that the metrics endpoint is not notified when the FedCM API is in
+// cooldown.
+TEST_F(FederatedAuthRequestImplTest, MetricsEndpointDuringCooldown) {
+  base::test::ScopedFeatureList list;
+  list.InitAndEnableFeature(features::kFedCmMetricsEndpoint);
+
+  test_api_permission_delegate_->RecordDismissAndEmbargo(
+      OriginFromString(kRpUrl));
+  std::unique_ptr<IdpNetworkRequestMetricsRecorder> unique_metrics_recorder =
+      std::make_unique<IdpNetworkRequestMetricsRecorder>();
+  IdpNetworkRequestMetricsRecorder* metrics_recorder =
+      unique_metrics_recorder.get();
+  SetNetworkRequestManager(std::move(unique_metrics_recorder));
+
+  RequestExpectations expectations = {
+      RequestTokenStatus::kError,
+      FederatedAuthRequestResult::kDisabledInSettings,
+      /*standalone_console_message=*/std::nullopt,
+      /* selected_idp_config_url=*/std::nullopt};
+  RunAuthTest(kDefaultRequestParameters, expectations, kConfigurationValid);
+  EXPECT_FALSE(DidFetchAnyEndpoint());
+  EXPECT_TRUE(
+      metrics_recorder->get_metrics_endpoints_notified_success().empty());
+  EXPECT_TRUE(
+      metrics_recorder->get_metrics_endpoints_notified_failure().empty());
+}
+
 // Test that the metrics endpoint is notified as a result of a successful
 // multi-IDP FederatedAuthRequestImpl::RequestToken() call.
 TEST_F(FederatedAuthRequestImplTest, MetricsEndpointMultiIdp) {
diff --git a/content/browser/webid/federated_sd_jwt_handler.cc b/content/browser/webid/federated_sd_jwt_handler.cc
index 14f683b..31a5091 100644
--- a/content/browser/webid/federated_sd_jwt_handler.cc
+++ b/content/browser/webid/federated_sd_jwt_handler.cc
@@ -15,6 +15,7 @@
 #include "content/browser/webid/fedcm_mappers.h"
 #include "content/browser/webid/federated_auth_request_impl.h"
 #include "content/browser/webid/flags.h"
+#include "content/browser/webid/jwt_signer.h"
 #include "content/browser/webid/sd_jwt.h"
 #include "crypto/ec_private_key.h"
 #include "crypto/hash.h"
diff --git a/content/browser/worker_host/worker_browsertest.cc b/content/browser/worker_host/worker_browsertest.cc
index 41a139d..0f176dc0 100644
--- a/content/browser/worker_host/worker_browsertest.cc
+++ b/content/browser/worker_host/worker_browsertest.cc
@@ -53,9 +53,9 @@
 #include "net/test/spawned_test_server/spawned_test_server.h"
 #include "net/test/test_data_directory.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
+#include "services/network/public/mojom/connection_change_observer_client.mojom.h"
 #include "services/network/public/mojom/cookie_manager.mojom.h"
 #include "services/network/public/mojom/network_context.mojom.h"
-#include "services/network/public/mojom/reconnect_event_observer.mojom.h"
 #include "third_party/blink/public/common/storage_key/storage_key.h"
 #include "url/gurl.h"
 
diff --git a/content/public/browser/permission_overrides.cc b/content/public/browser/permission_overrides.cc
index 10ec2c7..2853514 100644
--- a/content/public/browser/permission_overrides.cc
+++ b/content/public/browser/permission_overrides.cc
@@ -4,7 +4,7 @@
 
 #include "content/public/browser/permission_overrides.h"
 
-#include "base/no_destructor.h"
+#include "base/types/optional_ref.h"
 #include "third_party/blink/public/common/permissions/permission_utils.h"
 
 namespace content {
@@ -18,57 +18,67 @@
 PermissionOverrides& PermissionOverrides::operator=(
     PermissionOverrides&& other) = default;
 
-void PermissionOverrides::Set(const std::optional<url::Origin>& origin,
+void PermissionOverrides::Set(base::optional_ref<const url::Origin> origin,
                               blink::PermissionType permission,
                               const blink::mojom::PermissionStatus& status) {
-  PermissionOverridesMap& origin_overrides =
-      overrides_[origin.value_or(global_overrides_origin_)];
-  origin_overrides[permission] = status;
+  const url::Origin& key_origin =
+      origin.has_value() ? *origin : global_overrides_origin_;
+  overrides_[{key_origin, permission}] = status;
 
   // Special override status - MIDI_SYSEX is stronger than MIDI, meaning that
   // granting MIDI_SYSEX implies granting MIDI, while denying MIDI implies
   // denying MIDI_SYSEX.
   if (permission == blink::PermissionType::MIDI &&
       status != PermissionStatus::GRANTED) {
-    origin_overrides[blink::PermissionType::MIDI_SYSEX] = status;
+    overrides_[{key_origin, blink::PermissionType::MIDI_SYSEX}] = status;
   } else if (permission == blink::PermissionType::MIDI_SYSEX &&
              status == PermissionStatus::GRANTED) {
-    origin_overrides[blink::PermissionType::MIDI] = status;
+    overrides_[{key_origin, blink::PermissionType::MIDI}] = status;
   }
 }
 
 std::optional<PermissionStatus> PermissionOverrides::Get(
     const url::Origin& origin,
     blink::PermissionType permission) const {
-  auto current_override = overrides_.find(origin);
+  auto current_override = overrides_.find({origin, permission});
   if (current_override == overrides_.end())
-    current_override = overrides_.find(global_overrides_origin_);
+    current_override = overrides_.find({global_overrides_origin_, permission});
   if (current_override == overrides_.end())
     return std::nullopt;
 
-  auto new_status = current_override->second.find(permission);
-  if (new_status != current_override->second.end())
-    return std::make_optional(new_status->second);
-  return std::nullopt;
+  return current_override->second;
 }
 
-const PermissionOverridesMap& PermissionOverrides::GetAllForTest(
-    const std::optional<url::Origin>& origin) const {
-  static const base::NoDestructor<PermissionOverridesMap> empty_overrides;
-  auto it = origin ? overrides_.find(*origin) : overrides_.end();
-  if (it == overrides_.end())
-    it = overrides_.find(global_overrides_origin_);
-  if (it == overrides_.end())
-    return *empty_overrides;
-  return it->second;
+PermissionOverridesMap PermissionOverrides::GetAllForTest(
+    base::optional_ref<const url::Origin> origin) const {
+  PermissionOverridesMap output;
+  if (origin) {
+    for (const auto& [key, status] : overrides_) {
+      if (key.first == origin) {
+        output[key.second] = status;
+      }
+    }
+  }
+  for (const auto& [key, status] : overrides_) {
+    if (key.first == global_overrides_origin_ && !output.contains(key.second)) {
+      output[key.second] = status;
+    }
+  }
+
+  return output;
 }
 
-void PermissionOverrides::Reset(const std::optional<url::Origin>& origin) {
-  overrides_.erase(origin.value_or(global_overrides_origin_));
+void PermissionOverrides::Reset(base::optional_ref<const url::Origin> origin) {
+  const url::Origin& key_origin =
+      origin.has_value() ? *origin : global_overrides_origin_;
+  base::EraseIf(overrides_, [&](const auto& pair) {
+    const auto& [key, status] = pair;
+    return key.first == key_origin;
+  });
 }
 
 void PermissionOverrides::GrantPermissions(
-    const std::optional<url::Origin>& origin,
+    base::optional_ref<const url::Origin> origin,
     const std::vector<blink::PermissionType>& permissions) {
   const std::vector<blink::PermissionType>& kAllPermissionTypes =
       blink::GetAllPermissionTypes();
diff --git a/content/public/browser/permission_overrides.h b/content/public/browser/permission_overrides.h
index aa59ab2..cdf092e 100644
--- a/content/public/browser/permission_overrides.h
+++ b/content/public/browser/permission_overrides.h
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "base/containers/flat_map.h"
+#include "base/types/optional_ref.h"
 #include "content/common/content_export.h"
 #include "third_party/blink/public/mojom/permissions/permission_status.mojom.h"
 #include "url/origin.h"
@@ -32,7 +33,7 @@
 
   // Set permission override for |permission| at |origin| to |status|.
   // Null |origin| specifies global overrides.
-  void Set(const std::optional<url::Origin>& origin,
+  void Set(base::optional_ref<const url::Origin> origin,
            blink::PermissionType permission,
            const blink::mojom::PermissionStatus& status);
 
@@ -44,24 +45,23 @@
   // Get all overrides for particular |origin|, stored in |overrides|
   // if found. Will return empty overrides if none previously existed. Returns
   // global overrides when |origin| is nullptr.
-  const base::flat_map<blink::PermissionType, blink::mojom::PermissionStatus>&
-  GetAllForTest(const std::optional<url::Origin>& origin) const;
+  base::flat_map<blink::PermissionType, blink::mojom::PermissionStatus>
+  GetAllForTest(base::optional_ref<const url::Origin> origin) const;
 
   // Resets overrides for |origin|.
   // Null |origin| resets global overrides.
-  void Reset(const std::optional<url::Origin>& origin);
+  void Reset(base::optional_ref<const url::Origin> origin);
 
   // Sets status for |permissions| to GRANTED in |origin|, and DENIED
   // for all others.
   // Null |origin| grants permissions globally for context.
-  void GrantPermissions(const std::optional<url::Origin>& origin,
+  void GrantPermissions(base::optional_ref<const url::Origin> origin,
                         const std::vector<blink::PermissionType>& permissions);
 
  private:
   url::Origin global_overrides_origin_;
-  base::flat_map<
-      url::Origin,
-      base::flat_map<blink::PermissionType, blink::mojom::PermissionStatus>>
+  base::flat_map<std::pair<url::Origin, blink::PermissionType>,
+                 blink::mojom::PermissionStatus>
       overrides_;
 };
 
diff --git a/content/public/test/btm_service_test_utils.cc b/content/public/test/btm_service_test_utils.cc
index 4fee8a26..9081c3c 100644
--- a/content/public/test/btm_service_test_utils.cc
+++ b/content/public/test/btm_service_test_utils.cc
@@ -5,9 +5,24 @@
 #include "content/public/test/btm_service_test_utils.h"
 
 #include "base/logging.h"
+#include "content/browser/btm/btm_bounce_detector.h"
 #include "content/public/browser/btm_redirect_info.h"
 
 namespace content {
+void Populate3PcExceptions(BrowserContext* browser_context,
+                           WebContents* web_contents,
+                           const GURL& initial_url,
+                           const GURL& final_url,
+                           base::span<BtmRedirectInfoPtr> redirects) {
+  btm::Populate3PcExceptions(browser_context, web_contents, initial_url,
+                             final_url, redirects);
+}
+
+bool Are3PcsGenerallyEnabled(BrowserContext* browser_context,
+                             WebContents* web_contents) {
+  return btm::Are3PcsGenerallyEnabled(browser_context, web_contents);
+}
+
 BtmRedirectChainObserver::BtmRedirectChainObserver(BtmService* service,
                                                    GURL final_url)
     : final_url_(std::move(final_url)) {
diff --git a/content/public/test/btm_service_test_utils.h b/content/public/test/btm_service_test_utils.h
index 802db2b..153d01f 100644
--- a/content/public/test/btm_service_test_utils.h
+++ b/content/public/test/btm_service_test_utils.h
@@ -7,10 +7,22 @@
 
 #include "base/run_loop.h"
 #include "base/scoped_observation.h"
+#include "content/public/browser/btm_redirect_info.h"
 #include "content/public/browser/btm_service.h"
 #include "url/gurl.h"
 
 namespace content {
+class BrowserContext;
+class WebContents;
+
+void Populate3PcExceptions(BrowserContext* browser_context,
+                           WebContents* web_contents,
+                           const GURL& initial_url,
+                           const GURL& final_url,
+                           base::span<BtmRedirectInfoPtr> redirects);
+
+bool Are3PcsGenerallyEnabled(BrowserContext* browser_context,
+                             WebContents* web_contents);
 
 class BtmRedirectChainObserver : public BtmService::Observer {
  public:
diff --git a/content/public/test/permission_overrides_unittest.cc b/content/public/test/permission_overrides_unittest.cc
index 9d51ceb..cfeba20a 100644
--- a/content/public/test/permission_overrides_unittest.cc
+++ b/content/public/test/permission_overrides_unittest.cc
@@ -16,8 +16,15 @@
 namespace {
 using blink::PermissionType;
 using blink::mojom::PermissionStatus;
+using testing::AllOf;
+using testing::IsSupersetOf;
+using testing::Pair;
+using testing::SizeIs;
+using testing::UnorderedElementsAre;
 using url::Origin;
 
+constexpr size_t kPermissionsCount = 37;
+
 TEST(PermissionOverridesTest, GetOriginNoOverrides) {
   PermissionOverrides overrides;
   Origin url = Origin::Create(GURL("https://google.com/search?q=foo"));
@@ -30,24 +37,23 @@
   Origin url = Origin::Create(GURL("https://google.com/"));
   overrides.Set(url, PermissionType::MIDI_SYSEX, PermissionStatus::GRANTED);
 
-  EXPECT_EQ(*overrides.Get(url, PermissionType::MIDI_SYSEX),
+  EXPECT_EQ(overrides.Get(url, PermissionType::MIDI_SYSEX),
             PermissionStatus::GRANTED);
-  EXPECT_EQ(*overrides.Get(url, PermissionType::MIDI),
+  EXPECT_EQ(overrides.Get(url, PermissionType::MIDI),
             PermissionStatus::GRANTED);
 
   overrides.Set(url, PermissionType::MIDI_SYSEX, PermissionStatus::DENIED);
-  EXPECT_EQ(*overrides.Get(url, PermissionType::MIDI_SYSEX),
+  EXPECT_EQ(overrides.Get(url, PermissionType::MIDI_SYSEX),
             PermissionStatus::DENIED);
-  EXPECT_EQ(*overrides.Get(url, PermissionType::MIDI),
+  EXPECT_EQ(overrides.Get(url, PermissionType::MIDI),
             PermissionStatus::GRANTED);
 
   // Reset to all-granted MIDI.
   overrides.Set(url, PermissionType::MIDI_SYSEX, PermissionStatus::GRANTED);
 
   overrides.Set(url, PermissionType::MIDI, PermissionStatus::DENIED);
-  EXPECT_EQ(*overrides.Get(url, PermissionType::MIDI),
-            PermissionStatus::DENIED);
-  EXPECT_EQ(*overrides.Get(url, PermissionType::MIDI_SYSEX),
+  EXPECT_EQ(overrides.Get(url, PermissionType::MIDI), PermissionStatus::DENIED);
+  EXPECT_EQ(overrides.Get(url, PermissionType::MIDI_SYSEX),
             PermissionStatus::DENIED);
 }
 
@@ -56,7 +62,7 @@
   Origin url = Origin::Create(GURL("https://google.com/search?q=foo"));
   overrides.Set(url, PermissionType::GEOLOCATION, PermissionStatus::GRANTED);
 
-  EXPECT_EQ(*overrides.Get(url, PermissionType::GEOLOCATION),
+  EXPECT_EQ(overrides.Get(url, PermissionType::GEOLOCATION),
             PermissionStatus::GRANTED);
 }
 
@@ -70,13 +76,13 @@
   overrides.Set(url, PermissionType::AUDIO_CAPTURE, PermissionStatus::ASK);
 
   // Check that overrides are saved for the given url.
-  EXPECT_EQ(*overrides.Get(url, PermissionType::GEOLOCATION),
+  EXPECT_EQ(overrides.Get(url, PermissionType::GEOLOCATION),
             PermissionStatus::GRANTED);
 
-  EXPECT_EQ(*overrides.Get(url, PermissionType::NOTIFICATIONS),
+  EXPECT_EQ(overrides.Get(url, PermissionType::NOTIFICATIONS),
             PermissionStatus::DENIED);
 
-  EXPECT_EQ(*overrides.Get(url, PermissionType::AUDIO_CAPTURE),
+  EXPECT_EQ(overrides.Get(url, PermissionType::AUDIO_CAPTURE),
             PermissionStatus::ASK);
 }
 
@@ -133,11 +139,11 @@
   overrides.Set(url, PermissionType::NOTIFICATIONS, PermissionStatus::DENIED);
   overrides.Set(url, PermissionType::AUDIO_CAPTURE, PermissionStatus::ASK);
 
-  EXPECT_EQ(*overrides.Get(Origin::Create(GURL("https://google.com")),
-                           PermissionType::GEOLOCATION),
+  EXPECT_EQ(overrides.Get(Origin::Create(GURL("https://google.com")),
+                          PermissionType::GEOLOCATION),
             PermissionStatus::GRANTED);
-  EXPECT_EQ(*overrides.Get(Origin::Create(GURL("https://google.com/")),
-                           PermissionType::AUDIO_CAPTURE),
+  EXPECT_EQ(overrides.Get(Origin::Create(GURL("https://google.com/")),
+                          PermissionType::AUDIO_CAPTURE),
             PermissionStatus::ASK);
 }
 
@@ -175,7 +181,7 @@
                 PermissionStatus::ASK);
 
   // Origins do not interfere.
-  EXPECT_EQ(*overrides.Get(first_url, PermissionType::GEOLOCATION),
+  EXPECT_EQ(overrides.Get(first_url, PermissionType::GEOLOCATION),
             PermissionStatus::GRANTED);
   EXPECT_FALSE(
       overrides.Get(first_url, PermissionType::NOTIFICATIONS).has_value());
@@ -193,14 +199,14 @@
   overrides.Set(url, PermissionType::GEOLOCATION, PermissionStatus::GRANTED);
   overrides.Set(other_url, PermissionType::GEOLOCATION,
                 PermissionStatus::GRANTED);
-  EXPECT_EQ(*overrides.Get(url, PermissionType::GEOLOCATION),
+  EXPECT_EQ(overrides.Get(url, PermissionType::GEOLOCATION),
             PermissionStatus::GRANTED);
-  EXPECT_EQ(*overrides.Get(other_url, PermissionType::GEOLOCATION),
+  EXPECT_EQ(overrides.Get(other_url, PermissionType::GEOLOCATION),
             PermissionStatus::GRANTED);
 
   overrides.Reset(url);
   EXPECT_FALSE(overrides.Get(url, PermissionType::GEOLOCATION).has_value());
-  EXPECT_EQ(*overrides.Get(other_url, PermissionType::GEOLOCATION),
+  EXPECT_EQ(overrides.Get(other_url, PermissionType::GEOLOCATION),
             PermissionStatus::GRANTED);
 }
 
@@ -213,25 +219,130 @@
             PermissionType::NOTIFICATIONS});
 
   // All other types should be blocked - will test a set of them.
-  EXPECT_EQ(*overrides.Get(url, PermissionType::GEOLOCATION),
+  EXPECT_EQ(overrides.Get(url, PermissionType::GEOLOCATION),
             PermissionStatus::DENIED);
-  EXPECT_EQ(*overrides.Get(url, PermissionType::AUDIO_CAPTURE),
+  EXPECT_EQ(overrides.Get(url, PermissionType::AUDIO_CAPTURE),
             PermissionStatus::DENIED);
-  EXPECT_EQ(*overrides.Get(url, PermissionType::MIDI_SYSEX),
+  EXPECT_EQ(overrides.Get(url, PermissionType::MIDI_SYSEX),
             PermissionStatus::DENIED);
-  EXPECT_EQ(*overrides.Get(url, PermissionType::CLIPBOARD_READ_WRITE),
+  EXPECT_EQ(overrides.Get(url, PermissionType::CLIPBOARD_READ_WRITE),
             PermissionStatus::DENIED);
-  EXPECT_EQ(*overrides.Get(url, PermissionType::WAKE_LOCK_SYSTEM),
+  EXPECT_EQ(overrides.Get(url, PermissionType::WAKE_LOCK_SYSTEM),
             PermissionStatus::DENIED);
 
   // Specified types are granted.
-  EXPECT_EQ(*overrides.Get(url, PermissionType::NOTIFICATIONS),
+  EXPECT_EQ(overrides.Get(url, PermissionType::NOTIFICATIONS),
             PermissionStatus::GRANTED);
-  EXPECT_EQ(*overrides.Get(url, PermissionType::BACKGROUND_SYNC),
+  EXPECT_EQ(overrides.Get(url, PermissionType::BACKGROUND_SYNC),
             PermissionStatus::GRANTED);
-  EXPECT_EQ(*overrides.Get(url, PermissionType::BACKGROUND_FETCH),
+  EXPECT_EQ(overrides.Get(url, PermissionType::BACKGROUND_FETCH),
             PermissionStatus::GRANTED);
 }
 
+TEST(PermissionOverridesTest, GrantPermissions_AllOriginsShadowing) {
+  using enum blink::PermissionType;
+  using enum PermissionStatus;
+  PermissionOverrides overrides;
+
+  // Override some types for all origins.
+  overrides.GrantPermissions(std::nullopt, {GEOLOCATION, AUDIO_CAPTURE});
+
+  {
+    Origin origin = Origin::Create(GURL("https://google.com/search?q=all"));
+
+    // Override other permissions types for one origin.
+    overrides.GrantPermissions(
+        origin, {BACKGROUND_SYNC, BACKGROUND_FETCH, NOTIFICATIONS});
+
+    // The per-origin overrides are respected.
+    EXPECT_EQ(overrides.Get(origin, NOTIFICATIONS), GRANTED);
+    EXPECT_EQ(overrides.Get(origin, BACKGROUND_SYNC), GRANTED);
+    EXPECT_EQ(overrides.Get(origin, BACKGROUND_FETCH), GRANTED);
+
+    // Global overrides are shadowed by the single origin's `GrantPermissions`
+    // call.
+    EXPECT_EQ(overrides.Get(origin, GEOLOCATION), DENIED);
+    EXPECT_EQ(overrides.Get(origin, AUDIO_CAPTURE), DENIED);
+
+    EXPECT_THAT(overrides.GetAllForTest(origin),
+                AllOf(IsSupersetOf({
+                          std::make_pair(BACKGROUND_SYNC, GRANTED),
+                          std::make_pair(BACKGROUND_FETCH, GRANTED),
+                          std::make_pair(NOTIFICATIONS, GRANTED),
+                          std::make_pair(GEOLOCATION, DENIED),
+                          std::make_pair(AUDIO_CAPTURE, DENIED),
+                      }),
+                      SizeIs(kPermissionsCount)));
+    EXPECT_THAT(overrides.GetAllForTest(std::nullopt),
+                AllOf(IsSupersetOf({
+                          std::make_pair(BACKGROUND_SYNC, DENIED),
+                          std::make_pair(BACKGROUND_FETCH, DENIED),
+                          std::make_pair(NOTIFICATIONS, DENIED),
+                          std::make_pair(GEOLOCATION, GRANTED),
+                          std::make_pair(AUDIO_CAPTURE, GRANTED),
+                      }),
+                      SizeIs(kPermissionsCount)));
+  }
+  {
+    // For a different origin, only the global overrides take effect.
+    Origin origin = Origin::Create(GURL("https://www.google.com/search?q=all"));
+
+    EXPECT_EQ(overrides.Get(origin, NOTIFICATIONS), DENIED);
+    EXPECT_EQ(overrides.Get(origin, BACKGROUND_SYNC), DENIED);
+    EXPECT_EQ(overrides.Get(origin, BACKGROUND_FETCH), DENIED);
+
+    EXPECT_EQ(overrides.Get(origin, GEOLOCATION), GRANTED);
+    EXPECT_EQ(overrides.Get(origin, AUDIO_CAPTURE), GRANTED);
+
+    EXPECT_THAT(overrides.GetAllForTest(origin),
+                AllOf(IsSupersetOf({
+                          std::make_pair(BACKGROUND_SYNC, DENIED),
+                          std::make_pair(BACKGROUND_FETCH, DENIED),
+                          std::make_pair(NOTIFICATIONS, DENIED),
+                          std::make_pair(GEOLOCATION, GRANTED),
+                          std::make_pair(AUDIO_CAPTURE, GRANTED),
+                      }),
+                      SizeIs(kPermissionsCount)));
+  }
+}
+
+TEST(PermissionOverridesTest, SetPermission_AllOriginsNoShadowing) {
+  using enum blink::PermissionType;
+  using enum PermissionStatus;
+  PermissionOverrides overrides;
+
+  // Override a permission type for all origins.
+  overrides.Set(std::nullopt, GEOLOCATION, GRANTED);
+
+  {
+    Origin origin = Origin::Create(GURL("https://google.com/search?q=all"));
+
+    // Override another permission type for one origin.
+    overrides.Set(origin, BACKGROUND_SYNC, GRANTED);
+
+    // The per-origin override is respected.
+    EXPECT_EQ(overrides.Get(origin, BACKGROUND_SYNC), GRANTED);
+
+    // Global overrides are not shadowed by the single origin's `Set` call.
+    EXPECT_EQ(overrides.Get(origin, GEOLOCATION), GRANTED);
+
+    EXPECT_THAT(overrides.GetAllForTest(origin),
+                UnorderedElementsAre(Pair(GEOLOCATION, GRANTED),
+                                     Pair(BACKGROUND_SYNC, GRANTED)));
+    EXPECT_THAT(overrides.GetAllForTest(std::nullopt),
+                UnorderedElementsAre(Pair(GEOLOCATION, GRANTED)));
+  }
+  {
+    // For a different origin, only the global overrides take effect.
+    Origin origin = Origin::Create(GURL("https://www.google.com/search?q=all"));
+
+    EXPECT_EQ(overrides.Get(origin, BACKGROUND_SYNC), std::nullopt);
+
+    EXPECT_EQ(overrides.Get(origin, GEOLOCATION), GRANTED);
+    EXPECT_THAT(overrides.GetAllForTest(origin),
+                UnorderedElementsAre(Pair(GEOLOCATION, GRANTED)));
+  }
+}
+
 }  // namespace
 }  // namespace content
diff --git a/content/public/test/test_navigation_throttle.cc b/content/public/test/test_navigation_throttle.cc
index 1ac8053..21175c9 100644
--- a/content/public/test/test_navigation_throttle.cc
+++ b/content/public/test/test_navigation_throttle.cc
@@ -2,19 +2,23 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-
 #include "content/public/test/test_navigation_throttle.h"
 
 #include "base/functional/bind.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/navigation_throttle_registry.h"
 
 namespace content {
 
 TestNavigationThrottle::TestNavigationThrottle(NavigationHandle* handle)
     : NavigationThrottle(handle) {}
 
+TestNavigationThrottle::TestNavigationThrottle(
+    NavigationThrottleRegistry& registry)
+    : NavigationThrottle(registry) {}
+
 TestNavigationThrottle::~TestNavigationThrottle() {}
 
 NavigationThrottle::ThrottleCheckResult
@@ -84,8 +88,9 @@
 NavigationThrottle::ThrottleCheckResult TestNavigationThrottle::ProcessMethod(
     ThrottleMethod method) {
   method_properties_[method].call_count++;
-  if (!method_properties_[method].callback.is_null())
+  if (!method_properties_[method].callback.is_null()) {
     method_properties_[method].callback.Run();
+  }
 
   NavigationThrottle::ThrottleCheckResult result =
       method_properties_[method].result;
diff --git a/content/public/test/test_navigation_throttle.h b/content/public/test/test_navigation_throttle.h
index 9572153..b6617b65 100644
--- a/content/public/test/test_navigation_throttle.h
+++ b/content/public/test/test_navigation_throttle.h
@@ -36,7 +36,12 @@
     ASYNCHRONOUS,
   };
 
-  TestNavigationThrottle(NavigationHandle* handle);
+  // Note: This legacy constructor will be removed soon. New code should use the
+  // other constructor that takes a NavigationThrottleRegistry&.
+  // TODO(https://crbug.com/412524375): Remove this constructor.
+  explicit TestNavigationThrottle(NavigationHandle* handle);
+
+  explicit TestNavigationThrottle(NavigationThrottleRegistry& registry);
 
   TestNavigationThrottle(const TestNavigationThrottle&) = delete;
   TestNavigationThrottle& operator=(const TestNavigationThrottle&) = delete;
diff --git a/content/public/test/test_navigation_throttle_inserter.cc b/content/public/test/test_navigation_throttle_inserter.cc
index 465f04e..0c544aa9 100644
--- a/content/public/test/test_navigation_throttle_inserter.cc
+++ b/content/public/test/test_navigation_throttle_inserter.cc
@@ -16,23 +16,12 @@
     ThrottleInsertionCallback callback)
     : WebContentsObserver(web_contents), callback_(std::move(callback)) {}
 
-TestNavigationThrottleInserter::TestNavigationThrottleInserter(
-    WebContents* web_contents,
-    NewThrottleInsertionCallback callback)
-    : WebContentsObserver(web_contents), new_callback_(std::move(callback)) {}
-
 TestNavigationThrottleInserter::~TestNavigationThrottleInserter() = default;
 
 void TestNavigationThrottleInserter::DidStartNavigation(
     NavigationHandle* navigation_handle) {
   if (callback_) {
-    if (std::unique_ptr<NavigationThrottle> throttle =
-            callback_.Run(navigation_handle)) {
-      navigation_handle->RegisterThrottleForTesting(std::move(throttle));
-    }
-  }
-  if (new_callback_) {
-    new_callback_.Run(*NavigationRequest::From(navigation_handle)
+    callback_.Run(*NavigationRequest::From(navigation_handle)
                            ->GetNavigationThrottleRunnerForTesting());
   }
 }
diff --git a/content/public/test/test_navigation_throttle_inserter.h b/content/public/test/test_navigation_throttle_inserter.h
index 65bbee0..95a35c60 100644
--- a/content/public/test/test_navigation_throttle_inserter.h
+++ b/content/public/test/test_navigation_throttle_inserter.h
@@ -5,8 +5,6 @@
 #ifndef CONTENT_PUBLIC_TEST_TEST_NAVIGATION_THROTTLE_INSERTER_H_
 #define CONTENT_PUBLIC_TEST_TEST_NAVIGATION_THROTTLE_INSERTER_H_
 
-#include <memory>
-
 #include "base/functional/callback.h"
 #include "content/public/browser/web_contents_observer.h"
 
@@ -16,12 +14,7 @@
 class NavigationThrottleRegistry;
 class WebContents;
 
-// TODO(https://crbug.com/412524375): Remove old callback type.
 using ThrottleInsertionCallback =
-    base::RepeatingCallback<std::unique_ptr<NavigationThrottle>(
-        NavigationHandle*)>;
-
-using NewThrottleInsertionCallback =
     base::RepeatingCallback<void(NavigationThrottleRegistry& registry)>;
 
 // This class is instantiated with a NavigationThrottle factory callback, and
@@ -30,12 +23,8 @@
 //    navigation.
 class TestNavigationThrottleInserter : public WebContentsObserver {
  public:
-  // TODO(https://crbug.com/412524375): Remove old constructor with a legacy
-  // callback type.
   TestNavigationThrottleInserter(WebContents* web_contents,
                                  ThrottleInsertionCallback callback);
-  TestNavigationThrottleInserter(WebContents* web_contents,
-                                 NewThrottleInsertionCallback callback);
 
   TestNavigationThrottleInserter(const TestNavigationThrottleInserter&) =
       delete;
@@ -49,7 +38,6 @@
 
  private:
   ThrottleInsertionCallback callback_;
-  NewThrottleInsertionCallback new_callback_;
 };
 
 }  // namespace content
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 1403a31..a3e4acd 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -161,6 +161,8 @@
     "../browser/sms/test/mock_sms_web_contents_delegate.h",
     "../browser/sms/test/mock_user_consent_handler.cc",
     "../browser/sms/test/mock_user_consent_handler.h",
+    "../browser/tracing/test_tracing_session.cc",
+    "../browser/tracing/test_tracing_session.h",
     "../browser/usb/usb_test_utils.cc",
     "../browser/usb/usb_test_utils.h",
     "../browser/web_package/mock_signed_exchange_handler.cc",
@@ -3309,8 +3311,14 @@
   ]
 
   if (!is_ios) {
-    data_deps += [ "//third_party/perfetto/protos/perfetto/config/chrome:scenario_descriptor" ]
-    data += [ "$root_gen_dir/third_party/perfetto/protos/perfetto/config/chrome/scenario_config.descriptor" ]
+    data_deps += [
+      "//third_party/perfetto/protos/perfetto/config:descriptor",
+      "//third_party/perfetto/protos/perfetto/config/chrome:scenario_descriptor",
+    ]
+    data += [
+      "$root_gen_dir/third_party/perfetto/protos/perfetto/config/chrome/scenario_config.descriptor",
+      "$root_gen_dir/third_party/perfetto/protos/perfetto/config/config.descriptor",
+    ]
   }
 
   # Platforms where sqlite_dev_shell is defined.
@@ -3646,9 +3654,15 @@
 if (is_apple) {
   bundle_data("content_test_perfetto_bundle_data") {
     testonly = true
-    _relative_path = "third_party/perfetto/protos/perfetto/config/chrome"
-    public_deps = [ "//third_party/perfetto/protos/perfetto/config/chrome:scenario_descriptor_gen" ]
-    sources = [ "$root_gen_dir/$_relative_path/scenario_config.descriptor" ]
+    _relative_path = "third_party/perfetto/protos/perfetto/config"
+    public_deps = [
+      "//third_party/perfetto/protos/perfetto/config:descriptor_gen",
+      "//third_party/perfetto/protos/perfetto/config/chrome:scenario_descriptor_gen",
+    ]
+    sources = [
+      "$root_gen_dir/$_relative_path/chrome/scenario_config.descriptor",
+      "$root_gen_dir/$_relative_path/config.descriptor",
+    ]
     outputs =
         [ "{{bundle_resources_dir}}/gen/$_relative_path/{{source_file_part}}" ]
   }
diff --git a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
index 5ab9b58..834d61aa 100644
--- a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
@@ -407,7 +407,7 @@
 crbug.com/1176918 [ android android-pixel-4 android-webview-instrumentation renderer-skia-gl ] Pixel_Video_Context_Loss_VP9 [ Failure ]
 
 # WebGL drawingBufferStorage does not work on Android devices yet.
-crbug.com/1230619 [ android-chromium android-pie android-pixel-2 angle-disabled no-passthrough qualcomm renderer-skia-gl ] Pixel_WebGLFloat [ Failure ]
+crbug.com/1230619 [ android-chromium android android-pixel-2 angle-disabled no-passthrough qualcomm renderer-skia-gl ] Pixel_WebGLFloat [ Failure ]
 crbug.com/1230619 [ android-chromium android-pixel-6 angle-disabled arm no-clang-coverage no-passthrough target-cpu-64 ] Pixel_WebGLFloat [ Failure ]
 crbug.com/1230619 [ android-chromium android-pixel-4 android-r angle-disabled no-clang-coverage no-passthrough qualcomm renderer-skia-gl target-cpu-32 ] Pixel_WebGLFloat [ Failure ]
 
diff --git a/content/test/task_runner_deferring_throttle.cc b/content/test/task_runner_deferring_throttle.cc
index 7f14990..bd3901f 100644
--- a/content/test/task_runner_deferring_throttle.cc
+++ b/content/test/task_runner_deferring_throttle.cc
@@ -14,8 +14,8 @@
     bool defer_start,
     bool defer_redirect,
     bool defer_response,
-    NavigationHandle* handle)
-    : NavigationThrottle(handle),
+    NavigationThrottleRegistry& registry)
+    : NavigationThrottle(registry),
       defer_start_(defer_start),
       defer_redirect_(defer_redirect),
       defer_response_(defer_response),
@@ -24,15 +24,15 @@
 TaskRunnerDeferringThrottle::~TaskRunnerDeferringThrottle() = default;
 
 // static
-std::unique_ptr<NavigationThrottle> TaskRunnerDeferringThrottle::Create(
+void TaskRunnerDeferringThrottle::Create(
     scoped_refptr<base::TaskRunner> task_runner,
     bool defer_start,
     bool defer_redirect,
     bool defer_response,
-    NavigationHandle* handle) {
-  return base::WrapUnique(
-      new TaskRunnerDeferringThrottle(std::move(task_runner), defer_start,
-                                      defer_redirect, defer_response, handle));
+    NavigationThrottleRegistry& registry) {
+  registry.AddThrottle(base::WrapUnique(new TaskRunnerDeferringThrottle(
+      std::move(task_runner), defer_start, defer_redirect, defer_response,
+      registry)));
 }
 
 NavigationThrottle::ThrottleCheckResult
diff --git a/content/test/task_runner_deferring_throttle.h b/content/test/task_runner_deferring_throttle.h
index 9ac39ed..b51aa899 100644
--- a/content/test/task_runner_deferring_throttle.h
+++ b/content/test/task_runner_deferring_throttle.h
@@ -18,7 +18,7 @@
                               bool defer_start,
                               bool defer_redirect,
                               bool defer_response,
-                              NavigationHandle* handle);
+                              NavigationThrottleRegistry& registry);
 
   TaskRunnerDeferringThrottle(const TaskRunnerDeferringThrottle&) = delete;
   TaskRunnerDeferringThrottle& operator=(const TaskRunnerDeferringThrottle&) =
@@ -26,12 +26,12 @@
 
   ~TaskRunnerDeferringThrottle() override;
 
-  static std::unique_ptr<NavigationThrottle> Create(
+  static void Create(
       scoped_refptr<base::TaskRunner> task_runner,
       bool defer_start,
       bool defer_redirect,
       bool defer_response,
-      NavigationHandle* handle);
+      NavigationThrottleRegistry& registry);
 
   // NavigationThrottle:
   ThrottleCheckResult WillStartRequest() override;
diff --git a/crypto/obsolete/md5.cc b/crypto/obsolete/md5.cc
index 2eac8cb..5516691 100644
--- a/crypto/obsolete/md5.cc
+++ b/crypto/obsolete/md5.cc
@@ -10,6 +10,11 @@
 namespace crypto::obsolete {
 
 // static
+std::array<uint8_t, Md5::kSize> Md5::Hash(std::string_view data) {
+  return Hash(base::as_byte_span(data));
+}
+
+// static
 std::array<uint8_t, Md5::kSize> Md5::Hash(base::span<const uint8_t> data) {
   std::array<uint8_t, Md5::kSize> result;
   Md5 hasher;
@@ -51,6 +56,10 @@
   return Hash(data);
 }
 
+void Md5::Update(std::string_view data) {
+  Update(base::as_byte_span(data));
+}
+
 void Md5::Update(base::span<const uint8_t> data) {
   CHECK(EVP_DigestUpdate(ctx_.get(), data.data(), data.size()));
 }
diff --git a/crypto/obsolete/md5.h b/crypto/obsolete/md5.h
index c136797..46afdb7 100644
--- a/crypto/obsolete/md5.h
+++ b/crypto/obsolete/md5.h
@@ -16,6 +16,13 @@
 class Md5;
 }
 
+namespace ash::printing {
+crypto::obsolete::Md5 MakeMd5HasherForPrinterConfigurer();
+crypto::obsolete::Md5 MakeMd5HasherForUsbPrinterUtil();
+crypto::obsolete::Md5 MakeMd5HasherForZeroconf();
+std::string ServerPrinterId(const std::string& url);
+}  // namespace ash::printing
+
 namespace extensions::image_writer {
 crypto::obsolete::Md5 MakeMd5HasherForImageWriter();
 }
@@ -45,6 +52,7 @@
   Md5& operator=(Md5&& other);
   ~Md5();
 
+  void Update(std::string_view data);
   void Update(base::span<const uint8_t> data);
 
   void Finish(base::span<uint8_t, kSize> result);
@@ -62,10 +70,17 @@
   friend Md5 policy::MakeMd5HasherForPolicyEventId();
   friend Md5 extensions::image_writer::MakeMd5HasherForImageWriter();
 
+  // TODO(b/298652869): get rid of these.
+  friend Md5 ash::printing::MakeMd5HasherForPrinterConfigurer();
+  friend Md5 ash::printing::MakeMd5HasherForUsbPrinterUtil();
+  friend Md5 ash::printing::MakeMd5HasherForZeroconf();
+  friend std::string ash::printing::ServerPrinterId(const std::string& url);
+
   // TODO(https://crbug.com/416304903): get rid of this.
   friend Md5 web_app::internals::MakeMd5HasherForWebAppShortcutIcon();
 
   Md5();
+  static std::array<uint8_t, kSize> Hash(std::string_view data);
   static std::array<uint8_t, kSize> Hash(base::span<const uint8_t> data);
 
   bssl::ScopedEVP_MD_CTX ctx_;
diff --git a/docs/lldbinit.md b/docs/lldbinit.md
index 8ca91676..7817bbe 100644
--- a/docs/lldbinit.md
+++ b/docs/lldbinit.md
@@ -6,7 +6,7 @@
 
 If you have not installed LLDB yet, run `sudo apt-get install lldb` to get it.
 
-To use, add the following to your `~/.lldbinit`
+To use, add the following to your `~/.lldbinit`:
 
 ```
 # So that lldbinit.py takes precedence.
@@ -14,9 +14,15 @@
 script import lldbinit
 ```
 
-Make sure the build configurations include `is_debug=true`, this will set `symbol_level=2` by default, which is required if need to view the content of frame-level local variables.
+Replace `<your-path>` above with the absolute path containing your Chromium
+repo.
 
-If you want visualizer support for common pointer, string, and vector types in the Chromium codebase, you can also add the following:
+Make sure the build configuration includes `is_debug=true`; this will set
+`symbol_level=2` by default, which is required to view the content of
+frame-level local variables.
+
+For visualizer support for common pointer, string, and vector types in Chromium,
+add the following:
 
 ```
 script import chromium_visualizers
@@ -24,22 +30,36 @@
 
 ## How to attach to a process with lldb and start debugging
 
-- Follow the instructions above to create your `~/.lldbinit` file, don't forget to put the correct path to Chromium source in there.
-- Inside of your Chromium checkout, run `lldb out/Default/chrome` (or `out/Debug/chrome`)
-    - On Mac, most likely, `lldb out/Default/Chromium.app/Contents/MacOS/Chromium`
+- Follow the instructions above to create your `~/.lldbinit` file; don't forget
+  to put the correct absolute path to Chromium source in there.
+- In your Chromium checkout, run `lldb out/Default/chrome` (or
+  `out/Debug/chrome`)
+    - On Mac, most likely,
+      `lldb out/Default/Chromium.app/Contents/MacOS/Chromium`
 - Keep lldb running and start Chromium separately with `--no-sandbox` flag:
     - On Linux, `out/Default/chrome --no-sandbox`
     - On Mac, `out/Default/Chromium.app/Contents/MacOS/Chromium --no-sandbox`
-    - Note: if you start the process from lldb using `process launch -- --no-sandbox`, you will attach to the main browser process and will not be able to debug tab processes.
-- In Chromium, go to Customize and Control Chromium (three dots) -> More Tools -> Task Manager
-- Depending on what tab or process you want to debug, note the process ID.
+    - Note: if you start the process from lldb using
+      `process launch -- --no-sandbox`, you will attach to the main browser
+      process and will not be able to debug tab processes.
+- In Chromium, go to the three-dot menu -> _More Tools_ -> _Task Manager_
+- Note the process ID for the tab or process you want to debug.
 - In the lldb shell:
-    - Execute `process attach -p PID`. PID is the process ID of the tab (process) you want to debug.
-        - Note: it might take a while. Once lldb attaches to the process, you will see a message `Process PID stopped` and some stack traces.
-        - If you an error message such as `attach failed: Operation not permitted`, it is probably due to [ptrace Protection](https://wiki.ubuntu.com/SecurityTeam/Roadmap/KernelHardening#ptrace_Protection). You can disable this feature using `echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope`.
-    - Now you can set breakpoints, for example, `breakpoint set -f inspector_overlay_agent.cc -l 627`.
+    - Execute `process attach -p PID`. PID is the process ID of the process you
+      want to debug.
+        - Note: it might take a while. Once lldb attaches to the process, you
+          will see a message `Process PID stopped` and some stack traces.
+        - If you see an error message such as
+          `attach failed: Operation not permitted`, it is probably due to
+          [ptrace Protection](https://wiki.ubuntu.com/SecurityTeam/Roadmap/KernelHardening#ptrace_Protection).
+          You can disable this feature using
+          `echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope`.
+    - Now you can set breakpoints, e.g.,
+      `breakpoint set -f inspector_overlay_agent.cc -l 627`.
     - Execute `cont` to continue the execution of the process.
-    - Perform the actions which would trigger the breakpoint. lldb will stop the execution for you to inspect.
-    - You can pause the execution at any time by pressing Ctrl + C.
+    - Perform the actions that will trigger the breakpoint. lldb will stop the
+      execution for you to inspect.
+    - You can pause execution at any time by pressing Ctrl-C.
     - Type `help` to learn more about different lldb commands.
-    - More open-source documentation could be found [here](https://developer.apple.com/library/archive/documentation/IDEs/Conceptual/gdb_to_lldb_transition_guide/document/lldb-basics.html#//apple_ref/doc/uid/TP40012917-CH2-SW1).
+    - More open-source documentation can be found
+      [here](https://developer.apple.com/library/archive/documentation/IDEs/Conceptual/gdb_to_lldb_transition_guide/document/lldb-basics.html#//apple_ref/doc/uid/TP40012917-CH2-SW1).
diff --git a/docs/website b/docs/website
index 7122f63..47f1ea0 160000
--- a/docs/website
+++ b/docs/website
@@ -1 +1 @@
-Subproject commit 7122f63ddd3611e2418115a44dba01c08c2527e5
+Subproject commit 47f1ea00aea33ec880152302ae07f19171403754
diff --git a/extensions/common/api/OWNERS b/extensions/common/api/OWNERS
index 4a47142..0b525f43 100644
--- a/extensions/common/api/OWNERS
+++ b/extensions/common/api/OWNERS
@@ -13,4 +13,3 @@
 # New APIs or changes to existing APIs should be approved by an
 # extensions OWNER from //extensions/common/api/API_OWNERS.
 per-file automation*.idl=dtseng@chromium.org
-per-file automation*.idl=nektar@chromium.org
diff --git a/gpu/command_buffer/client/shared_image_interface.h b/gpu/command_buffer/client/shared_image_interface.h
index 45cecdd..df8cf266 100644
--- a/gpu/command_buffer/client/shared_image_interface.h
+++ b/gpu/command_buffer/client/shared_image_interface.h
@@ -179,6 +179,18 @@
       const SharedImageInfo& si_info,
       gfx::GpuMemoryBufferHandle buffer_handle) = 0;
 
+  // Creates a shared image for an existing MLTensor.
+  // Tensors store numeric values in multiple dimensions.
+  // |size| is calculated from the tensor's shape: the product of all dimensions
+  // except the last determines the height, and the last dimension becomes the
+  // width. |usage| must include gpu::SHARED_IMAGE_USAGE_WEBNN_SHARED_TENSOR.
+  // |format| should be valid and correspond to the equivalent dataType.
+  virtual scoped_refptr<ClientSharedImage> CreateSharedImageForMLTensor(
+      std::string debug_label,
+      viz::SharedImageFormat format,
+      const gfx::Size& size,
+      gpu::SharedImageUsageSet usage) = 0;
+
   // Creates a shared image with the usage of
   // gpu::SHARED_IMAGE_USAGE_CPU_WRITE_ONLY only. A shared memory buffer is
   // created internally and a shared image is created out of this buffer. This
diff --git a/gpu/command_buffer/client/test_shared_image_interface.cc b/gpu/command_buffer/client/test_shared_image_interface.cc
index 8c90dcc1..ca8736c2 100644
--- a/gpu/command_buffer/client/test_shared_image_interface.cc
+++ b/gpu/command_buffer/client/test_shared_image_interface.cc
@@ -307,6 +307,15 @@
                                                  holder_, std::move(mapping));
 }
 
+scoped_refptr<ClientSharedImage>
+TestSharedImageInterface::CreateSharedImageForMLTensor(
+    std::string debug_label,
+    viz::SharedImageFormat format,
+    const gfx::Size& size,
+    gpu::SharedImageUsageSet usage) {
+  NOTREACHED();
+}
+
 void TestSharedImageInterface::UpdateSharedImage(
     const SyncToken& sync_token,
     const Mailbox& mailbox) {
diff --git a/gpu/command_buffer/client/test_shared_image_interface.h b/gpu/command_buffer/client/test_shared_image_interface.h
index cbaa147d..4a9b558c 100644
--- a/gpu/command_buffer/client/test_shared_image_interface.h
+++ b/gpu/command_buffer/client/test_shared_image_interface.h
@@ -67,6 +67,12 @@
       const SharedImageInfo& si_info,
       gfx::GpuMemoryBufferHandle buffer_handle) override;
 
+  scoped_refptr<ClientSharedImage> CreateSharedImageForMLTensor(
+      std::string debug_label,
+      viz::SharedImageFormat format,
+      const gfx::Size& size,
+      gpu::SharedImageUsageSet usage) override;
+
   scoped_refptr<ClientSharedImage> CreateSharedImageForSoftwareCompositor(
       const SharedImageInfo& si_info) override;
 
diff --git a/gpu/command_buffer/common/shared_image_usage.cc b/gpu/command_buffer/common/shared_image_usage.cc
index 81812899..b800892 100644
--- a/gpu/command_buffer/common/shared_image_usage.cc
+++ b/gpu/command_buffer/common/shared_image_usage.cc
@@ -57,6 +57,7 @@
       {SHARED_IMAGE_USAGE_RASTER_OVER_GLES2_ONLY, "RasterOverGLES2Only"},
       {SHARED_IMAGE_USAGE_PROTECTED_VIDEO, "ProtectedVideo"},
       {SHARED_IMAGE_USAGE_WEBGPU_SHARED_BUFFER, "WebgpuSharedBuffer"},
+      {SHARED_IMAGE_USAGE_WEBNN_SHARED_TENSOR, "WebnnSharedTensor"},
       {SHARED_IMAGE_USAGE_CPU_ONLY_READ_WRITE, "CpuOnlyReadWrite"},
   };
 
diff --git a/gpu/command_buffer/common/shared_image_usage.h b/gpu/command_buffer/common/shared_image_usage.h
index 4ed0a7d..c67bfd79 100644
--- a/gpu/command_buffer/common/shared_image_usage.h
+++ b/gpu/command_buffer/common/shared_image_usage.h
@@ -106,15 +106,18 @@
   // to create a MappableSI but that format is non-texturable.
   SHARED_IMAGE_USAGE_CPU_ONLY_READ_WRITE = 1 << 25,
 
+  // Image will be used as a WebNN shared tensor
+  SHARED_IMAGE_USAGE_WEBNN_SHARED_TENSOR = 1 << 26,
+
   // Start service side only usage flags after this entry. They must be larger
   // than `LAST_CLIENT_USAGE`.
-  LAST_CLIENT_USAGE = SHARED_IMAGE_USAGE_CPU_ONLY_READ_WRITE,
+  LAST_CLIENT_USAGE = SHARED_IMAGE_USAGE_WEBNN_SHARED_TENSOR,
 
   // Image will have pixels uploaded from CPU. The backing must implement
   // `UploadFromMemory()` if it supports this usage. Clients should specify
   // SHARED_IMAGE_USAGE_CPU_WRITE_ONLY if they need to write pixels to the
   // image.
-  SHARED_IMAGE_USAGE_CPU_UPLOAD = 1 << 26,
+  SHARED_IMAGE_USAGE_CPU_UPLOAD = 1 << 27,
 
   LAST_SHARED_IMAGE_USAGE = SHARED_IMAGE_USAGE_CPU_UPLOAD
 };
diff --git a/gpu/command_buffer/service/shared_image/dawn_shared_texture_cache.cc b/gpu/command_buffer/service/shared_image/dawn_shared_texture_cache.cc
index 637821d..e615a36 100644
--- a/gpu/command_buffer/service/shared_image/dawn_shared_texture_cache.cc
+++ b/gpu/command_buffer/service/shared_image/dawn_shared_texture_cache.cc
@@ -137,16 +137,16 @@
   }
 
   auto* texture_cache = GetTextureCache(device);
-  if (!texture_cache) {
-    return;
+  if (texture_cache) {
+    auto iter = std::ranges::find_if(*texture_cache, [&](const auto& kv_pair) {
+      return kv_pair.second.Get() == texture.Get();
+    });
+    if (iter != texture_cache->end()) {
+      return;
+    }
   }
 
-  auto iter = std::ranges::find_if(*texture_cache, [&](const auto& kv_pair) {
-    return kv_pair.second.Get() == texture.Get();
-  });
-  if (iter == texture_cache->end()) {
-    texture.Destroy();
-  }
+  texture.Destroy();
 }
 
 void DawnSharedTextureCache::EraseDataIfDeviceLost() {
diff --git a/gpu/command_buffer/service/shared_image_interface_in_process.cc b/gpu/command_buffer/service/shared_image_interface_in_process.cc
index d8a6431d..afa9ec9 100644
--- a/gpu/command_buffer/service/shared_image_interface_in_process.cc
+++ b/gpu/command_buffer/service/shared_image_interface_in_process.cc
@@ -490,6 +490,15 @@
 }
 
 scoped_refptr<ClientSharedImage>
+SharedImageInterfaceInProcess::CreateSharedImageForMLTensor(
+    std::string debug_label,
+    viz::SharedImageFormat format,
+    const gfx::Size& size,
+    gpu::SharedImageUsageSet usage) {
+  NOTREACHED();
+}
+
+scoped_refptr<ClientSharedImage>
 SharedImageInterfaceInProcess::CreateSharedImageForSoftwareCompositor(
     const SharedImageInfo& si_info) {
   base::WritableSharedMemoryMapping mapping;
diff --git a/gpu/command_buffer/service/shared_image_interface_in_process.h b/gpu/command_buffer/service/shared_image_interface_in_process.h
index becd85a..40f4484 100644
--- a/gpu/command_buffer/service/shared_image_interface_in_process.h
+++ b/gpu/command_buffer/service/shared_image_interface_in_process.h
@@ -93,6 +93,11 @@
   scoped_refptr<ClientSharedImage> CreateSharedImage(
       const SharedImageInfo& si_info,
       gfx::GpuMemoryBufferHandle buffer_handle) override;
+  scoped_refptr<ClientSharedImage> CreateSharedImageForMLTensor(
+      std::string debug_label,
+      viz::SharedImageFormat format,
+      const gfx::Size& size,
+      gpu::SharedImageUsageSet usage) override;
   scoped_refptr<ClientSharedImage> CreateSharedImageForSoftwareCompositor(
       const SharedImageInfo& si_info) override;
   void UpdateSharedImage(const SyncToken& sync_token,
diff --git a/gpu/ipc/client/client_shared_image_interface.cc b/gpu/ipc/client/client_shared_image_interface.cc
index a41ffca..cbcf80c 100644
--- a/gpu/ipc/client/client_shared_image_interface.cc
+++ b/gpu/ipc/client/client_shared_image_interface.cc
@@ -197,6 +197,28 @@
 }
 
 scoped_refptr<ClientSharedImage>
+ClientSharedImageInterface::CreateSharedImageForMLTensor(
+    std::string debug_label,
+    viz::SharedImageFormat format,
+    const gfx::Size& size,
+    gpu::SharedImageUsageSet usage) {
+  CHECK(gpu::IsValidClientUsage(usage)) << uint32_t(usage);
+  CHECK(usage.Has(SHARED_IMAGE_USAGE_WEBNN_SHARED_TENSOR));
+
+  const SharedImageInfo si_info = {format, std::move(size), gfx::ColorSpace(),
+                                   usage, std::move(debug_label)};
+
+  auto mailbox = proxy_->CreateSharedImage(si_info, std::nullopt);
+  if (mailbox.IsZero()) {
+    return nullptr;
+  }
+
+  return base::WrapRefCounted<ClientSharedImage>(new ClientSharedImage(
+      AddMailbox(mailbox), si_info.meta, GenUnverifiedSyncToken(), holder_,
+      gfx::EMPTY_BUFFER));
+}
+
+scoped_refptr<ClientSharedImage>
 ClientSharedImageInterface::CreateSharedImageForSoftwareCompositor(
     const SharedImageInfo& si_info) {
   base::WritableSharedMemoryMapping mapping;
diff --git a/gpu/ipc/client/client_shared_image_interface.h b/gpu/ipc/client/client_shared_image_interface.h
index 3b3d0ed1..b0fa232 100644
--- a/gpu/ipc/client/client_shared_image_interface.h
+++ b/gpu/ipc/client/client_shared_image_interface.h
@@ -66,6 +66,12 @@
       const SharedImageInfo& si_info,
       gfx::GpuMemoryBufferHandle buffer_handle) override;
 
+  scoped_refptr<ClientSharedImage> CreateSharedImageForMLTensor(
+      std::string debug_label,
+      viz::SharedImageFormat format,
+      const gfx::Size& size,
+      gpu::SharedImageUsageSet usage) override;
+
   // Used by the software compositor only. |usage| must be
   // gpu::SHARED_IMAGE_USAGE_CPU_WRITE_ONLY. Call client_shared_image->Map()
   // later to get the shared memory mapping.
diff --git a/gpu/ipc/host/gpu_disk_cache.cc b/gpu/ipc/host/gpu_disk_cache.cc
index 9168fe8..a92646b 100644
--- a/gpu/ipc/host/gpu_disk_cache.cc
+++ b/gpu/ipc/host/gpu_disk_cache.cc
@@ -19,6 +19,11 @@
 #include "net/base/net_errors.h"
 
 namespace gpu {
+namespace {
+
+constexpr int kCacheIndex = 1;
+
+}
 
 // GpuDiskCacheEntry handles the work of caching/updating the cached
 // blobs.
@@ -206,6 +211,14 @@
 int GpuDiskCacheEntry::OpenCallback(int rv) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   if (rv == net::OK) {
+    size_t existing_size =
+        base::checked_cast<size_t>(entry_->GetDataSize(kCacheIndex));
+    if (existing_size != blob_.size()) {
+      // The blob has changed.
+      return WriteCallback(net::OK);
+    }
+
+    // The blob is unchanged.
     cache_->backend()->OnExternalCacheHit(key_);
     cache_->EntryComplete(this);
     return rv;
@@ -253,10 +266,10 @@
 
   op_type_ = WRITE_DATA;
   auto io_buf = base::MakeRefCounted<net::StringIOBuffer>(blob_);
-  return entry_->WriteData(1, 0, io_buf.get(), blob_.length(),
+  return entry_->WriteData(kCacheIndex, 0, io_buf.get(), blob_.length(),
                            base::BindOnce(&GpuDiskCacheEntry::OnOpComplete,
                                           weak_ptr_factory_.GetWeakPtr()),
-                           false);
+                           /*truncate=*/true);
 }
 
 int GpuDiskCacheEntry::IOComplete(int rv) {
diff --git a/gpu/ipc/host/gpu_disk_cache_unittest.cc b/gpu/ipc/host/gpu_disk_cache_unittest.cc
index b1f345f..fd9024b 100644
--- a/gpu/ipc/host/gpu_disk_cache_unittest.cc
+++ b/gpu/ipc/host/gpu_disk_cache_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
 #include "net/base/test_completion_callback.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace gpu {
@@ -183,6 +184,63 @@
   EXPECT_EQ(count, loaded_calls);
 }
 
+TEST_F(GpuDiskCacheTest, ModifyExistingKey) {
+  InitCache();
+
+  scoped_refptr<GpuDiskCache> cache = factory()->Create(handle_);
+  ASSERT_TRUE(cache.get() != nullptr);
+
+  {
+    net::TestCompletionCallback available_cb;
+    int rv = cache->SetAvailableCallback(available_cb.callback());
+    ASSERT_EQ(net::OK, available_cb.GetResult(rv));
+  }
+  EXPECT_EQ(0, cache->Size());
+
+  cache->Cache(kCacheKey, kCacheValue2);
+
+  {
+    net::TestCompletionCallback complete_cb;
+    int rv = cache->SetCacheCompleteCallback(complete_cb.callback());
+    ASSERT_EQ(net::OK, complete_cb.GetResult(rv));
+  }
+  EXPECT_EQ(1, cache->Size());
+
+  // Cache a different value to the same key. The new value should be smaller
+  // than old value to ensure the old value is fully removed from cache.
+  cache->Cache(kCacheKey, kCacheValue);
+  ASSERT_LT(std::string_view(kCacheKey).size(),
+            std::string_view(kCacheKey2).size());
+
+  {
+    net::TestCompletionCallback complete_cb;
+    int rv = cache->SetCacheCompleteCallback(complete_cb.callback());
+    ASSERT_EQ(net::OK, complete_cb.GetResult(rv));
+  }
+  EXPECT_EQ(1, cache->Size());
+
+  // Close, re-open, and verify that the second Cache() modified the value on
+  // disk.
+  cache = nullptr;
+  std::vector<std::pair<std::string, std::string>> loaded_data;
+  cache = factory()->Create(
+      handle_,
+      base::BindLambdaForTesting(
+          [&loaded_data](const GpuDiskCacheHandle& handle,
+                         const std::string& key, const std::string& value) {
+            loaded_data.emplace_back(key, value);
+          }));
+  ASSERT_TRUE(cache.get() != nullptr);
+
+  {
+    net::TestCompletionCallback available_cb;
+    int rv = cache->SetAvailableCallback(available_cb.callback());
+    ASSERT_EQ(net::OK, available_cb.GetResult(rv));
+  }
+  EXPECT_THAT(loaded_data,
+              testing::ElementsAre(std::pair(kCacheKey, kCacheValue)));
+}
+
 TEST_F(GpuDiskCacheTest, ReleasedCacheHandle) {
   // Init cache registers the handle.
   InitCache();
diff --git a/gpu/ipc/service/gpu_channel_shared_image_interface.cc b/gpu/ipc/service/gpu_channel_shared_image_interface.cc
index a3ed0cb..934766b 100644
--- a/gpu/ipc/service/gpu_channel_shared_image_interface.cc
+++ b/gpu/ipc/service/gpu_channel_shared_image_interface.cc
@@ -382,6 +382,15 @@
 }
 
 scoped_refptr<ClientSharedImage>
+GpuChannelSharedImageInterface::CreateSharedImageForMLTensor(
+    std::string debug_label,
+    viz::SharedImageFormat format,
+    const gfx::Size& size,
+    gpu::SharedImageUsageSet usage) {
+  NOTREACHED();
+}
+
+scoped_refptr<ClientSharedImage>
 GpuChannelSharedImageInterface::CreateSharedImageForSoftwareCompositor(
     const SharedImageInfo& si_info) {
   base::WritableSharedMemoryMapping mapping;
diff --git a/gpu/ipc/service/gpu_channel_shared_image_interface.h b/gpu/ipc/service/gpu_channel_shared_image_interface.h
index 6232593..b362726 100644
--- a/gpu/ipc/service/gpu_channel_shared_image_interface.h
+++ b/gpu/ipc/service/gpu_channel_shared_image_interface.h
@@ -69,6 +69,11 @@
   scoped_refptr<ClientSharedImage> CreateSharedImage(
       const SharedImageInfo& si_info,
       gfx::GpuMemoryBufferHandle buffer_handle) override;
+  scoped_refptr<ClientSharedImage> CreateSharedImageForMLTensor(
+      std::string debug_label,
+      viz::SharedImageFormat format,
+      const gfx::Size& size,
+      gpu::SharedImageUsageSet usage) override;
   scoped_refptr<ClientSharedImage> CreateSharedImageForSoftwareCompositor(
       const SharedImageInfo& si_info) override;
   void UpdateSharedImage(const SyncToken& sync_token,
diff --git a/infra/config/generated/builders/ci/android-desktop-arm64-compile-dbg/properties.json b/infra/config/generated/builders/ci/android-desktop-arm64-compile-dbg/properties.json
index 8bc11f5..f38c189b 100644
--- a/infra/config/generated/builders/ci/android-desktop-arm64-compile-dbg/properties.json
+++ b/infra/config/generated/builders/ci/android-desktop-arm64-compile-dbg/properties.json
@@ -52,6 +52,8 @@
           "group": "tryserver.chromium.android"
         }
       ],
+      "retry_failed_shards": true,
+      "retry_invalid_shards": true,
       "targets_spec_directory": "src/infra/config/generated/builders/ci/android-desktop-arm64-compile-dbg/targets"
     }
   },
diff --git a/infra/config/generated/builders/ci/android-desktop-arm64-compile-rel/properties.json b/infra/config/generated/builders/ci/android-desktop-arm64-compile-rel/properties.json
index aa22b83..742219d 100644
--- a/infra/config/generated/builders/ci/android-desktop-arm64-compile-rel/properties.json
+++ b/infra/config/generated/builders/ci/android-desktop-arm64-compile-rel/properties.json
@@ -52,6 +52,8 @@
           "group": "tryserver.chromium.android"
         }
       ],
+      "retry_failed_shards": true,
+      "retry_invalid_shards": true,
       "targets_spec_directory": "src/infra/config/generated/builders/ci/android-desktop-arm64-compile-rel/targets"
     }
   },
diff --git a/infra/config/generated/builders/ci/android-desktop-x64-compile-dbg/properties.json b/infra/config/generated/builders/ci/android-desktop-x64-compile-dbg/properties.json
index 6b676d7..1a13f8f 100644
--- a/infra/config/generated/builders/ci/android-desktop-x64-compile-dbg/properties.json
+++ b/infra/config/generated/builders/ci/android-desktop-x64-compile-dbg/properties.json
@@ -52,6 +52,8 @@
           "group": "tryserver.chromium.android"
         }
       ],
+      "retry_failed_shards": true,
+      "retry_invalid_shards": true,
       "targets_spec_directory": "src/infra/config/generated/builders/ci/android-desktop-x64-compile-dbg/targets"
     }
   },
diff --git a/infra/config/generated/builders/ci/android-desktop-x64-compile-rel/properties.json b/infra/config/generated/builders/ci/android-desktop-x64-compile-rel/properties.json
index 4cdf2e2d..60a3f7d 100644
--- a/infra/config/generated/builders/ci/android-desktop-x64-compile-rel/properties.json
+++ b/infra/config/generated/builders/ci/android-desktop-x64-compile-rel/properties.json
@@ -99,6 +99,8 @@
           "group": "tryserver.chromium.android"
         }
       ],
+      "retry_failed_shards": true,
+      "retry_invalid_shards": true,
       "targets_spec_directory": "src/infra/config/generated/builders/ci/android-desktop-x64-compile-rel/targets"
     }
   },
diff --git a/infra/config/generated/builders/ci/android-desktop-x64-rel-15-tests/properties.json b/infra/config/generated/builders/ci/android-desktop-x64-rel-15-tests/properties.json
index c6241d1..5498d26e 100644
--- a/infra/config/generated/builders/ci/android-desktop-x64-rel-15-tests/properties.json
+++ b/infra/config/generated/builders/ci/android-desktop-x64-rel-15-tests/properties.json
@@ -89,6 +89,8 @@
           "group": "tryserver.chromium.android"
         }
       ],
+      "retry_failed_shards": true,
+      "retry_invalid_shards": true,
       "targets_spec_directory": "src/infra/config/generated/builders/ci/android-desktop-x64-rel-15-tests/targets"
     }
   },
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg
index 8b3e53b..55f6788 100644
--- a/infra/config/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -87985,10 +87985,12 @@
     builders {
       name: "android-dawn-arm-rel"
       swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builder:android-dawn-arm-rel"
+      dimensions: "builderless:1"
       dimensions: "cpu:x86-64"
+      dimensions: "free_space:standard"
       dimensions: "os:Ubuntu-22.04"
-      dimensions: "pool:luci.chromium.try"
+      dimensions: "pool:luci.chromium.gpu.try"
+      dimensions: "ssd:0"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
         cipd_version: "latest"
@@ -88090,6 +88092,7 @@
         predicates: "has(build.output.properties.is_cached)"
         predicates: "string(build.output.properties.is_cached) == \"false\""
       }
+      max_concurrent_builds: 3
     }
     builders {
       name: "android-dawn-arm64-exp-rel"
@@ -88206,10 +88209,12 @@
     builders {
       name: "android-dawn-arm64-rel"
       swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builder:android-dawn-arm64-rel"
+      dimensions: "builderless:1"
       dimensions: "cpu:x86-64"
+      dimensions: "free_space:standard"
       dimensions: "os:Ubuntu-22.04"
-      dimensions: "pool:luci.chromium.try"
+      dimensions: "pool:luci.chromium.gpu.try"
+      dimensions: "ssd:0"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
         cipd_version: "latest"
@@ -88311,6 +88316,7 @@
         predicates: "has(build.output.properties.is_cached)"
         predicates: "string(build.output.properties.is_cached) == \"false\""
       }
+      max_concurrent_builds: 3
     }
     builders {
       name: "android-dawn-arm64-s24-rel"
@@ -95045,10 +95051,12 @@
     builders {
       name: "dawn-android-arm-deps-rel"
       swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builder:dawn-android-arm-deps-rel"
+      dimensions: "builderless:1"
       dimensions: "cpu:x86-64"
+      dimensions: "free_space:standard"
       dimensions: "os:Ubuntu-22.04"
-      dimensions: "pool:luci.chromium.try"
+      dimensions: "pool:luci.chromium.gpu.try"
+      dimensions: "ssd:0"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
         cipd_version: "latest"
@@ -95151,14 +95159,17 @@
         predicates: "has(build.output.properties.is_cached)"
         predicates: "string(build.output.properties.is_cached) == \"false\""
       }
+      max_concurrent_builds: 5
     }
     builders {
       name: "dawn-android-arm64-deps-rel"
       swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builder:dawn-android-arm64-deps-rel"
+      dimensions: "builderless:1"
       dimensions: "cpu:x86-64"
+      dimensions: "free_space:standard"
       dimensions: "os:Ubuntu-22.04"
-      dimensions: "pool:luci.chromium.try"
+      dimensions: "pool:luci.chromium.gpu.try"
+      dimensions: "ssd:0"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
         cipd_version: "latest"
@@ -95261,14 +95272,17 @@
         predicates: "has(build.output.properties.is_cached)"
         predicates: "string(build.output.properties.is_cached) == \"false\""
       }
+      max_concurrent_builds: 5
     }
     builders {
       name: "dawn-chromium-presubmit"
       swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builder:dawn-chromium-presubmit"
+      dimensions: "builderless:1"
       dimensions: "cpu:x86-64"
+      dimensions: "free_space:standard"
       dimensions: "os:Ubuntu-22.04"
-      dimensions: "pool:luci.chromium.try"
+      dimensions: "pool:luci.chromium.gpu.try"
+      dimensions: "ssd:0"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
         cipd_version: "latest"
@@ -95370,14 +95384,17 @@
         predicates: "has(build.output.properties.is_cached)"
         predicates: "string(build.output.properties.is_cached) == \"false\""
       }
+      max_concurrent_builds: 2
     }
     builders {
       name: "dawn-linux-x64-deps-rel"
       swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builder:dawn-linux-x64-deps-rel"
+      dimensions: "builderless:1"
       dimensions: "cpu:x86-64"
+      dimensions: "free_space:standard"
       dimensions: "os:Ubuntu-22.04"
-      dimensions: "pool:luci.chromium.try"
+      dimensions: "pool:luci.chromium.gpu.try"
+      dimensions: "ssd:0"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
         cipd_version: "latest"
@@ -95480,6 +95497,7 @@
         predicates: "has(build.output.properties.is_cached)"
         predicates: "string(build.output.properties.is_cached) == \"false\""
       }
+      max_concurrent_builds: 5
     }
     builders {
       name: "dawn-mac-arm64-deps-rel"
@@ -112784,10 +112802,12 @@
     builders {
       name: "linux-dawn-rel"
       swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builder:linux-dawn-rel"
+      dimensions: "builderless:1"
       dimensions: "cpu:x86-64"
+      dimensions: "free_space:standard"
       dimensions: "os:Ubuntu-22.04"
-      dimensions: "pool:luci.chromium.try"
+      dimensions: "pool:luci.chromium.gpu.try"
+      dimensions: "ssd:0"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
         cipd_version: "latest"
@@ -112889,6 +112909,7 @@
         predicates: "has(build.output.properties.is_cached)"
         predicates: "string(build.output.properties.is_cached) == \"false\""
       }
+      max_concurrent_builds: 3
     }
     builders {
       name: "linux-dcheck-off-rel"
diff --git a/infra/config/subprojects/chromium/ci/chromium.android.desktop.star b/infra/config/subprojects/chromium/ci/chromium.android.desktop.star
index 7a2964f..c7fdd9e 100644
--- a/infra/config/subprojects/chromium/ci/chromium.android.desktop.star
+++ b/infra/config/subprojects/chromium/ci/chromium.android.desktop.star
@@ -15,6 +15,12 @@
 ci.defaults.set(
     executable = ci.DEFAULT_EXECUTABLE,
     builder_group = "chromium.android.desktop",
+    builder_config_settings = builder_config.ci_settings(
+        retry_failed_shards = True,
+        # Android emulator tasks often flake during emulator start-up, which
+        # leads to the whole shard being marked as invalid.
+        retry_invalid_shards = True,
+    ),
     pool = ci.DEFAULT_POOL,
     builderless = False,
     os = os.LINUX_DEFAULT,
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.dawn.star b/infra/config/subprojects/chromium/try/tryserver.chromium.dawn.star
index 497f0901..a60e9b5c 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.dawn.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.dawn.star
@@ -52,8 +52,12 @@
         retry_without_patch = False,
     ),
     gn_args = "ci/Dawn Chromium Presubmit",
+    pool = "luci.chromium.gpu.try",
+    builderless = True,
+    os = os.LINUX_DEFAULT,
     execution_timeout = 30 * time.minute,
     main_list_view = "try",
+    max_concurrent_builds = 2,
 )
 
 try_.builder(
@@ -64,7 +68,11 @@
         "ci/Dawn Android arm DEPS Release (Pixel 4)",
     ],
     gn_args = "ci/Dawn Android arm DEPS Builder",
+    pool = "luci.chromium.gpu.try",
+    builderless = True,
+    os = os.LINUX_DEFAULT,
     main_list_view = "try",
+    max_concurrent_builds = 5,
     test_presentation = resultdb.test_presentation(
         grouping_keys = ["status", "v.test_suite", "v.gpu"],
     ),
@@ -92,7 +100,11 @@
         "ci/Dawn Android arm64 DEPS Release (Pixel 6)",
     ],
     gn_args = "ci/Dawn Android arm64 DEPS Builder",
+    pool = "luci.chromium.gpu.try",
+    builderless = True,
+    os = os.LINUX_DEFAULT,
     main_list_view = "try",
+    max_concurrent_builds = 5,
     test_presentation = resultdb.test_presentation(
         grouping_keys = ["status", "v.test_suite", "v.gpu"],
     ),
@@ -123,7 +135,11 @@
         "ci/Dawn Linux x64 DEPS Release (NVIDIA)",
     ],
     gn_args = "ci/Dawn Linux x64 DEPS Builder",
+    pool = "luci.chromium.gpu.try",
+    builderless = True,
+    os = os.LINUX_DEFAULT,
     main_list_view = "try",
+    max_concurrent_builds = 5,
     test_presentation = resultdb.test_presentation(
         grouping_keys = ["status", "v.test_suite", "v.gpu"],
     ),
@@ -309,6 +325,10 @@
         "ci/Dawn Android arm Release (Pixel 4)",
     ],
     gn_args = "ci/Dawn Android arm Builder",
+    pool = "luci.chromium.gpu.try",
+    builderless = True,
+    os = os.LINUX_DEFAULT,
+    max_concurrent_builds = 3,
     test_presentation = resultdb.test_presentation(
         grouping_keys = ["status", "v.test_suite", "v.gpu"],
     ),
@@ -321,6 +341,10 @@
         "ci/Dawn Android arm64 Release (Pixel 6)",
     ],
     gn_args = "ci/Dawn Android arm64 Builder",
+    pool = "luci.chromium.gpu.try",
+    builderless = True,
+    os = os.LINUX_DEFAULT,
+    max_concurrent_builds = 3,
     test_presentation = resultdb.test_presentation(
         grouping_keys = ["status", "v.test_suite", "v.gpu"],
     ),
@@ -404,6 +428,10 @@
         "ci/Dawn Linux x64 Release (NVIDIA)",
     ],
     gn_args = "ci/Dawn Linux x64 Builder",
+    pool = "luci.chromium.gpu.try",
+    builderless = True,
+    os = os.LINUX_DEFAULT,
+    max_concurrent_builds = 3,
     test_presentation = resultdb.test_presentation(
         grouping_keys = ["status", "v.test_suite", "v.gpu"],
     ),
diff --git a/infra/inclusive_language_presubmit_exempt_dirs.txt b/infra/inclusive_language_presubmit_exempt_dirs.txt
index ac24ecd..0ef55bd 100644
--- a/infra/inclusive_language_presubmit_exempt_dirs.txt
+++ b/infra/inclusive_language_presubmit_exempt_dirs.txt
@@ -657,7 +657,6 @@
 third_party/rust/chromium_crates_io/vendor/codespan-reporting-v0_12 4 2
 third_party/rust/chromium_crates_io/vendor/codespan-reporting-v0_12/examples 1 1
 third_party/rust/chromium_crates_io/vendor/crc32fast-v1/.github/workflows 2 1
-third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8 3 3
 third_party/rust/chromium_crates_io/vendor/cxx-v1 1 1
 third_party/rust/chromium_crates_io/vendor/cxx-v1/book 1 1
 third_party/rust/chromium_crates_io/vendor/cxx-v1/book/src 3 1
@@ -737,8 +736,8 @@
 third_party/rust/chromium_crates_io/vendor/windows-sys-v0_52 1 1
 third_party/rust/chromium_crates_io/vendor/windows-sys-v0_52/src/Windows/Wdk/System/SystemServices 1 1
 third_party/rust/chromium_crates_io/vendor/windows-sys-v0_52/src/Windows/Win32/Networking/Clustering 2 1
-third_party/rust/chromium_crates_io/vendor/zip-v2 2 2
-third_party/rust/chromium_crates_io/vendor/zip-v2/src 1 1
+third_party/rust/chromium_crates_io/vendor/zip-v3 2 2
+third_party/rust/chromium_crates_io/vendor/zip-v3/src 1 1
 third_party/screen-ai 1 1
 third_party/sentencepiece/src 2 2
 third_party/sentencepiece/src/doc 3 1
diff --git a/internal b/internal
index 91e81b8..e5f2041 160000
--- a/internal
+++ b/internal
@@ -1 +1 @@
-Subproject commit 91e81b807fa486c4f6b6400fbef49c6983b23791
+Subproject commit e5f2041ac763622cc191728d4b6db0d67c86e284
diff --git a/ios/chrome/app/profile/first_run_profile_agent.h b/ios/chrome/app/profile/first_run_profile_agent.h
index 34fd752..6c0c36a 100644
--- a/ios/chrome/app/profile/first_run_profile_agent.h
+++ b/ios/chrome/app/profile/first_run_profile_agent.h
@@ -10,6 +10,10 @@
 // ProfileAgent that displays the first run UI when needed and handles the
 // ProfileInitStage::kFirstRun stage (including the transition to next stage).
 @interface FirstRunProfileAgent : ObservingProfileAgent
+
+// Indicates to this class that the Tab Grid was presented.
+- (void)tabGridWasPresented;
+
 @end
 
 #endif  // IOS_CHROME_APP_PROFILE_FIRST_RUN_PROFILE_AGENT_H_
diff --git a/ios/chrome/app/profile/first_run_profile_agent.mm b/ios/chrome/app/profile/first_run_profile_agent.mm
index 5ccdb01..bc9f1784 100644
--- a/ios/chrome/app/profile/first_run_profile_agent.mm
+++ b/ios/chrome/app/profile/first_run_profile_agent.mm
@@ -26,6 +26,7 @@
 #import "ios/chrome/browser/shared/public/commands/application_commands.h"
 #import "ios/chrome/browser/shared/public/commands/command_dispatcher.h"
 #import "ios/chrome/browser/shared/public/commands/guided_tour_commands.h"
+#import "ios/chrome/browser/shared/public/commands/tab_grid_toolbar_commands.h"
 #import "ios/chrome/browser/shared/public/features/features.h"
 #import "ios/chrome/browser/signin/model/signin_util.h"
 
@@ -53,10 +54,30 @@
   // Coordinator for the first step of the guided tour.
   GuidedTourCoordinator* _guidedTourCoordinator;
 
+  // The current step in the guided tour.
+  GuidedTourStep _currentGuidedTourStep;
+
   // Used to force the device orientation in portrait mode on iPhone.
   std::unique_ptr<ScopedForcePortraitOrientation> _scopedForceOrientation;
 }
 
+#pragma mark - Public
+
+- (void)tabGridWasPresented {
+  if (_currentGuidedTourStep == GuidedTourStepTabGridIncognito) {
+    id<BrowserProvider> presentingInterface =
+        _presentingSceneState.browserProviderInterface.currentBrowserProvider;
+    Browser* browser = presentingInterface.browser;
+    __weak FirstRunProfileAgent* weakSelf = self;
+    ProceduralBlock completion = ^{
+      [weakSelf showLongPressStep];
+    };
+    id<TabGridToolbarCommands> handler = HandlerForProtocol(
+        browser->GetCommandDispatcher(), TabGridToolbarCommands);
+    [handler showGuidedTourIncognitoStepWithDismissalCompletion:completion];
+  }
+}
+
 #pragma mark - SceneStateObserver
 
 - (void)sceneStateDidDisableUI:(SceneState*)sceneState {
@@ -183,6 +204,7 @@
 }
 
 - (void)showNTPStep {
+  _currentGuidedTourStep = GuidedTourStepNTP;
   // Command Dispatcher to show NTP IPH
   id<BrowserProvider> presentingInterface =
       _presentingSceneState.browserProviderInterface.currentBrowserProvider;
@@ -199,17 +221,22 @@
   [_guidedTourCoordinator start];
 }
 
+- (void)showLongPressStep {
+  // TODO(crbug.com/413461470): Implement
+}
+
 #pragma mark - GuidedTourCoordinatorDelegate
 
 - (void)stepCompleted:(GuidedTourStep)step {
+  CHECK_EQ(step, _currentGuidedTourStep);
   if (step == GuidedTourStepNTP) {
+    _currentGuidedTourStep = GuidedTourStepTabGridIncognito;
     id<BrowserProvider> presentingInterface =
         _presentingSceneState.browserProviderInterface.currentBrowserProvider;
     Browser* browser = presentingInterface.browser;
     id<ApplicationCommands> applicationHandler = HandlerForProtocol(
         browser->GetCommandDispatcher(), ApplicationCommands);
     [applicationHandler displayTabGridInMode:TabGridOpeningMode::kRegular];
-    // TODO(crbug.com/413461470): Trigger next step.
   }
 }
 
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index 8e1eef95..b672a71 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -1891,9 +1891,6 @@
       <message name="IDS_IOS_DOCKING_EDIT_HOME_SCREEN_LOTTIE_INSTRUCTION" desc="Title of the instruction shown in the Docking Promo Lottie, which describes the Edit Home Screen text when the user long presses on the Chrome app.">
         Edit Home Screen
       </message>
-      <message name="IDS_IOS_DOCKING_PROMO_NO_THANKS_BUTTON_TITLE" desc="Title of the button that dismisses the Docking Promo.">
-        No Thanks
-      </message>
       <message name="IDS_IOS_DOCKING_PROMO_PRIMARY_BUTTON_TITLE" desc="Title of the primary button in the Docking Promo, which confirms the promo has been seen by the user.">
         Got it
       </message>
@@ -2317,6 +2314,9 @@
       <message name="IDS_IOS_ENTERPRISE_SIGNED_OUT_MESSAGE_WITH_UNO" desc="Message displayed in a prompt when the user is signed out due to browser sign-in becoming disabled by policy. Related with IDS_IOS_ENTERPRISE_SIGNED_OUT.">
         Your organization turned off sign-in. New bookmarks, passwords, and more will be saved only to this device.
       </message>
+      <message name="IDS_IOS_ENTERPRISE_SWITCH_TO_MANAGED_ACCOUNT_WIDE_SCREEN" desc="The snackbar message subtitle to confirm the signed-in account after a successful account switch. For enterprise users, we remind them that their Google Account is managed by their company, school, or organization. This string does not have line break, so terminal punctuation is required for sentence fragments. [iOS only]">
+        <ph name="EMAIL">$1<ex>johndoe@acme.com</ex></ph>. This account is managed.
+      </message>
       <message name="IDS_IOS_ENTERPRISE_SWITCH_TO_MANAGED_WIDE_SCREEN" desc="The snackbar message subtitle to confirm the signed-in account after a successful account switch. For enterprise users, we remind them that their Google Account is managed by their company, school, or organization. This string does not have line break, so terminal punctuation is required for sentence fragments. [iOS only]">
         <ph name="EMAIL">$1<ex>johndoe@acme.com</ex></ph>. Managed by your organization.
       </message>
@@ -2452,6 +2452,12 @@
       <message name="IDS_IOS_FIRST_RUN_GUIDED_TOUR_PROMPT_BUTTON_TITLE" desc="Title of the button for the user to start the First Run Guided Tour." meaning="Title-cased">
       Show me around
     </message>
+      <message name="IDS_IOS_FIRST_RUN_GUIDED_TOUR_TAB_GRID_INCOGNITO_IPH_TEXT" desc="Text of the Guided Tour IPH for the step focusing on the tab grid's incognito page control item." meaning="Title-cased">
+          Keep your browsing private by switching to Incognito mode
+    </message>
+      <message name="IDS_IOS_FIRST_RUN_GUIDED_TOUR_TAB_GRID_INCOGNITO_IPH_TITLE" desc="Title of the Guided Tour IPH for the step focusing on the tab grid's incognito page control item." meaning="Title-cased">
+          Browse in Incognito
+    </message>
       <message name="IDS_IOS_FIRST_RUN_SCREEN_READ_MORE" desc="The label on the primary button of some screens in the first run experience if the content requires scrolling to the end [iOS only]">
         More
       </message>
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_DOCKING_PROMO_NO_THANKS_BUTTON_TITLE.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_DOCKING_PROMO_NO_THANKS_BUTTON_TITLE.png.sha1
deleted file mode 100644
index bb210fa1..0000000
--- a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_DOCKING_PROMO_NO_THANKS_BUTTON_TITLE.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-6856c9e5b6b153ef7a31110ca2416b54250fe8ef
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_ENTERPRISE_SWITCH_TO_MANAGED_ACCOUNT_WIDE_SCREEN.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_ENTERPRISE_SWITCH_TO_MANAGED_ACCOUNT_WIDE_SCREEN.png.sha1
new file mode 100644
index 0000000..9a18dd8
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_ENTERPRISE_SWITCH_TO_MANAGED_ACCOUNT_WIDE_SCREEN.png.sha1
@@ -0,0 +1 @@
+2c0d53d7ca764333f405630ef6565086661a1538
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_FIRST_RUN_GUIDED_TOUR_TAB_GRID_INCOGNITO_IPH_TEXT.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_FIRST_RUN_GUIDED_TOUR_TAB_GRID_INCOGNITO_IPH_TEXT.png.sha1
new file mode 100644
index 0000000..60fc06c
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_FIRST_RUN_GUIDED_TOUR_TAB_GRID_INCOGNITO_IPH_TEXT.png.sha1
@@ -0,0 +1 @@
+2ced47aa88caa5d7e0333c6fc2aa512d2ad12ace
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_FIRST_RUN_GUIDED_TOUR_TAB_GRID_INCOGNITO_IPH_TITLE.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_FIRST_RUN_GUIDED_TOUR_TAB_GRID_INCOGNITO_IPH_TITLE.png.sha1
new file mode 100644
index 0000000..60fc06c
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_FIRST_RUN_GUIDED_TOUR_TAB_GRID_INCOGNITO_IPH_TITLE.png.sha1
@@ -0,0 +1 @@
+2ced47aa88caa5d7e0333c6fc2aa512d2ad12ace
\ No newline at end of file
diff --git a/ios/chrome/browser/autofill/ui_bundled/ios_chrome_payments_autofill_client.h b/ios/chrome/browser/autofill/ui_bundled/ios_chrome_payments_autofill_client.h
index c5a69c7..f8de6ae 100644
--- a/ios/chrome/browser/autofill/ui_bundled/ios_chrome_payments_autofill_client.h
+++ b/ios/chrome/browser/autofill/ui_bundled/ios_chrome_payments_autofill_client.h
@@ -12,6 +12,8 @@
 #import "base/memory/raw_ref.h"
 #import "base/memory/weak_ptr.h"
 #import "components/autofill/core/browser/autofill_progress_dialog_type.h"
+#import "components/autofill/core/browser/payments/autofill_save_card_delegate.h"
+#import "components/autofill/core/browser/payments/autofill_save_card_ui_info.h"
 #import "components/autofill/core/browser/payments/payments_autofill_client.h"
 #import "components/autofill/core/browser/ui/payments/autofill_progress_dialog_controller_impl.h"
 #include "components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_controller_impl.h"
@@ -141,6 +143,14 @@
   }
 
  private:
+  // Shows save card UI offering upload or local save. If
+  // `should_show_save_card_bottomsheet` is true shows bottomsheet otherwise
+  // shows infobar.
+  void ShowSaveCreditCard(
+      AutofillSaveCardUiInfo ui_info,
+      std::unique_ptr<AutofillSaveCardDelegate> save_card_delegate,
+      bool should_show_save_card_bottomsheet);
+
   const raw_ref<autofill::ChromeAutofillClientIOS> client_;
 
   const raw_ref<infobars::InfoBarManager> infobar_manager_;
@@ -190,8 +200,8 @@
   base::WeakPtr<SaveCardBottomSheetModel> save_card_bottom_sheet_model_;
 
   // Indicates whether the save card bottom sheet should be presented instead of
-  // the infobar.
-  bool show_save_card_bottom_sheet_;
+  // the infobar for uploading the card to server.
+  bool show_save_card_bottom_sheet_for_upload_;
 };
 
 }  // namespace payments
diff --git a/ios/chrome/browser/autofill/ui_bundled/ios_chrome_payments_autofill_client.mm b/ios/chrome/browser/autofill/ui_bundled/ios_chrome_payments_autofill_client.mm
index 48ac50e..2342dc3 100644
--- a/ios/chrome/browser/autofill/ui_bundled/ios_chrome_payments_autofill_client.mm
+++ b/ios/chrome/browser/autofill/ui_bundled/ios_chrome_payments_autofill_client.mm
@@ -15,6 +15,7 @@
 #import "base/strings/sys_string_conversions.h"
 #import "components/autofill/core/browser/autofill_progress_dialog_type.h"
 #import "components/autofill/core/browser/data_manager/payments/payments_data_manager.h"
+#import "components/autofill/core/browser/field_types.h"
 #import "components/autofill/core/browser/metrics/payments/credit_card_save_metrics.h"
 #import "components/autofill/core/browser/payments/autofill_error_dialog_context.h"
 #import "components/autofill/core/browser/payments/autofill_save_card_delegate.h"
@@ -61,17 +62,28 @@
                                       std::move(delegate));
 }
 
-// Returns true if the card to be saved has 0 strikes (i.e the user has not
+// Returns true if the card to be saved locally has 0 strikes (i.e the user has
+// not rejected offer for the card previously) and
+// `kAutofillLocalSaveCardBottomSheet` feature is enabled.
+bool ShouldShowSaveCardBottomSheetForLocalSave(
+    const PaymentsAutofillClient::SaveCreditCardOptions& options) {
+  return options.num_strikes.has_value() && options.num_strikes.value() == 0 &&
+         base::FeatureList::IsEnabled(
+             features::kAutofillLocalSaveCardBottomSheet);
+}
+
+// Returns true if the card to be uploaded has 0 strikes (i.e the user has not
 // rejected upload offer for the card previously) and, both cardholder name
 // and expiration date are present and `kAutofillSaveCardBottomSheet` feature
 // is enabled.
-bool ShowSaveCardBottomSheet(
+bool ShouldShowSaveCardBottomSheetForUploadSave(
     const PaymentsAutofillClient::SaveCreditCardOptions& options) {
   return options.num_strikes.has_value() && options.num_strikes.value() == 0 &&
          !options.should_request_name_from_user &&
          !options.should_request_expiration_date_from_user &&
          base::FeatureList::IsEnabled(features::kAutofillSaveCardBottomSheet);
 }
+
 }  // namespace
 
 IOSChromePaymentsAutofillClient::IOSChromePaymentsAutofillClient(
@@ -104,11 +116,14 @@
     SaveCreditCardOptions options,
     LocalSaveCardPromptCallback callback) {
   DCHECK(options.show_prompt);
-  infobar_manager_->AddInfoBar(CreateSaveCardInfoBarMobile(
-      std::make_unique<AutofillSaveCardInfoBarDelegateIOS>(
-          AutofillSaveCardUiInfo::CreateForLocalSave(options, card),
-          std::make_unique<AutofillSaveCardDelegate>(std::move(callback),
-                                                     options))));
+  CHECK(!card.GetInfo(CREDIT_CARD_EXP_MONTH, client_->GetAppLocale()).empty());
+  CHECK(!card.GetInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, client_->GetAppLocale())
+             .empty());
+
+  ShowSaveCreditCard(
+      AutofillSaveCardUiInfo::CreateForLocalSave(options, card),
+      std::make_unique<AutofillSaveCardDelegate>(std::move(callback), options),
+      ShouldShowSaveCardBottomSheetForLocalSave(options));
 }
 
 void IOSChromePaymentsAutofillClient::ShowSaveCreditCardToCloud(
@@ -122,36 +137,13 @@
       client_->GetIdentityManager()->FindExtendedAccountInfo(
           client_->GetIdentityManager()->GetPrimaryAccountInfo(
               signin::ConsentLevel::kSignin));
-  AutofillSaveCardUiInfo ui_info = AutofillSaveCardUiInfo::CreateForUploadSave(
-      options, card, legal_message_lines, account_info);
-  // Delegate providing callbacks for the save card UI.
-  std::unique_ptr<AutofillSaveCardDelegate> common_delegate =
-      std::make_unique<AutofillSaveCardDelegate>(std::move(callback), options);
-  show_save_card_bottom_sheet_ = ShowSaveCardBottomSheet(options);
-  if (show_save_card_bottom_sheet_) {
-    AutofillBottomSheetTabHelper* bottom_sheet_tab_helper =
-        AutofillBottomSheetTabHelper::FromWebState(web_state_);
-    std::unique_ptr<SaveCardBottomSheetModel> model =
-        std::make_unique<SaveCardBottomSheetModel>(std::move(ui_info),
-                                                   std::move(common_delegate));
-    save_card_bottom_sheet_model_ = model->GetWeakPtr();
-    bottom_sheet_tab_helper->ShowSaveCardBottomSheet(std::move(model));
-    return;
-  }
-  if (base::FeatureList::IsEnabled(features::kAutofillSaveCardBottomSheet)) {
-    // Logs the decision to not show the bottomsheet for users with flag
-    // enabled.
-    autofill_metrics::LogSaveCreditCardPromptResultIOS(
-        autofill::autofill_metrics::SaveCreditCardPromptResultIOS::kNotShown,
-        common_delegate->is_for_upload(),
-        common_delegate->GetSaveCreditCardOptions(),
-        autofill::autofill_metrics::SaveCreditCardPromptOverlayType::
-            kBottomSheet);
-  }
-
-  infobar_manager_->AddInfoBar(CreateSaveCardInfoBarMobile(
-      std::make_unique<AutofillSaveCardInfoBarDelegateIOS>(
-          std::move(ui_info), std::move(common_delegate))));
+  show_save_card_bottom_sheet_for_upload_ =
+      ShouldShowSaveCardBottomSheetForUploadSave(options);
+  ShowSaveCreditCard(
+      AutofillSaveCardUiInfo::CreateForUploadSave(
+          options, card, legal_message_lines, account_info),
+      std::make_unique<AutofillSaveCardDelegate>(std::move(callback), options),
+      show_save_card_bottom_sheet_for_upload_);
 }
 
 // TODO(crbug.com/413418918): Remove optional from
@@ -171,7 +163,7 @@
   } else if (save_card_bottom_sheet_model_) {
     save_card_bottom_sheet_model_->CreditCardUploadCompleted(
         card_saved, std::move(callback));
-  } else if (show_save_card_bottom_sheet_) {
+  } else if (show_save_card_bottom_sheet_for_upload_) {
     // If a bottomsheet was showing before and was dismissed before getting the
     // save card result, the weak ref to save card bottomsheet model would be
     // invalid since model's lifecycle is same as the UI's and, the callback
@@ -441,4 +433,36 @@
   return client_->GetPersonalDataManager().payments_data_manager();
 }
 
+void IOSChromePaymentsAutofillClient::ShowSaveCreditCard(
+    AutofillSaveCardUiInfo ui_info,
+    std::unique_ptr<AutofillSaveCardDelegate> save_card_delegate,
+    bool should_show_save_card_bottomsheet) {
+  if (should_show_save_card_bottomsheet) {
+    AutofillBottomSheetTabHelper* bottom_sheet_tab_helper =
+        AutofillBottomSheetTabHelper::FromWebState(web_state_);
+    std::unique_ptr<SaveCardBottomSheetModel> model =
+        std::make_unique<SaveCardBottomSheetModel>(
+            std::move(ui_info), std::move(save_card_delegate));
+    save_card_bottom_sheet_model_ = model->GetWeakPtr();
+    bottom_sheet_tab_helper->ShowSaveCardBottomSheet(std::move(model));
+    return;
+  }
+  if (save_card_delegate->is_for_upload()
+          ? base::FeatureList::IsEnabled(features::kAutofillSaveCardBottomSheet)
+          : base::FeatureList::IsEnabled(
+                features::kAutofillLocalSaveCardBottomSheet)) {
+    // Logs the decision to not show the bottomsheet for users with flag
+    // enabled.
+    autofill_metrics::LogSaveCreditCardPromptResultIOS(
+        autofill::autofill_metrics::SaveCreditCardPromptResultIOS::kNotShown,
+        save_card_delegate->is_for_upload(),
+        save_card_delegate->GetSaveCreditCardOptions(),
+        autofill::autofill_metrics::SaveCreditCardPromptOverlayType::
+            kBottomSheet);
+  }
+  infobar_manager_->AddInfoBar(CreateSaveCardInfoBarMobile(
+      std::make_unique<AutofillSaveCardInfoBarDelegateIOS>(
+          std::move(ui_info), std::move(save_card_delegate))));
+}
+
 }  // namespace autofill::payments
diff --git a/ios/chrome/browser/autofill/ui_bundled/ios_chrome_payments_autofill_client_unittest.mm b/ios/chrome/browser/autofill/ui_bundled/ios_chrome_payments_autofill_client_unittest.mm
index c85f1acf..1d573963 100644
--- a/ios/chrome/browser/autofill/ui_bundled/ios_chrome_payments_autofill_client_unittest.mm
+++ b/ios/chrome/browser/autofill/ui_bundled/ios_chrome_payments_autofill_client_unittest.mm
@@ -13,6 +13,7 @@
 #import "components/autofill/core/browser/metrics/payments/credit_card_save_metrics.h"
 #import "components/autofill/core/browser/payments/autofill_error_dialog_context.h"
 #import "components/autofill/core/browser/payments/virtual_card_enrollment_manager.h"
+#import "components/autofill/core/browser/test_utils/autofill_test_utils.h"
 #import "components/autofill/core/browser/ui/payments/virtual_card_enroll_ui_model.h"
 #import "components/autofill/core/common/autofill_payments_features.h"
 #import "components/autofill/ios/browser/autofill_agent.h"
@@ -198,13 +199,31 @@
   std::unique_ptr<TestChromeAutofillClient> autofill_client_;
 };
 
+// Test that save card bottomsheet is not shown for local save when flag is
+// disabled.
+TEST_F(IOSChromePaymentsAutofillClientTest,
+       DoNotShowLocalSaveCardBottomSheet_FlagDisabled) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndDisableFeature(
+      autofill::features::kAutofillLocalSaveCardBottomSheet);
+  payments_client()->ShowSaveCreditCardLocally(
+      test::GetCreditCard(),
+      payments::PaymentsAutofillClient::SaveCreditCardOptions()
+          .with_num_strikes(0)
+          .with_show_prompt(true),
+      base::DoNothing());
+  EXPECT_FALSE([autofill_commands() showSaveCardBottomSheetCalled]);
+}
+
+// Test that save card bottomsheet is not shown for upload save when flag is
+// disabled.
 TEST_F(IOSChromePaymentsAutofillClientTest,
        DoNotShowSaveCardBottomSheet_FlagDisabled) {
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndDisableFeature(
       autofill::features::kAutofillSaveCardBottomSheet);
   payments_client()->ShowSaveCreditCardToCloud(
-      CreditCard(), LegalMessageLines(),
+      test::GetCreditCard(), LegalMessageLines(),
       payments::PaymentsAutofillClient::SaveCreditCardOptions()
           .with_num_strikes(0)
           .with_show_prompt(true),
@@ -221,7 +240,7 @@
        CreditCardUploadCompleted_CardSaved_WithInfobar) {
   // Shows card upload in an infobar for a card with 1 strike.
   payments_client()->ShowSaveCreditCardToCloud(
-      CreditCard(), LegalMessageLines(),
+      test::GetCreditCard(), LegalMessageLines(),
       payments::PaymentsAutofillClient::SaveCreditCardOptions()
           .with_num_strikes(1)
           .with_show_prompt(true),
@@ -256,7 +275,7 @@
        CreditCardUploadCompleted_CardNotSaved_WithInfobar) {
   // Shows card upload in an infobar for a card with 1 strike.
   payments_client()->ShowSaveCreditCardToCloud(
-      CreditCard(), LegalMessageLines(),
+      test::GetCreditCard(), LegalMessageLines(),
       payments::PaymentsAutofillClient::SaveCreditCardOptions()
           .with_num_strikes(1)
           .with_show_prompt(true),
@@ -298,7 +317,7 @@
     CreditCardUploadCompleted_ClientSideTimeout_WithInfobar_NoErrorConfirmation) {
   // Shows card upload in an infobar for a card with 1 strike.
   payments_client()->ShowSaveCreditCardToCloud(
-      CreditCard(), LegalMessageLines(),
+      test::GetCreditCard(), LegalMessageLines(),
       payments::PaymentsAutofillClient::SaveCreditCardOptions()
           .with_num_strikes(1)
           .with_show_prompt(true),
@@ -413,7 +432,7 @@
 TEST_F(IOSChromePaymentsAutofillClientWithSaveCardBottomSheetTest,
        ShowSaveCardBottomSheet_FlagEnabled) {
   payments_client()->ShowSaveCreditCardToCloud(
-      CreditCard(), LegalMessageLines(),
+      test::GetCreditCard(), LegalMessageLines(),
       payments::PaymentsAutofillClient::SaveCreditCardOptions()
           .with_num_strikes(0)
           .with_show_prompt(true),
@@ -426,7 +445,7 @@
   base::HistogramTester histogram_tester;
 
   payments_client()->ShowSaveCreditCardToCloud(
-      CreditCard(), LegalMessageLines(),
+      test::GetCreditCard(), LegalMessageLines(),
       payments::PaymentsAutofillClient::SaveCreditCardOptions()
           .with_num_strikes(1)
           .with_show_prompt(true),
@@ -445,7 +464,7 @@
   base::HistogramTester histogram_tester;
 
   payments_client()->ShowSaveCreditCardToCloud(
-      CreditCard(), LegalMessageLines(),
+      test::GetIncompleteCreditCard(), LegalMessageLines(),
       payments::PaymentsAutofillClient::SaveCreditCardOptions()
           .with_should_request_name_from_user(true)
           .with_num_strikes(0)
@@ -465,7 +484,7 @@
   base::HistogramTester histogram_tester;
 
   payments_client()->ShowSaveCreditCardToCloud(
-      CreditCard(), LegalMessageLines(),
+      test::GetExpiredCreditCard(), LegalMessageLines(),
       payments::PaymentsAutofillClient::SaveCreditCardOptions()
           .with_should_request_expiration_date_from_user(true)
           .with_num_strikes(0)
@@ -484,6 +503,8 @@
        DoNoShowSaveCardBottomSheet_ForRequestingCardHolderNameAndExpiryDate) {
   base::HistogramTester histogram_tester;
 
+  // Passing an empty CreditCard() to `ShowSaveCreditCardToCloud`
+  // since this test is regarding missing cardholder name and expiry date.
   payments_client()->ShowSaveCreditCardToCloud(
       CreditCard(), LegalMessageLines(),
       payments::PaymentsAutofillClient::SaveCreditCardOptions()
@@ -506,7 +527,7 @@
 TEST_F(IOSChromePaymentsAutofillClientWithSaveCardBottomSheetTest,
        CreditCardUploadCompleted_CardSaved) {
   payments_client()->ShowSaveCreditCardToCloud(
-      CreditCard(), LegalMessageLines(),
+      test::GetCreditCard(), LegalMessageLines(),
       payments::PaymentsAutofillClient::SaveCreditCardOptions()
           .with_num_strikes(0)
           .with_show_prompt(true),
@@ -524,7 +545,7 @@
 TEST_F(IOSChromePaymentsAutofillClientWithSaveCardBottomSheetTest,
        CreditCardUploadCompleted_CardNotSaved) {
   payments_client()->ShowSaveCreditCardToCloud(
-      CreditCard(), LegalMessageLines(),
+      test::GetCreditCard(), LegalMessageLines(),
       payments::PaymentsAutofillClient::SaveCreditCardOptions()
           .with_num_strikes(0)
           .with_show_prompt(true),
@@ -548,7 +569,7 @@
 TEST_F(IOSChromePaymentsAutofillClientWithSaveCardBottomSheetTest,
        CreditCardUploadCompleted_ClientSideTimeout_NoErrorConfirmation) {
   payments_client()->ShowSaveCreditCardToCloud(
-      CreditCard(), LegalMessageLines(),
+      test::GetCreditCard(), LegalMessageLines(),
       payments::PaymentsAutofillClient::SaveCreditCardOptions()
           .with_num_strikes(0)
           .with_show_prompt(true),
@@ -565,6 +586,106 @@
   EXPECT_FALSE(error_context.has_value());
 }
 
+class IOSChromePaymentsAutofillClientWithLocalSaveCardBottomSheetTest
+    : public IOSChromePaymentsAutofillClientTest {
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_{
+      autofill::features::kAutofillLocalSaveCardBottomSheet};
+};
+
+TEST_F(IOSChromePaymentsAutofillClientWithLocalSaveCardBottomSheetTest,
+       ShowSaveCardBottomSheet_FlagEnabled) {
+  payments_client()->ShowSaveCreditCardLocally(
+      test::GetCreditCard(),
+      payments::PaymentsAutofillClient::SaveCreditCardOptions()
+          .with_num_strikes(0)
+          .with_show_prompt(true),
+      base::DoNothing());
+  EXPECT_TRUE([autofill_commands() showSaveCardBottomSheetCalled]);
+}
+
+TEST_F(IOSChromePaymentsAutofillClientWithLocalSaveCardBottomSheetTest,
+       DoNoShowSaveCardBottomSheet_CardWithMoreThan0Strike) {
+  base::HistogramTester histogram_tester;
+
+  payments_client()->ShowSaveCreditCardLocally(
+      test::GetCreditCard(),
+      payments::PaymentsAutofillClient::SaveCreditCardOptions()
+          .with_num_strikes(1)
+          .with_show_prompt(true),
+      base::DoNothing());
+  EXPECT_FALSE([autofill_commands() showSaveCardBottomSheetCalled]);
+
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.SaveCreditCardPromptResult.IOS.Local.BottomSheet.NumStrikes.1."
+      "NoFixFlow",
+      autofill::autofill_metrics::SaveCreditCardPromptResultIOS::kNotShown,
+      /*expected_count=*/1);
+}
+
+TEST_F(IOSChromePaymentsAutofillClientWithLocalSaveCardBottomSheetTest,
+       ShowSaveCardBottomSheet_WithMissingCardHolderName) {
+  base::HistogramTester histogram_tester;
+
+  payments_client()->ShowSaveCreditCardLocally(
+      test::GetIncompleteCreditCard(),
+      payments::PaymentsAutofillClient::SaveCreditCardOptions()
+          .with_should_request_name_from_user(true)
+          .with_num_strikes(0)
+          .with_show_prompt(true),
+      base::DoNothing());
+  EXPECT_TRUE([autofill_commands() showSaveCardBottomSheetCalled]);
+
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.SaveCreditCardPromptResult.IOS.Local.BottomSheet.NumStrikes.0."
+      "RequestingCardHolderName",
+      autofill::autofill_metrics::SaveCreditCardPromptResultIOS::kNotShown,
+      /*expected_count=*/0);
+}
+
+TEST_F(IOSChromePaymentsAutofillClientWithLocalSaveCardBottomSheetTest,
+       ShowSaveCardBottomSheet_WithInvalidExpiryDate) {
+  base::HistogramTester histogram_tester;
+
+  payments_client()->ShowSaveCreditCardLocally(
+      test::GetExpiredCreditCard(),
+      payments::PaymentsAutofillClient::SaveCreditCardOptions()
+          .with_should_request_expiration_date_from_user(true)
+          .with_num_strikes(0)
+          .with_show_prompt(true),
+      base::DoNothing());
+  EXPECT_TRUE([autofill_commands() showSaveCardBottomSheetCalled]);
+
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.SaveCreditCardPromptResult.IOS.Local.BottomSheet.NumStrikes.0."
+      "RequestingExpiryDate",
+      autofill::autofill_metrics::SaveCreditCardPromptResultIOS::kNotShown,
+      /*expected_count=*/0);
+}
+
+TEST_F(IOSChromePaymentsAutofillClientWithLocalSaveCardBottomSheetTest,
+       ShowSaveCardBottomSheet_WithMissingCardHolderNameAndInvalidExpiryDate) {
+  base::HistogramTester histogram_tester;
+  CreditCard card = test::GetIncompleteCreditCard();
+  card.SetExpirationMonth(1);
+  card.SetExpirationYear(2020);
+  payments_client()->ShowSaveCreditCardLocally(
+      card,
+      payments::PaymentsAutofillClient::SaveCreditCardOptions()
+          .with_should_request_name_from_user(true)
+          .with_should_request_expiration_date_from_user(true)
+          .with_num_strikes(0)
+          .with_show_prompt(true),
+      base::DoNothing());
+  EXPECT_TRUE([autofill_commands() showSaveCardBottomSheetCalled]);
+
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.SaveCreditCardPromptResult.IOS.Local.BottomSheet.NumStrikes.0."
+      "RequestingCardHolderNameAndExpiryDate",
+      autofill::autofill_metrics::SaveCreditCardPromptResultIOS::kNotShown,
+      /*expected_count=*/0);
+}
+
 }  // namespace
 
 }  // namespace autofill
diff --git a/ios/chrome/browser/autofill/ui_bundled/manual_fill/address_view_controller_egtest.mm b/ios/chrome/browser/autofill/ui_bundled/manual_fill/address_view_controller_egtest.mm
index d608ec9..d07a964 100644
--- a/ios/chrome/browser/autofill/ui_bundled/manual_fill/address_view_controller_egtest.mm
+++ b/ios/chrome/browser/autofill/ui_bundled/manual_fill/address_view_controller_egtest.mm
@@ -607,6 +607,8 @@
       performAction:grey_tap()];
 
   // Tap the "Done" button to dismiss the view.
+  [ChromeEarlGrey
+      waitForUIElementToAppearWithMatcher:NavigationBarDoneButton()];
   [[EarlGrey selectElementWithMatcher:NavigationBarDoneButton()]
       performAction:grey_tap()];
 
diff --git a/ios/chrome/browser/docking_promo/coordinator/BUILD.gn b/ios/chrome/browser/docking_promo/coordinator/BUILD.gn
index e0320f5..3b489980 100644
--- a/ios/chrome/browser/docking_promo/coordinator/BUILD.gn
+++ b/ios/chrome/browser/docking_promo/coordinator/BUILD.gn
@@ -21,8 +21,6 @@
     "//ios/chrome/browser/docking_promo/ui",
     "//ios/chrome/browser/feature_engagement/model",
     "//ios/chrome/browser/first_run/ui_bundled:screen_delegate",
-    "//ios/chrome/browser/ntp/model:set_up_list_item_type",
-    "//ios/chrome/browser/ntp/model:set_up_list_prefs",
     "//ios/chrome/browser/promos_manager/model",
     "//ios/chrome/browser/promos_manager/model:constants",
     "//ios/chrome/browser/promos_manager/model:factory",
diff --git a/ios/chrome/browser/docking_promo/coordinator/DEPS b/ios/chrome/browser/docking_promo/coordinator/DEPS
deleted file mode 100644
index faa7702..0000000
--- a/ios/chrome/browser/docking_promo/coordinator/DEPS
+++ /dev/null
@@ -1,3 +0,0 @@
-include_rules = [
-  "+ios/chrome/browser/ntp/model",
-]
diff --git a/ios/chrome/browser/docking_promo/coordinator/docking_promo_coordinator.mm b/ios/chrome/browser/docking_promo/coordinator/docking_promo_coordinator.mm
index ba259a9..3e35cd06 100644
--- a/ios/chrome/browser/docking_promo/coordinator/docking_promo_coordinator.mm
+++ b/ios/chrome/browser/docking_promo/coordinator/docking_promo_coordinator.mm
@@ -9,11 +9,8 @@
 #import <optional>
 
 #import "base/feature_list.h"
-#import "base/ios/block_types.h"
-#import "base/memory/raw_ptr.h"
 #import "components/feature_engagement/public/event_constants.h"
 #import "components/feature_engagement/public/tracker.h"
-#import "components/prefs/pref_service.h"
 #import "ios/chrome/app/application_delegate/app_state.h"
 #import "ios/chrome/app/profile/profile_state.h"
 #import "ios/chrome/browser/docking_promo/coordinator/docking_promo_mediator.h"
@@ -22,15 +19,11 @@
 #import "ios/chrome/browser/docking_promo/ui/docking_promo_view_controller.h"
 #import "ios/chrome/browser/feature_engagement/model/tracker_factory.h"
 #import "ios/chrome/browser/first_run/ui_bundled/first_run_screen_delegate.h"
-#import "ios/chrome/browser/ntp/model/set_up_list_item_type.h"
-#import "ios/chrome/browser/ntp/model/set_up_list_prefs.h"
 #import "ios/chrome/browser/promos_manager/model/promos_manager.h"
 #import "ios/chrome/browser/promos_manager/model/promos_manager_factory.h"
 #import "ios/chrome/browser/promos_manager/ui_bundled/promos_manager_ui_handler.h"
 #import "ios/chrome/browser/shared/coordinator/scene/scene_state.h"
-#import "ios/chrome/browser/shared/model/application_context/application_context.h"
 #import "ios/chrome/browser/shared/model/browser/browser.h"
-#import "ios/chrome/browser/shared/model/prefs/pref_names.h"
 #import "ios/chrome/browser/shared/public/commands/command_dispatcher.h"
 #import "ios/chrome/browser/shared/public/features/features.h"
 #import "ios/chrome/browser/start_surface/ui_bundled/start_surface_util.h"
@@ -52,10 +45,6 @@
   BOOL _firstRun;
   /// First run screen delegate.
   __weak id<FirstRunScreenDelegate> _firstRunDelegate;
-  /// Trigger for the promo being displayed.
-  DockingPromoTrigger _promoTrigger;
-  // Whether or not the Set Up List Item should be marked complete.
-  BOOL _markItemComplete;
 }
 
 @synthesize baseNavigationController = _baseNavigationController;
@@ -103,9 +92,7 @@
                                   base::TimeDelta::Min())];
 
   if (_firstRun) {
-    _promoTrigger = DockingPromoTrigger::kFRE;
-    self.viewController =
-        [[DockingPromoViewController alloc] initWithRemindMeLater:YES];
+    self.viewController = [[DockingPromoViewController alloc] init];
     self.mediator.tracker =
         feature_engagement::TrackerFactory::GetForProfile(self.profile);
     self.viewController.actionHandler = self;
@@ -138,16 +125,13 @@
 
 #pragma mark - DockingPromoCommands
 
-- (void)showDockingPromoWithTrigger:(DockingPromoTrigger)trigger {
-  _promoTrigger = trigger;
-
-  if ((![self forceShow] && ![self.mediator canShowDockingPromo]) ||
+- (void)showDockingPromo:(BOOL)forced {
+  if ((!forced && ![self.mediator canShowDockingPromo]) ||
       [self.viewController isBeingPresented]) {
     return;
   }
 
-  self.viewController = [[DockingPromoViewController alloc]
-      initWithRemindMeLater:[self promoHasRemindMeLater]];
+  self.viewController = [[DockingPromoViewController alloc] init];
   self.mediator.tracker =
       feature_engagement::TrackerFactory::GetForProfile(self.profile);
   self.viewController.actionHandler = self;
@@ -161,7 +145,6 @@
 #pragma mark - ConfirmationAlertActionHandler
 
 - (void)confirmationAlertPrimaryAction {
-  _markItemComplete = YES;
   if (_firstRun) {
     [_firstRunDelegate screenWillFinishPresenting];
   } else {
@@ -172,24 +155,6 @@
 }
 
 - (void)confirmationAlertSecondaryAction {
-  [self promoHasRemindMeLater] ? [self remindMeLaterAction]
-                               : [self noThanksAction];
-}
-
-#pragma mark - UIAdaptivePresentationControllerDelegate
-
-- (void)presentationControllerDidDismiss:
-    (UIPresentationController*)presentationController {
-  [self promoWasDismissed];
-  RecordDockingPromoAction(IOSDockingPromoAction::kDismissViaSwipe);
-}
-
-#pragma mark - Private
-
-// Handles user interaction with the "Remind Me Later" function.
-- (void)remindMeLaterAction {
-  DCHECK([self promoHasRemindMeLater]);
-
   feature_engagement::Tracker* tracker =
       feature_engagement::TrackerFactory::GetForProfile(self.profile);
   tracker->NotifyEvent(feature_engagement::events::kDockingPromoRemindMeLater);
@@ -205,65 +170,21 @@
   RecordDockingPromoAction(IOSDockingPromoAction::kRemindMeLater);
 }
 
-// Handles user interaction with the "No Thanks" function.
-- (void)noThanksAction {
-  DCHECK(!_firstRun);
-  DCHECK(![self promoHasRemindMeLater]);
+#pragma mark - UIAdaptivePresentationControllerDelegate
 
-  _markItemComplete = YES;
-  [self hidePromo];
-  RecordDockingPromoAction(IOSDockingPromoAction::kDismissViaNoThanks);
+- (void)presentationControllerDidDismiss:
+    (UIPresentationController*)presentationController {
+  [self promoWasDismissed];
+  RecordDockingPromoAction(IOSDockingPromoAction::kDismissViaSwipe);
 }
 
-// Returns YES if the promo should have "Remind Me Later" functionality based
-// off of the latest promo trigger.
-- (BOOL)promoHasRemindMeLater {
-  DCHECK(_promoTrigger != DockingPromoTrigger::kTriggerUnset);
-  switch (_promoTrigger) {
-    case DockingPromoTrigger::kFRE:
-    case DockingPromoTrigger::kTipsModule:
-    case DockingPromoTrigger::kPromosManager:
-      return YES;
-    case DockingPromoTrigger::kSetUpList:
-      return NO;
-    case DockingPromoTrigger::kTriggerUnset:
-    default:
-      NOTREACHED();
-  }
-}
-
-// Returns YES if the promo should be forcibly shown based off of the latest
-// promo trigger.
-- (BOOL)forceShow {
-  DCHECK(_promoTrigger != DockingPromoTrigger::kTriggerUnset);
-  switch (_promoTrigger) {
-    case DockingPromoTrigger::kFRE:
-    case DockingPromoTrigger::kTipsModule:
-    case DockingPromoTrigger::kSetUpList:
-      return YES;
-    case DockingPromoTrigger::kPromosManager:
-      return NO;
-    case DockingPromoTrigger::kTriggerUnset:
-    default:
-      NOTREACHED();
-  }
-}
+#pragma mark - Private
 
 // Dismisses the feature.
 - (void)hidePromo {
-  ProceduralBlock completion = nil;
-  if (_markItemComplete) {
-    __weak __typeof(self) weakSelf = self;
-    completion = ^{
-      DockingPromoCoordinator* strongSelf = weakSelf;
-      if (strongSelf) {
-        strongSelf->_markItemComplete = NO;
-      }
-    };
-  }
   [self.viewController.presentingViewController
       dismissViewControllerAnimated:YES
-                         completion:completion];
+                         completion:nil];
 }
 
 // Does any clean up for when the promo is fully dismissed.
diff --git a/ios/chrome/browser/docking_promo/ui/docking_promo_display_handler.mm b/ios/chrome/browser/docking_promo/ui/docking_promo_display_handler.mm
index be8a6120..5b199f03 100644
--- a/ios/chrome/browser/docking_promo/ui/docking_promo_display_handler.mm
+++ b/ios/chrome/browser/docking_promo/ui/docking_promo_display_handler.mm
@@ -30,7 +30,7 @@
 #pragma mark - StandardPromoDisplayHandler
 
 - (void)handleDisplay {
-  [_handler showDockingPromoWithTrigger:DockingPromoTrigger::kPromosManager];
+  [_handler showDockingPromo:NO];
 }
 
 #pragma mark - PromoProtocol
diff --git a/ios/chrome/browser/docking_promo/ui/docking_promo_view_controller.h b/ios/chrome/browser/docking_promo/ui/docking_promo_view_controller.h
index 38e4b51a..a6fbe52 100644
--- a/ios/chrome/browser/docking_promo/ui/docking_promo_view_controller.h
+++ b/ios/chrome/browser/docking_promo/ui/docking_promo_view_controller.h
@@ -10,17 +10,6 @@
 // Container view controller for the Docking Promo.
 @interface DockingPromoViewController : AnimatedPromoViewController
 
-// Creates a new instance of the view controller. If `remindMeLater` is YES, the
-// view controller will include a "Remind Me Later" button. Otherwise, the view
-// controller will contain a "No Thanks" button.
-- (instancetype)initWithRemindMeLater:(BOOL)remindMeLater
-    NS_DESIGNATED_INITIALIZER;
-
-- (instancetype)initWithNibName:(NSString*)nibNameOrNil
-                         bundle:(NSBundle*)nibBundleOrNil NS_UNAVAILABLE;
-- (instancetype)initWithCoder:(NSCoder*)aDecoder NS_UNAVAILABLE;
-- (instancetype)init NS_UNAVAILABLE;
-
 @end
 
 #endif  // IOS_CHROME_BROWSER_DOCKING_PROMO_UI_DOCKING_PROMO_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/docking_promo/ui/docking_promo_view_controller.mm b/ios/chrome/browser/docking_promo/ui/docking_promo_view_controller.mm
index 450f685..1d38535 100644
--- a/ios/chrome/browser/docking_promo/ui/docking_promo_view_controller.mm
+++ b/ios/chrome/browser/docking_promo/ui/docking_promo_view_controller.mm
@@ -27,17 +27,7 @@
 
 }  // namespace
 
-@implementation DockingPromoViewController {
-  BOOL _remindMeLater;
-}
-
-- (instancetype)initWithRemindMeLater:(BOOL)remindMeLater {
-  self = [super initWithNibName:nil bundle:nil];
-  if (self) {
-    _remindMeLater = remindMeLater;
-  }
-  return self;
-}
+@implementation DockingPromoViewController
 
 #pragma mark - UIViewController
 
@@ -54,10 +44,7 @@
   self.primaryActionString =
       l10n_util::GetNSString(IDS_IOS_DOCKING_PROMO_PRIMARY_BUTTON_TITLE);
   self.secondaryActionString =
-      _remindMeLater
-          ? l10n_util::GetNSString(IDS_IOS_DOCKING_PROMO_SECONDARY_BUTTON_TITLE)
-          : l10n_util::GetNSString(
-                IDS_IOS_DOCKING_PROMO_NO_THANKS_BUTTON_TITLE);
+      l10n_util::GetNSString(IDS_IOS_DOCKING_PROMO_SECONDARY_BUTTON_TITLE);
 
   // Set the text localization.
   NSString* editHomeScreenTitle = l10n_util::GetNSString(
diff --git a/ios/chrome/browser/first_run/ui_bundled/guided_tour/guided_tour_coordinator.mm b/ios/chrome/browser/first_run/ui_bundled/guided_tour/guided_tour_coordinator.mm
index bad5080..d634a40 100644
--- a/ios/chrome/browser/first_run/ui_bundled/guided_tour/guided_tour_coordinator.mm
+++ b/ios/chrome/browser/first_run/ui_bundled/guided_tour/guided_tour_coordinator.mm
@@ -15,8 +15,9 @@
 #import "ui/base/l10n/l10n_util_mac.h"
 
 namespace {
-// Corner radius of the Tab Grid button spotlight cutout for the NTP step.
+// Corner radius of the spotlight cutouts.
 const CGFloat kNTPTabGridButtonSpotlightCornerRadius = 7.0f;
+const CGFloat kNTPTabGridPageControlCornerRadius = 13.0f;
 }  // namespace
 
 @implementation GuidedTourCoordinator {
@@ -41,14 +42,14 @@
 
 - (void)start {
   __weak GuidedTourCoordinator* weakSelf = self;
-  BubbleArrowDirection direction = IsSplitToolbarMode(self.baseViewController)
+  BubbleArrowDirection direction = [self shouldPointArrowDown]
                                        ? BubbleArrowDirectionDown
                                        : BubbleArrowDirectionUp;
   _presenter = [[GuidedTourBubbleViewControllerPresenter alloc]
       initWithText:[self bodyString]
       title:[self titleString]
       arrowDirection:direction
-      alignment:BubbleAlignmentBottomOrTrailing
+      alignment:[self bubbleAlignment]
       bubbleType:BubbleViewTypeRichWithNext
       backgroundCutoutCornerRadius:[self backgroundCutoutCornerRadius]
       dismissalCallback:^(IPHDismissalReasonType reason) {
@@ -60,15 +61,10 @@
 
   UIView* anchorView = [self anchorView];
   CGPoint anchorPoint = [self anchorPointForAnchorView:anchorView];
-  CGPoint anchorViewOrigin =
-      [anchorView.superview convertPoint:anchorView.frame.origin toView:nil];
-  CGRect anchorViewFrame =
-      CGRectMake(anchorViewOrigin.x, anchorViewOrigin.y,
-                 anchorView.frame.size.width, anchorView.frame.size.height);
 
   [_presenter presentInViewController:self.baseViewController
                           anchorPoint:anchorPoint
-                      anchorViewFrame:anchorViewFrame];
+                      anchorViewFrame:[self cutoutView]];
 }
 
 - (void)stop {
@@ -85,6 +81,9 @@
         static_cast<ToolbarButton*>([LayoutGuideCenterForBrowser(self.browser)
             referencedViewUnderName:kTabSwitcherGuide]);
     return tabSwitcherButton.spotlightView;
+  } else if (_step == GuidedTourStepTabGridIncognito) {
+    return [LayoutGuideCenterForBrowser(nil)
+        referencedViewUnderName:kTabGridPageControlIncognitoGuide];
   }
   NOTREACHED() << "A layout guide view needs to be fetched for each step";
 }
@@ -93,7 +92,7 @@
 // anchored.
 - (CGPoint)anchorPointForAnchorView:(UIView*)anchorView {
   CGPoint anchorPoint;
-  if (IsSplitToolbarMode(self.baseViewController)) {
+  if ([self shouldPointArrowDown]) {
     anchorPoint = CGPointMake(CGRectGetMidX(anchorView.frame),
                               CGRectGetMinY(anchorView.frame));
   } else {
@@ -117,6 +116,9 @@
 - (NSString*)titleString {
   if (_step == GuidedTourStepNTP) {
     return l10n_util::GetNSString(IDS_IOS_FIRST_RUN_GUIDED_TOUR_NTP_IPH_TITLE);
+  } else if (_step == GuidedTourStepTabGridIncognito) {
+    return l10n_util::GetNSString(
+        IDS_IOS_FIRST_RUN_GUIDED_TOUR_TAB_GRID_INCOGNITO_IPH_TITLE);
   }
   return @"";
 }
@@ -125,6 +127,9 @@
 - (NSString*)bodyString {
   if (_step == GuidedTourStepNTP) {
     return l10n_util::GetNSString(IDS_IOS_FIRST_RUN_GUIDED_TOUR_NTP_IPH_TEXT);
+  } else if (_step == GuidedTourStepTabGridIncognito) {
+    return l10n_util::GetNSString(
+        IDS_IOS_FIRST_RUN_GUIDED_TOUR_TAB_GRID_INCOGNITO_IPH_TEXT);
   }
   return @"";
 }
@@ -132,7 +137,42 @@
 // The corner radius of the spotlight cutout for this Bubble View.
 - (CGFloat)backgroundCutoutCornerRadius {
   return _step == GuidedTourStepNTP ? kNTPTabGridButtonSpotlightCornerRadius
-                                    : 0;
+                                    : kNTPTabGridPageControlCornerRadius;
+}
+
+// YES if the bubble arrow should point down (e.g. the NTP step is pointing down
+// to the bottom toolbar).
+- (BOOL)shouldPointArrowDown {
+  return IsSplitToolbarMode(self.baseViewController) &&
+         _step == GuidedTourStepNTP;
+}
+
+// Returns the bubble alignment for each step.
+- (BubbleAlignment)bubbleAlignment {
+  if (_step == GuidedTourStepNTP) {
+    return BubbleAlignmentBottomOrTrailing;
+  } else if (_step == GuidedTourStepTabGridIncognito) {
+    return BubbleAlignmentTopOrLeading;
+  }
+  NOTREACHED()
+      << "Need to define the bubble alignment for each guided tour step";
+}
+
+// Returns the frame that needs to be cut out of the blur background.
+- (CGRect)cutoutView {
+  UIView* cutoutView;
+  if (_step == GuidedTourStepNTP) {
+    cutoutView = [self anchorView];
+  } else {
+    // The TabGrid Page Control steps should cut out the entire page control,
+    // not just the anchor view.
+    cutoutView = [LayoutGuideCenterForBrowser(nil)
+        referencedViewUnderName:kTabGridPageControlGuide];
+  }
+  CGPoint cutoutViewOrigin =
+      [cutoutView.superview convertPoint:cutoutView.frame.origin toView:nil];
+  return CGRectMake(cutoutViewOrigin.x, cutoutViewOrigin.y,
+                    cutoutView.frame.size.width, cutoutView.frame.size.height);
 }
 
 @end
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm
index 1420c31..56b22ee 100644
--- a/ios/chrome/browser/flags/about_flags.mm
+++ b/ios/chrome/browser/flags/about_flags.mm
@@ -1551,6 +1551,10 @@
      FEATURE_WITH_PARAMS_VALUE_TYPE(omnibox::kMaxZeroSuggestMatches,
                                     kOmniboxMaxZPSMatchesVariations,
                                     "OmniboxMaxZPSVariations")},
+    {"omnibox-mobile-parity-update",
+     flag_descriptions::kOmniboxMobileParityUpdateName,
+     flag_descriptions::kOmniboxMobileParityUpdateDescription, flags_ui::kOsIos,
+     FEATURE_VALUE_TYPE(omnibox::kOmniboxMobileParityUpdate)},
     {"force-startup-signin-promo",
      flag_descriptions::kForceStartupSigninPromoName,
      flag_descriptions::kForceStartupSigninPromoDescription, flags_ui::kOsIos,
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
index 4b7cbc1..d647a51 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
@@ -1008,6 +1008,11 @@
     "Specifies how to blend URL ML scores and search traditional scores using "
     "a piecewise ML score mapping function.";
 
+const char kOmniboxMobileParityUpdateName[] = "Omnibox Mobile parity update";
+const char kOmniboxMobileParityUpdateDescription[] =
+    "When set, applies certain assets to match Desktop visuals and "
+    "descriptions";
+
 const char kOmniboxMlUrlScoreCachingName[] = "Omnibox ML URL Score Caching";
 const char kOmniboxMlUrlScoreCachingDescription[] =
     "Enables in-memory caching of ML URL scores.";
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
index 62e2fd7..6ec3fec 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
@@ -604,6 +604,9 @@
 extern const char kOmniboxMlUrlSearchBlendingName[];
 extern const char kOmniboxMlUrlSearchBlendingDescription[];
 
+extern const char kOmniboxMobileParityUpdateName[];
+extern const char kOmniboxMobileParityUpdateDescription[];
+
 extern const char kOmniboxOnClobberFocusTypeOnIOSName[];
 extern const char kOmniboxOnClobberFocusTypeOnIOSDescription[];
 
diff --git a/ios/chrome/browser/lens_overlay/ui/lens_overlay_container_view_controller.mm b/ios/chrome/browser/lens_overlay/ui/lens_overlay_container_view_controller.mm
index 447bdb7..a4bf8eb 100644
--- a/ios/chrome/browser/lens_overlay/ui/lens_overlay_container_view_controller.mm
+++ b/ios/chrome/browser/lens_overlay/ui/lens_overlay_container_view_controller.mm
@@ -27,7 +27,7 @@
 const CGFloat kSidePannelOutlineBorderWidth = 1.0;
 
 // The corner radius of the outline that surrounds the results page.
-const CGFloat kSidePannelOutlineCornerRadius = 8.0;
+const CGFloat kSidePannelOutlineCornerRadius = 13.0;
 
 // The lateral inset ammount of the border outlining the results page.
 const CGFloat kSidePannelOutlineLateralInset = 8.0;
@@ -37,7 +37,7 @@
 
 // The corner radius of the selection UI when presented in the side panel
 // presentation.
-const CGFloat kSelectionUICornerRadius = 14.0;
+const CGFloat kSelectionUICornerRadius = 13.0;
 
 }  // namespace
 
diff --git a/ios/chrome/browser/omnibox/coordinator/BUILD.gn b/ios/chrome/browser/omnibox/coordinator/BUILD.gn
index ad5a038..4d1d3b70 100644
--- a/ios/chrome/browser/omnibox/coordinator/BUILD.gn
+++ b/ios/chrome/browser/omnibox/coordinator/BUILD.gn
@@ -9,6 +9,7 @@
     "omnibox_coordinator.mm",
     "omnibox_mediator.h",
     "omnibox_mediator.mm",
+    "omnibox_mediator_delegate.h",
     "zero_suggest_prefetch_helper.h",
     "zero_suggest_prefetch_helper.mm",
   ]
diff --git a/ios/chrome/browser/omnibox/coordinator/omnibox_coordinator.mm b/ios/chrome/browser/omnibox/coordinator/omnibox_coordinator.mm
index 60d83d43..9284206 100644
--- a/ios/chrome/browser/omnibox/coordinator/omnibox_coordinator.mm
+++ b/ios/chrome/browser/omnibox/coordinator/omnibox_coordinator.mm
@@ -20,6 +20,7 @@
 #import "ios/chrome/browser/feature_engagement/model/tracker_factory.h"
 #import "ios/chrome/browser/location_bar/ui_bundled/location_bar_constants.h"
 #import "ios/chrome/browser/omnibox/coordinator/omnibox_mediator.h"
+#import "ios/chrome/browser/omnibox/coordinator/omnibox_mediator_delegate.h"
 #import "ios/chrome/browser/omnibox/coordinator/popup/omnibox_popup_coordinator.h"
 #import "ios/chrome/browser/omnibox/coordinator/zero_suggest_prefetch_helper.h"
 #import "ios/chrome/browser/omnibox/model/autocomplete_result_wrapper.h"
@@ -60,7 +61,8 @@
 #import "ios/chrome/browser/url_loading/model/url_loading_params.h"
 #import "ios/web/public/navigation/navigation_manager.h"
 
-@interface OmniboxCoordinator () <OmniboxAssistiveKeyboardMediatorDelegate>
+@interface OmniboxCoordinator () <OmniboxAssistiveKeyboardMediatorDelegate,
+                                  OmniboxMediatorDelegate>
 
 // View controller managed by this coordinator.
 @property(nonatomic, strong) OmniboxViewController* viewController;
@@ -128,71 +130,76 @@
 - (void)start {
   DCHECK(!self.popupCoordinator);
 
+  ProfileIOS* profile = self.profile;
+  Browser* browser = self.browser;
+
   _toolbarHandler =
-      HandlerForProtocol(self.browser->GetCommandDispatcher(), ToolbarCommands);
+      HandlerForProtocol(browser->GetCommandDispatcher(), ToolbarCommands);
 
-  self.viewController =
+  OmniboxViewController* viewController =
       [[OmniboxViewController alloc] initWithIsLensOverlay:_isLensOverlay];
-  self.viewController.defaultLeadingImage =
+  self.viewController = viewController;
+  viewController.defaultLeadingImage =
       GetOmniboxSuggestionIcon(OmniboxSuggestionIconType::kDefaultFavicon);
-  self.viewController.layoutGuideCenter =
-      LayoutGuideCenterForBrowser(self.browser);
-  self.viewController.searchOnlyUI = self.searchOnlyUI;
+  viewController.layoutGuideCenter = LayoutGuideCenterForBrowser(browser);
+  viewController.searchOnlyUI = self.searchOnlyUI;
 
-  BOOL incognito = self.profile->IsOffTheRecord();
-  self.mediator = [[OmniboxMediator alloc]
+  OmniboxTextFieldIOS* textField = viewController.textField;
+
+  BOOL incognito = profile->IsOffTheRecord();
+  OmniboxMediator* mediator = [[OmniboxMediator alloc]
       initWithIncognito:incognito
                 tracker:feature_engagement::TrackerFactory::GetForProfile(
-                            self.profile)
+                            profile)
           isLensOverlay:_isLensOverlay];
+  self.mediator = mediator;
 
+  mediator.delegate = self;
   TemplateURLService* templateURLService =
-      ios::TemplateURLServiceFactory::GetForProfile(self.profile);
-  self.mediator.templateURLService = templateURLService;
-  self.mediator.faviconLoader =
-      IOSChromeFaviconLoaderFactory::GetForProfile(self.profile);
-  self.mediator.consumer = self.viewController;
-  self.mediator.omniboxCommandsHandler =
-      HandlerForProtocol(self.browser->GetCommandDispatcher(), OmniboxCommands);
-  self.mediator.lensCommandsHandler =
-      HandlerForProtocol(self.browser->GetCommandDispatcher(), LensCommands);
-  self.mediator.loadQueryCommandsHandler = HandlerForProtocol(
-      self.browser->GetCommandDispatcher(), LoadQueryCommands);
-  self.mediator.sceneState = self.browser->GetSceneState();
-  self.mediator.URLLoadingBrowserAgent =
-      UrlLoadingBrowserAgent::FromBrowser(self.browser);
-  self.viewController.pasteDelegate = self.mediator;
-  self.viewController.mutator = self.mediator;
+      ios::TemplateURLServiceFactory::GetForProfile(profile);
+  mediator.templateURLService = templateURLService;
+  mediator.faviconLoader =
+      IOSChromeFaviconLoaderFactory::GetForProfile(profile);
+  mediator.consumer = viewController;
+  mediator.omniboxCommandsHandler =
+      HandlerForProtocol(browser->GetCommandDispatcher(), OmniboxCommands);
+  mediator.lensCommandsHandler =
+      HandlerForProtocol(browser->GetCommandDispatcher(), LensCommands);
+  mediator.loadQueryCommandsHandler =
+      HandlerForProtocol(browser->GetCommandDispatcher(), LoadQueryCommands);
+  mediator.sceneState = browser->GetSceneState();
+  mediator.URLLoadingBrowserAgent =
+      UrlLoadingBrowserAgent::FromBrowser(browser);
+  viewController.pasteDelegate = mediator;
+  viewController.mutator = mediator;
 
   DCHECK(_client.get());
 
   id<OmniboxCommands> omniboxHandler =
-      HandlerForProtocol(self.browser->GetCommandDispatcher(), OmniboxCommands);
-  _editView = std::make_unique<OmniboxViewIOS>(self.textField,
-                                               std::move(_client), self.profile,
-                                               omniboxHandler, _toolbarHandler);
+      HandlerForProtocol(browser->GetCommandDispatcher(), OmniboxCommands);
+  _editView = std::make_unique<OmniboxViewIOS>(
+      textField, std::move(_client), profile, omniboxHandler, _toolbarHandler);
   self.pasteDelegate = [[OmniboxTextFieldPasteDelegate alloc] init];
-  [self.textField setPasteDelegate:self.pasteDelegate];
+  [textField setPasteDelegate:self.pasteDelegate];
 
   _keyboardMediator = [[OmniboxAssistiveKeyboardMediator alloc] init];
-  _keyboardMediator.applicationCommandsHandler = HandlerForProtocol(
-      self.browser->GetCommandDispatcher(), ApplicationCommands);
+  _keyboardMediator.applicationCommandsHandler =
+      HandlerForProtocol(browser->GetCommandDispatcher(), ApplicationCommands);
   _keyboardMediator.lensCommandsHandler =
-      HandlerForProtocol(self.browser->GetCommandDispatcher(), LensCommands);
-  _keyboardMediator.qrScannerCommandsHandler = HandlerForProtocol(
-      self.browser->GetCommandDispatcher(), QRScannerCommands);
-  _keyboardMediator.layoutGuideCenter =
-      LayoutGuideCenterForBrowser(self.browser);
+      HandlerForProtocol(browser->GetCommandDispatcher(), LensCommands);
+  _keyboardMediator.qrScannerCommandsHandler =
+      HandlerForProtocol(browser->GetCommandDispatcher(), QRScannerCommands);
+  _keyboardMediator.layoutGuideCenter = LayoutGuideCenterForBrowser(browser);
   // TODO(crbug.com/40670043): Use HandlerForProtocol after commands protocol
   // clean up.
   _keyboardMediator.browserCoordinatorCommandsHandler =
       static_cast<id<BrowserCoordinatorCommands>>(
-          self.browser->GetCommandDispatcher());
-  _keyboardMediator.omniboxTextField = self.textField;
+          browser->GetCommandDispatcher());
+  _keyboardMediator.omniboxTextField = textField;
   _keyboardMediator.delegate = self;
 
   self.zeroSuggestPrefetchHelper = [[ZeroSuggestPrefetchHelper alloc]
-      initWithWebStateList:self.browser->GetWebStateList()
+      initWithWebStateList:browser->GetWebStateList()
                 controller:_editView->controller()];
 
   _omniboxAutocompleteController = [[OmniboxAutocompleteController alloc]
@@ -202,17 +209,17 @@
       initWithOmniboxController:_editView->controller()
                  omniboxViewIOS:_editView.get()
                   inLensOverlay:_isLensOverlay];
-  _omniboxTextController.delegate = self.mediator;
+  _omniboxTextController.delegate = mediator;
   _omniboxTextController.focusDelegate = self.focusDelegate;
   _omniboxTextController.omniboxAutocompleteController =
       _omniboxAutocompleteController;
-  _omniboxTextController.textField = self.textField;
+  _omniboxTextController.textField = textField;
   _omniboxAutocompleteController.omniboxTextController = _omniboxTextController;
 
-  self.mediator.omniboxTextController = _omniboxTextController;
+  mediator.omniboxTextController = _omniboxTextController;
   _editView->SetOmniboxTextController(_omniboxTextController);
 
-  CommandDispatcher* dispatcher = self.browser->GetCommandDispatcher();
+  CommandDispatcher* dispatcher = browser->GetCommandDispatcher();
   OmniboxPedalAnnotator* annotator = [[OmniboxPedalAnnotator alloc] init];
   annotator.applicationHandler =
       HandlerForProtocol(dispatcher, ApplicationCommands);
@@ -272,33 +279,7 @@
 }
 
 - (void)focusOmnibox {
-  if (!self.keyboardAccessoryView &&
-      (!self.searchOnlyUI || experimental_flags::IsOmniboxDebuggingEnabled())) {
-    TemplateURLService* templateURLService =
-        ios::TemplateURLServiceFactory::GetForProfile(self.profile);
-    self.keyboardAccessoryView = ConfigureAssistiveKeyboardViews(
-        self.textField, kDotComTLD, _keyboardMediator, templateURLService,
-        HandlerForProtocol(self.browser->GetCommandDispatcher(), HelpCommands));
-  }
-
-  if (![self.textField isFirstResponder]) {
-    base::RecordAction(base::UserMetricsAction("MobileOmniboxFocused"));
-
-    // In multiwindow context, -becomeFirstRepsonder is not enough to get the
-    // keyboard input. The window will not automatically become key. Make it key
-    // manually. UITextField does this under the hood when tapped from
-    // -[UITextInteractionAssistant(UITextInteractionAssistant_Internal)
-    // setFirstResponderIfNecessaryActivatingSelection:]
-    if (base::ios::IsMultipleScenesSupported()) {
-      [self.textField.window makeKeyAndVisible];
-    }
-
-    [self.textField becomeFirstResponder];
-    // Ensures that the accessibility system focuses the text field instead of
-    // the popup crbug.com/1469173.
-    UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification,
-                                    self.textField);
-  }
+  [_omniboxTextController focusOmnibox];
 }
 
 - (void)endEditing {
@@ -358,7 +339,7 @@
 #pragma mark Scribble
 
 - (void)focusOmniboxForScribble {
-  [self.textField becomeFirstResponder];
+  [_omniboxTextController focusOmnibox];
   [self.viewController prepareOmniboxForScribble];
 }
 
@@ -372,11 +353,18 @@
   [self.popupCoordinator toggleOmniboxDebuggerView];
 }
 
-#pragma mark - private
+#pragma mark - OmniboxMediatorDelegate
 
-// Convenience accessor.
-- (OmniboxTextFieldIOS*)textField {
-  return self.viewController.textField;
+- (void)omniboxMediatorDidBeginEditing:(OmniboxMediator*)mediator {
+  if (!self.keyboardAccessoryView &&
+      (!self.searchOnlyUI || experimental_flags::IsOmniboxDebuggingEnabled())) {
+    TemplateURLService* templateURLService =
+        ios::TemplateURLServiceFactory::GetForProfile(self.profile);
+    self.keyboardAccessoryView = ConfigureAssistiveKeyboardViews(
+        self.viewController.textField, kDotComTLD, _keyboardMediator,
+        templateURLService,
+        HandlerForProtocol(self.browser->GetCommandDispatcher(), HelpCommands));
+  }
 }
 
 #pragma mark - Testing
diff --git a/ios/chrome/browser/omnibox/coordinator/omnibox_mediator.h b/ios/chrome/browser/omnibox/coordinator/omnibox_mediator.h
index c5206d2f..257309b 100644
--- a/ios/chrome/browser/omnibox/coordinator/omnibox_mediator.h
+++ b/ios/chrome/browser/omnibox/coordinator/omnibox_mediator.h
@@ -16,6 +16,7 @@
 @protocol LoadQueryCommands;
 @protocol OmniboxCommands;
 @protocol OmniboxConsumer;
+@protocol OmniboxMediatorDelegate;
 @class OmniboxTextController;
 @class SceneState;
 class TemplateURLService;
@@ -30,6 +31,9 @@
                                        OmniboxTextControllerDelegate,
                                        OmniboxViewControllerPasteDelegate>
 
+/// Delegate for events in this class.
+@property(nonatomic, weak) id<OmniboxMediatorDelegate> delegate;
+
 /// Controller of the omnibox text.
 @property(nonatomic, weak) OmniboxTextController* omniboxTextController;
 
diff --git a/ios/chrome/browser/omnibox/coordinator/omnibox_mediator.mm b/ios/chrome/browser/omnibox/coordinator/omnibox_mediator.mm
index 40c866e..e38b47b 100644
--- a/ios/chrome/browser/omnibox/coordinator/omnibox_mediator.mm
+++ b/ios/chrome/browser/omnibox/coordinator/omnibox_mediator.mm
@@ -14,6 +14,7 @@
 #import "ios/chrome/browser/favicon/model/favicon_loader.h"
 #import "ios/chrome/browser/lens/ui_bundled/lens_entrypoint.h"
 #import "ios/chrome/browser/net/model/crurl.h"
+#import "ios/chrome/browser/omnibox/coordinator/omnibox_mediator_delegate.h"
 #import "ios/chrome/browser/omnibox/model/autocomplete_suggestion.h"
 #import "ios/chrome/browser/omnibox/model/omnibox_text_controller.h"
 #import "ios/chrome/browser/omnibox/public/omnibox_constants.h"
@@ -186,6 +187,7 @@
 
 - (void)onDidBeginEditing {
   [self.omniboxTextController onDidBeginEditing];
+  [self.delegate omniboxMediatorDidBeginEditing:self];
 }
 
 - (BOOL)shouldChangeCharactersInRange:(NSRange)range
diff --git a/ios/chrome/browser/omnibox/coordinator/omnibox_mediator_delegate.h b/ios/chrome/browser/omnibox/coordinator/omnibox_mediator_delegate.h
new file mode 100644
index 0000000..b43e52a
--- /dev/null
+++ b/ios/chrome/browser/omnibox/coordinator/omnibox_mediator_delegate.h
@@ -0,0 +1,20 @@
+// 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_OMNIBOX_COORDINATOR_OMNIBOX_MEDIATOR_DELEGATE_H_
+#define IOS_CHROME_BROWSER_OMNIBOX_COORDINATOR_OMNIBOX_MEDIATOR_DELEGATE_H_
+
+#import <UIKit/UIKit.h>
+
+@class OmniboxMediator;
+
+/// Delegate for the omnibox mediator.
+@protocol OmniboxMediatorDelegate <NSObject>
+
+/// Omnibox textfield did begin editing.
+- (void)omniboxMediatorDidBeginEditing:(OmniboxMediator*)mediator;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_OMNIBOX_COORDINATOR_OMNIBOX_MEDIATOR_DELEGATE_H_
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 a061760..b8c505cf 100644
--- a/ios/chrome/browser/omnibox/model/omnibox_edit_model_ios.mm
+++ b/ios/chrome/browser/omnibox/model/omnibox_edit_model_ios.mm
@@ -47,6 +47,7 @@
 #import "components/omnibox/browser/omnibox_event_global_tracker.h"
 #import "components/omnibox/browser/omnibox_field_trial.h"
 #import "components/omnibox/browser/omnibox_log.h"
+#import "components/omnibox/browser/omnibox_logging_utils.h"
 #import "components/omnibox/browser/omnibox_metrics_provider.h"
 #import "components/omnibox/browser/omnibox_navigation_observer.h"
 #import "components/omnibox/browser/omnibox_popup_selection.h"
@@ -138,95 +139,6 @@
   return parts;
 }
 
-// This function provides a logging implementation that aligns with the original
-// definition of the `DEPRECATED_UMA_HISTOGRAM_MEDIUM_TIMES()` macro, which is
-// currently being used to log the `FocusToOpenTimeAnyPopupState3` Omnibox
-// metric.
-void LogHistogramMediumTimes(const std::string& histogram_name,
-                             base::TimeDelta elapsed) {
-  base::UmaHistogramCustomTimes(histogram_name, elapsed, base::Milliseconds(10),
-                                base::Minutes(3), 50);
-}
-
-void LogFocusToOpenTime(base::TimeDelta elapsed,
-                        bool is_zero_prefix,
-                        PageClassification page_classification,
-                        AutocompleteMatch& match,
-                        size_t action_index) {
-  LogHistogramMediumTimes("Omnibox.FocusToOpenTimeAnyPopupState3", elapsed);
-
-  std::string summarized_result_type;
-  switch (OmniboxMetricsProvider::GetClientSummarizedResultType(
-      match.GetOmniboxEventResultType(action_index))) {
-    case ClientSummarizedResultType::kSearch:
-      summarized_result_type = "SEARCH";
-      break;
-    case ClientSummarizedResultType::kUrl:
-      summarized_result_type = "URL";
-      break;
-    default:
-      summarized_result_type = "OTHER";
-      break;
-  }
-
-  LogHistogramMediumTimes(
-      base::StrCat(
-          {"Omnibox.FocusToOpenTimeAnyPopupState3.BySummarizedResultType.",
-           summarized_result_type}),
-      elapsed);
-
-  const std::string page_context =
-      OmniboxEventProto::PageClassification_Name(page_classification);
-  LogHistogramMediumTimes(
-      base::StrCat({"Omnibox.FocusToOpenTimeAnyPopupState3.ByPageContext.",
-                    page_context}),
-      elapsed);
-
-  LogHistogramMediumTimes(
-      base::StrCat(
-          {"Omnibox.FocusToOpenTimeAnyPopupState3.BySummarizedResultType.",
-           summarized_result_type, ".ByPageContext.", page_context}),
-      elapsed);
-
-  if (is_zero_prefix) {
-    LogHistogramMediumTimes("Omnibox.FocusToOpenTimeAnyPopupState3.ZeroSuggest",
-                            elapsed);
-    LogHistogramMediumTimes(
-        base::StrCat({"Omnibox.FocusToOpenTimeAnyPopupState3.ZeroSuggest."
-                      "BySummarizedResultType.",
-                      summarized_result_type}),
-        elapsed);
-    LogHistogramMediumTimes(
-        base::StrCat(
-            {"Omnibox.FocusToOpenTimeAnyPopupState3.ZeroSuggest.ByPageContext.",
-             page_context}),
-        elapsed);
-    LogHistogramMediumTimes(
-        base::StrCat({"Omnibox.FocusToOpenTimeAnyPopupState3.ZeroSuggest."
-                      "BySummarizedResultType.",
-                      summarized_result_type, ".ByPageContext.", page_context}),
-        elapsed);
-  } else {
-    LogHistogramMediumTimes(
-        "Omnibox.FocusToOpenTimeAnyPopupState3.TypedSuggest", elapsed);
-    LogHistogramMediumTimes(
-        base::StrCat({"Omnibox.FocusToOpenTimeAnyPopupState3.TypedSuggest."
-                      "BySummarizedResultType.",
-                      summarized_result_type}),
-        elapsed);
-    LogHistogramMediumTimes(
-        base::StrCat({"Omnibox.FocusToOpenTimeAnyPopupState3.TypedSuggest."
-                      "ByPageContext.",
-                      page_context}),
-        elapsed);
-    LogHistogramMediumTimes(
-        base::StrCat({"Omnibox.FocusToOpenTimeAnyPopupState3.TypedSuggest."
-                      "BySummarizedResultType.",
-                      summarized_result_type, ".ByPageContext.", page_context}),
-        elapsed);
-  }
-}
-
 }  // namespace
 
 // OmniboxEditModelIOS
@@ -900,9 +812,11 @@
     elapsed_time_since_user_focused_omnibox = now - last_omnibox_focus_;
     // Only record focus to open time when a focus actually happened (as
     // opposed to, say, dragging a link onto the omnibox).
-    LogFocusToOpenTime(elapsed_time_since_user_focused_omnibox,
-                       input_.IsZeroSuggest(), GetPageClassification(), match,
-                       selection.IsAction() ? selection.action_index : -1);
+
+    omnibox::LogFocusToOpenTime(
+        elapsed_time_since_user_focused_omnibox, input_.IsZeroSuggest(),
+        GetPageClassification(), match,
+        selection.IsAction() ? selection.action_index : -1);
   }
 
   // In some unusual cases, we ignore autocomplete_controller()->result() and
diff --git a/ios/chrome/browser/omnibox/model/omnibox_text_controller.h b/ios/chrome/browser/omnibox/model/omnibox_text_controller.h
index 156ab2c..a98fc6b 100644
--- a/ios/chrome/browser/omnibox/model/omnibox_text_controller.h
+++ b/ios/chrome/browser/omnibox/model/omnibox_text_controller.h
@@ -50,6 +50,9 @@
 /// Returns whether the omnibox is first responder.
 - (BOOL)isOmniboxFirstResponder;
 
+/// Focuses the omnibox.
+- (void)focusOmnibox;
+
 /// Ends omnibox editing / defocus the omnibox.
 - (void)endEditing;
 
diff --git a/ios/chrome/browser/omnibox/model/omnibox_text_controller.mm b/ios/chrome/browser/omnibox/model/omnibox_text_controller.mm
index 42fc9df..45b6963 100644
--- a/ios/chrome/browser/omnibox/model/omnibox_text_controller.mm
+++ b/ios/chrome/browser/omnibox/model/omnibox_text_controller.mm
@@ -6,6 +6,7 @@
 
 #import <UniformTypeIdentifiers/UniformTypeIdentifiers.h>
 
+#import "base/ios/ios_util.h"
 #import "base/memory/raw_ptr.h"
 #import "base/metrics/user_metrics.h"
 #import "base/metrics/user_metrics_action.h"
@@ -89,6 +90,29 @@
   return [self.textField isFirstResponder];
 }
 
+- (void)focusOmnibox {
+  UITextField* textField = self.textField;
+  if ([textField isFirstResponder]) {
+    return;
+  }
+  base::RecordAction(base::UserMetricsAction("MobileOmniboxFocused"));
+
+  // In multiwindow context, -becomeFirstRepsonder is not enough to get the
+  // keyboard input. The window will not automatically become key. Make it key
+  // manually. UITextField does this under the hood when tapped from
+  // -[UITextInteractionAssistant(UITextInteractionAssistant_Internal)
+  // setFirstResponderIfNecessaryActivatingSelection:]
+  if (base::ios::IsMultipleScenesSupported()) {
+    [textField.window makeKeyAndVisible];
+  }
+
+  [textField becomeFirstResponder];
+  // Ensures that the accessibility system focuses the text field instead of
+  // the popup crbug.com/1469173.
+  UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification,
+                                  textField);
+}
+
 - (void)endEditing {
   [self hideKeyboard];
 
diff --git a/ios/chrome/browser/post_restore_signin/ui_bundled/post_restore_signin_provider.mm b/ios/chrome/browser/post_restore_signin/ui_bundled/post_restore_signin_provider.mm
index 07cccf3..a63af2c 100644
--- a/ios/chrome/browser/post_restore_signin/ui_bundled/post_restore_signin_provider.mm
+++ b/ios/chrome/browser/post_restore_signin/ui_bundled/post_restore_signin_provider.mm
@@ -183,6 +183,11 @@
 }
 
 - (void)signinDone {
+  if (![self isSignedIn]) {
+    // TODO(crbug.com/418696054): Convert to NOTREACHED.
+    DUMP_WILL_BE_NOTREACHED();
+    return;
+  }
   _syncUserSettings->SetSelectedType(syncer::UserSelectableType::kHistory,
                                      _historySyncEnabled);
   _syncUserSettings->SetSelectedType(syncer::UserSelectableType::kTabs,
diff --git a/ios/chrome/browser/shared/model/browser/test/test_browser.mm b/ios/chrome/browser/shared/model/browser/test/test_browser.mm
index c89b735..f804dde 100644
--- a/ios/chrome/browser/shared/model/browser/test/test_browser.mm
+++ b/ios/chrome/browser/shared/model/browser/test/test_browser.mm
@@ -52,10 +52,11 @@
           profile->IsOffTheRecord() ? Type::kIncognito : Type::kRegular) {}
 
 TestBrowser::~TestBrowser() {
+  // Ensure all WebStates are closed before destroying the Browser.
+  CloseAllWebStates(*web_state_list_, WebStateList::CLOSE_NO_FLAGS);
   for (auto& observer : observers_) {
     observer.BrowserDestroyed(this);
   }
-  web_state_list_.reset();
   ClearAllUserData();
 }
 
diff --git a/ios/chrome/browser/shared/public/commands/docking_promo_commands.h b/ios/chrome/browser/shared/public/commands/docking_promo_commands.h
index c78305e..fc3fda9b 100644
--- a/ios/chrome/browser/shared/public/commands/docking_promo_commands.h
+++ b/ios/chrome/browser/shared/public/commands/docking_promo_commands.h
@@ -5,20 +5,11 @@
 #ifndef IOS_CHROME_BROWSER_SHARED_PUBLIC_COMMANDS_DOCKING_PROMO_COMMANDS_H_
 #define IOS_CHROME_BROWSER_SHARED_PUBLIC_COMMANDS_DOCKING_PROMO_COMMANDS_H_
 
-// Trigger sources for the docking promo.
-enum class DockingPromoTrigger {
-  kTriggerUnset,
-  kTipsModule,
-  kSetUpList,
-  kPromosManager,
-  kFRE,
-};
-
 // Commands to show app-wide Docking Promo(s).
 @protocol DockingPromoCommands <NSObject>
 
 // Show Docking Promo if conditions are met, or if `forced` is YES.
-- (void)showDockingPromoWithTrigger:(DockingPromoTrigger)trigger;
+- (void)showDockingPromo:(BOOL)forced;
 
 @end
 
diff --git a/ios/chrome/browser/shared/public/commands/tab_grid_toolbar_commands.h b/ios/chrome/browser/shared/public/commands/tab_grid_toolbar_commands.h
index 0118f0e..4178bf9b 100644
--- a/ios/chrome/browser/shared/public/commands/tab_grid_toolbar_commands.h
+++ b/ios/chrome/browser/shared/public/commands/tab_grid_toolbar_commands.h
@@ -5,12 +5,19 @@
 #ifndef IOS_CHROME_BROWSER_SHARED_PUBLIC_COMMANDS_TAB_GRID_TOOLBAR_COMMANDS_H_
 #define IOS_CHROME_BROWSER_SHARED_PUBLIC_COMMANDS_TAB_GRID_TOOLBAR_COMMANDS_H_
 
+#include "base/ios/block_types.h"
+
 // Protocol used to send commands to the tab grid toolbars.
 @protocol TabGridToolbarCommands
 
 // Display an IPH to advertise the Tab Group view.
 - (void)showSavedTabGroupIPH;
 
+// Display the Guided Tour step that highlights the Incognito tab. `completion`
+// will be executed after the step dismisses.
+- (void)showGuidedTourIncognitoStepWithDismissalCompletion:
+    (ProceduralBlock)completion;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_SHARED_PUBLIC_COMMANDS_TAB_GRID_TOOLBAR_COMMANDS_H_
diff --git a/ios/chrome/browser/shared/ui/util/identity_snackbar/identity_snackbar_message_view.mm b/ios/chrome/browser/shared/ui/util/identity_snackbar/identity_snackbar_message_view.mm
index 9648fd24..609ab48 100644
--- a/ios/chrome/browser/shared/ui/util/identity_snackbar/identity_snackbar_message_view.mm
+++ b/ios/chrome/browser/shared/ui/util/identity_snackbar/identity_snackbar_message_view.mm
@@ -293,14 +293,21 @@
       UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPhone &&
       UIDeviceOrientationIsPortrait(UIDevice.currentDevice.orientation);
   NSString* email = _snackbarMessage.email;
-  _emailView.text =
-      useShortLabels
-          ? email
-          : l10n_util::GetNSStringF(
-                _snackbarMessage.managementState.is_browser_managed()
-                    ? IDS_IOS_ENTERPRISE_SWITCH_TO_MANAGED_BROWSER_WIDE_SCREEN
-                    : IDS_IOS_ENTERPRISE_SWITCH_TO_MANAGED_WIDE_SCREEN,
-                base::SysNSStringToUTF16(email));
+  if (useShortLabels) {
+    _emailView.text = email;
+  } else if (_snackbarMessage.managementState.is_browser_managed()) {
+    _emailView.text = l10n_util::GetNSStringF(
+        IDS_IOS_ENTERPRISE_SWITCH_TO_MANAGED_BROWSER_WIDE_SCREEN,
+        base::SysNSStringToUTF16(email));
+  } else if (AreSeparateProfilesForManagedAccountsEnabled()) {
+    _emailView.text = l10n_util::GetNSStringF(
+        IDS_IOS_ENTERPRISE_SWITCH_TO_MANAGED_ACCOUNT_WIDE_SCREEN,
+        base::SysNSStringToUTF16(email));
+  } else {
+    _emailView.text = l10n_util::GetNSStringF(
+        IDS_IOS_ENTERPRISE_SWITCH_TO_MANAGED_WIDE_SCREEN,
+        base::SysNSStringToUTF16(email));
+  }
 
   if (!useShortLabels) {
     _managementView.text = nil;
@@ -308,7 +315,7 @@
     _managementView.text = l10n_util::GetNSString(
         _snackbarMessage.managementState.is_browser_managed()
             ? IDS_IOS_ENTERPRISE_BROWSER_MANAGED
-            : IDS_IOS_ENTERPRISE_MANAGED_BY_YOUR_ORGANIZATION);
+            : IDS_IOS_ENTERPRISE_ACCOUNT_MANAGED);
   } else {
     _managementView.text =
         l10n_util::GetNSString(IDS_IOS_ENTERPRISE_MANAGED_BY_YOUR_ORGANIZATION);
diff --git a/ios/chrome/browser/shared/ui/util/layout_guide_names.h b/ios/chrome/browser/shared/ui/util/layout_guide_names.h
index 29e629401..4dea83b5 100644
--- a/ios/chrome/browser/shared/ui/util/layout_guide_names.h
+++ b/ios/chrome/browser/shared/ui/util/layout_guide_names.h
@@ -51,6 +51,11 @@
 // A guide that is constrained to match the frame of the bottom toolbar in the
 // tab grid.
 extern GuideName* const kTabGridBottomToolbarGuide;
+// A guide that is constrained to match the frame of the Tab Grid page control.
+extern GuideName* const kTabGridPageControlGuide;
+// A guide that is constrained to match the frame of the incognito page of the
+// Tab Grid page control.
+extern GuideName* const kTabGridPageControlIncognitoGuide;
 // A guide that is constrained to match the frame of the first Autofill result.
 extern GuideName* const kAutofillFirstSuggestionGuide;
 // A guide that is constrained to match the frame of the Lens button in the
diff --git a/ios/chrome/browser/shared/ui/util/layout_guide_names.mm b/ios/chrome/browser/shared/ui/util/layout_guide_names.mm
index a1cfed6..5858f17 100644
--- a/ios/chrome/browser/shared/ui/util/layout_guide_names.mm
+++ b/ios/chrome/browser/shared/ui/util/layout_guide_names.mm
@@ -19,6 +19,9 @@
 GuideName* const kFeedIPHNamedGuide = @"kFeedIPHNamedGuide";
 GuideName* const kShareButtonGuide = @"kShareButtonGuide";
 GuideName* const kTabGridBottomToolbarGuide = @"kTabGridBottomToolbarGuide";
+GuideName* const kTabGridPageControlGuide = @"kTabGridPageControlGuide";
+GuideName* const kTabGridPageControlIncognitoGuide =
+    @"kTabGridPageControlIncognitoGuide";
 GuideName* const kAutofillFirstSuggestionGuide =
     @"kAutofillFirstSuggestionGuide";
 GuideName* const kLensKeyboardButtonGuide = @"kLensKeyboardButtonGuide";
diff --git a/ios/chrome/browser/signin/model/system_account_updater.mm b/ios/chrome/browser/signin/model/system_account_updater.mm
index 3f384d3c..edd2311 100644
--- a/ios/chrome/browser/signin/model/system_account_updater.mm
+++ b/ios/chrome/browser/signin/model/system_account_updater.mm
@@ -189,6 +189,10 @@
 }
 
 void SystemAccountUpdater::HandleMigrationIfNeeded() {
+  // Perform migration only if the flag is enabled.
+  if (!IsWidgetsForMultiprofileEnabled()) {
+    return;
+  }
   PrefService* local_state = GetApplicationContext()->GetLocalState();
 
   if (!local_state) {
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/BUILD.gn b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/BUILD.gn
index 5939646a..4c3727f7 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/BUILD.gn
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/BUILD.gn
@@ -30,6 +30,7 @@
     "//components/strings",
     "//components/supervised_user/core/browser",
     "//components/supervised_user/core/common",
+    "//ios/chrome/app/profile:first_run_profile_agent",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/authentication/ui_bundled/history_sync",
     "//ios/chrome/browser/authentication/ui_bundled/signin:constants",
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/DEPS b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/DEPS
index 7e52ec9..4e49899 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/DEPS
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/DEPS
@@ -2,6 +2,7 @@
   "+components/collaboration/public",
   "+components/saved_tab_groups/public",
   "+ios/chrome/browser/settings/ui_bundled",
+  "+ios/chrome/browser/first_run/ui_bundled/guided_tour/guided_tour_coordinator.h",
 ]
 
 specific_include_rules = {
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_grid_coordinator.mm b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_grid_coordinator.mm
index b9cbbc22..2517e4b 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_grid_coordinator.mm
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_grid_coordinator.mm
@@ -24,6 +24,7 @@
 #import "components/strings/grit/components_strings.h"
 #import "components/supervised_user/core/browser/supervised_user_utils.h"
 #import "components/sync/service/sync_service.h"
+#import "ios/chrome/app/profile/first_run_profile_agent.h"
 #import "ios/chrome/browser/authentication/ui_bundled/history_sync/history_sync_coordinator.h"
 #import "ios/chrome/browser/authentication/ui_bundled/history_sync/history_sync_popup_coordinator.h"
 #import "ios/chrome/browser/authentication/ui_bundled/history_sync/history_sync_utils.h"
@@ -486,6 +487,13 @@
   __weak __typeof(self) weakSelf = self;
 
   ProceduralBlock transitionCompletionBlock = ^{
+    TabGridCoordinator* strongSelf = weakSelf;
+    if (strongSelf && IsBestOfAppGuidedTourEnabled()) {
+      Browser* browser = strongSelf.regularBrowser;
+      FirstRunProfileAgent* profileAgent = [FirstRunProfileAgent
+          agentFromProfile:browser->GetSceneState().profileState];
+      [profileAgent tabGridWasPresented];
+    }
     [weakSelf transitionToGridCompleteForAndroidTabsPrompt:
                   shouldDisplayBringAndroidTabsPrompt];
   };
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/BUILD.gn b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/BUILD.gn
index 4f78755..fc26fee 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/BUILD.gn
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/BUILD.gn
@@ -17,13 +17,16 @@
     "//components/feature_engagement/public",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/bubble/ui_bundled",
+    "//ios/chrome/browser/bubble/ui_bundled/guided_tour",
     "//ios/chrome/browser/feature_engagement/model",
+    "//ios/chrome/browser/first_run/ui_bundled/guided_tour",
     "//ios/chrome/browser/menu/ui_bundled",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/browser",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/chrome/browser/shared/public/commands",
+    "//ios/chrome/browser/shared/public/features",
     "//ios/chrome/browser/tab_switcher/ui_bundled/tab_grid:tab_grid_mode",
     "//ios/chrome/browser/tab_switcher/ui_bundled/tab_grid:tab_grid_paging",
     "//ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid:grid_toolbars_mutator",
@@ -56,9 +59,11 @@
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/incognito_reauth/ui_bundled:features",
     "//ios/chrome/browser/keyboard/ui_bundled",
+    "//ios/chrome/browser/shared/coordinator/layout_guide",
     "//ios/chrome/browser/shared/public/features",
     "//ios/chrome/browser/shared/ui/symbols",
     "//ios/chrome/browser/shared/ui/util",
+    "//ios/chrome/browser/shared/ui/util:util_swift",
     "//ios/chrome/browser/tab_switcher/ui_bundled/tab_grid:tab_grid_paging",
     "//ios/chrome/browser/tab_switcher/ui_bundled/tab_grid:tab_grid_ui_constants",
     "//ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid:grid_ui_constants",
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_page_control.h b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_page_control.h
index 897966a..8352a253 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_page_control.h
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_page_control.h
@@ -69,9 +69,10 @@
 // is `scrolledToEdge` or not.
 - (void)setScrollViewScrolledToEdge:(BOOL)scrolledToEdge;
 
-// Highlights (put a blue background) the last element of the page control.
-- (void)highlightLastPageControl;
-// Removes the highlight on the last page control, if there is one.
+// Highlights (put a blue background) the `page`.
+- (void)highlightPageControlItem:(TabGridPage)page;
+
+// Removes the last highlighted page of the page control, if there is one.
 - (void)resetLastPageControlHighlight;
 
 // Returns the frame of the last segment, in window coordinates.
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_page_control.mm b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_page_control.mm
index 3b87e583..68900fd 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_page_control.mm
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_page_control.mm
@@ -9,9 +9,12 @@
 #import <algorithm>
 
 #import "base/check_op.h"
+#import "ios/chrome/browser/shared/coordinator/layout_guide/layout_guide_util.h"
 #import "ios/chrome/browser/shared/public/features/features.h"
 #import "ios/chrome/browser/shared/ui/symbols/symbols.h"
+#import "ios/chrome/browser/shared/ui/util/layout_guide_names.h"
 #import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h"
+#import "ios/chrome/browser/shared/ui/util/util_swift.h"
 #import "ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_grid_constants.h"
 #import "ios/chrome/common/ui/colors/semantic_color_names.h"
 #import "ios/chrome/grit/ios_strings.h"
@@ -203,8 +206,9 @@
   UIAccessibilityElement* _regularAccessibilityElement;
   UIAccessibilityElement* _thirdPanelAccessibilityElement;
 
-  // Highlight view for the last control.
+  // Highlighted view and associated icon.
   UIView* _highlightView;
+  UIView* _highlightedIcon;
 }
 
 + (instancetype)pageControl {
@@ -369,7 +373,7 @@
   }
 }
 
-- (void)highlightLastPageControl {
+- (void)highlightPageControlItem:(TabGridPage)page {
   UIView* highlightBackground = [[UIView alloc] init];
   highlightBackground.translatesAutoresizingMaskIntoConstraints = NO;
   highlightBackground.backgroundColor = [UIColor colorNamed:kBlueColor];
@@ -377,16 +381,39 @@
 
   [self insertSubview:highlightBackground aboveSubview:self.background];
 
+  UILayoutGuide* pageGuide;
   [NSLayoutConstraint activateConstraints:@[
-    [highlightBackground.trailingAnchor
-        constraintEqualToAnchor:self.thirdPanelGuide.trailingAnchor],
     [highlightBackground.topAnchor
         constraintEqualToAnchor:self.sliderView.topAnchor],
     [highlightBackground.bottomAnchor
         constraintEqualToAnchor:self.sliderView.bottomAnchor],
-    [highlightBackground.leadingAnchor
-        constraintEqualToAnchor:self.regularGuide.centerXAnchor],
   ]];
+  switch (page) {
+    case TabGridPageIncognitoTabs:
+      pageGuide = self.incognitoGuide;
+      _highlightedIcon = self.incognitoNotSelectedIcon;
+      [NSLayoutConstraint activateConstraints:@[
+        [highlightBackground.leadingAnchor
+            constraintEqualToAnchor:pageGuide.leadingAnchor],
+        [highlightBackground.trailingAnchor
+            constraintEqualToAnchor:self.regularGuide.centerXAnchor]
+      ]];
+      break;
+    case TabGridPageRemoteTabs:
+    case TabGridPageTabGroups:
+      pageGuide = self.thirdPanelGuide;
+      _highlightedIcon = self.thirdPanelNotSelectedIcon;
+      [NSLayoutConstraint activateConstraints:@[
+        [highlightBackground.leadingAnchor
+            constraintEqualToAnchor:self.regularGuide.centerXAnchor],
+        [highlightBackground.trailingAnchor
+            constraintEqualToAnchor:pageGuide.trailingAnchor]
+      ]];
+      break;
+    case TabGridPageRegularTabs:
+      // Not supported right now.
+      break;
+  }
 
   highlightBackground.alpha = 0;
   [UIView animateWithDuration:kHighlightAnimationDuration
@@ -394,12 +421,13 @@
                      highlightBackground.alpha = 1;
                    }];
 
-  self.thirdPanelNotSelectedIcon.tintColor = UIColor.blackColor;
 
   _highlightView = highlightBackground;
+  _highlightedIcon.tintColor = UIColor.blackColor;
 }
 
 - (void)resetLastPageControlHighlight {
+  CHECK(_highlightView);
   UIView* highlightView = _highlightView;
   [UIView animateWithDuration:kHighlightAnimationDuration
       animations:^{
@@ -409,8 +437,7 @@
         [highlightView removeFromSuperview];
       }];
   _highlightView = nil;
-  self.thirdPanelNotSelectedIcon.tintColor =
-      [UIColor colorNamed:kStaticGrey300Color];
+  _highlightedIcon.tintColor = [UIColor colorNamed:kStaticGrey300Color];
 }
 
 - (CGRect)lastSegmentFrame {
@@ -721,6 +748,10 @@
   self.regularSelectedLabel = regularSelectedLabel;
 
   self.incognitoHoverView = [self configureHoverView];
+
+  LayoutGuideCenter* center = LayoutGuideCenterForBrowser(nil);
+  [center referenceView:self.incognitoHoverView
+              underName:kTabGridPageControlIncognitoGuide];
   self.regularHoverView = [self configureHoverView];
   self.thirdPanelHoverView = [self configureHoverView];
 
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_toolbars_coordinator.mm b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_toolbars_coordinator.mm
index 6e8b7e6e..9c5b9a97 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_toolbars_coordinator.mm
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_toolbars_coordinator.mm
@@ -10,6 +10,7 @@
 #import "components/feature_engagement/public/feature_constants.h"
 #import "ios/chrome/browser/bubble/ui_bundled/bubble_view_controller_presenter.h"
 #import "ios/chrome/browser/feature_engagement/model/tracker_factory.h"
+#import "ios/chrome/browser/first_run/ui_bundled/guided_tour/guided_tour_coordinator.h"
 #import "ios/chrome/browser/shared/model/browser/browser.h"
 #import "ios/chrome/browser/shared/model/profile/profile_ios.h"
 #import "ios/chrome/browser/shared/public/commands/command_dispatcher.h"
@@ -23,12 +24,16 @@
 #import "ios/chrome/grit/ios_strings.h"
 #import "ui/base/l10n/l10n_util_mac.h"
 
-@interface TabGridToolbarsCoordinator () <TabGridToolbarCommands>
+@interface TabGridToolbarsCoordinator () <GuidedTourCoordinatorDelegate,
+                                          TabGridToolbarCommands>
 @end
 
 @implementation TabGridToolbarsCoordinator {
   // Mediator of all tab grid toolbars.
   TabGridToolbarsMediator* _mediator;
+  // Coordinator for the first step of the guided tour.
+  GuidedTourCoordinator* _guidedTourCoordinator;
+  ProceduralBlock _guidedTourCompletionBlock;
 }
 
 - (void)start {
@@ -99,11 +104,36 @@
     return;
   }
 
-  [self.topToolbar highlightLastPageControl];
+  [self.topToolbar highlightPageControlItem:TabGridPageTabGroups];
   [presenter presentInViewController:self.baseViewController
                          anchorPoint:anchorPoint];
 }
 
+- (void)showGuidedTourIncognitoStepWithDismissalCompletion:
+    (ProceduralBlock)completion {
+  [self.topToolbar highlightPageControlItem:TabGridPageIncognitoTabs];
+  _guidedTourCoordinator =
+      [[GuidedTourCoordinator alloc] initWithStep:GuidedTourStepTabGridIncognito
+                               baseViewController:self.baseViewController
+                                          browser:self.browser
+                                         delegate:self];
+  [_guidedTourCoordinator start];
+  _guidedTourCompletionBlock = completion;
+}
+
+#pragma mark - GuidedTourCoordinatorDelegate
+
+- (void)nextTappedForStep:(GuidedTourStep)step {
+  [self.topToolbar resetLastPageControlHighlight];
+}
+
+// Indicates to the delegate that the `step` was dismissed.
+- (void)stepCompleted:(GuidedTourStep)step {
+  [_guidedTourCoordinator stop];
+  _guidedTourCoordinator = nil;
+  _guidedTourCompletionBlock();
+}
+
 #pragma mark - Private
 
 // Callback for when the saved tab group IPH is dismissed.
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_top_toolbar.h b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_top_toolbar.h
index 382d5dd..9d14ad8 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_top_toolbar.h
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_top_toolbar.h
@@ -66,9 +66,9 @@
 // Sets the title of the Select All button to "Select All".
 - (void)configureSelectAllButtonTitle;
 
-// Highlights (put a blue background) the last element of the page control.
-- (void)highlightLastPageControl;
-// Removes the highlight on the last page control, if there is one.
+// Highlights (put a blue background) `page` of the page control.
+- (void)highlightPageControlItem:(TabGridPage)page;
+// Removes the last highlighted page, if there is one.
 - (void)resetLastPageControlHighlight;
 
 // Hides components and uses a black background color for tab grid transition
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_top_toolbar.mm b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_top_toolbar.mm
index b0952d1..3b0651b 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_top_toolbar.mm
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_top_toolbar.mm
@@ -15,8 +15,11 @@
 #import "base/task/sequenced_task_runner.h"
 #import "ios/chrome/browser/incognito_reauth/ui_bundled/features.h"
 #import "ios/chrome/browser/keyboard/ui_bundled/UIKeyCommand+Chrome.h"
+#import "ios/chrome/browser/shared/coordinator/layout_guide/layout_guide_util.h"
 #import "ios/chrome/browser/shared/ui/symbols/symbols.h"
+#import "ios/chrome/browser/shared/ui/util/layout_guide_names.h"
 #import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h"
+#import "ios/chrome/browser/shared/ui/util/util_swift.h"
 #import "ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/grid_constants.h"
 #import "ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_grid_constants.h"
 #import "ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_page_control.h"
@@ -210,8 +213,8 @@
       l10n_util::GetNSString(IDS_IOS_TAB_GRID_SELECT_ALL_BUTTON);
 }
 
-- (void)highlightLastPageControl {
-  [self.pageControl highlightLastPageControl];
+- (void)highlightPageControlItem:(TabGridPage)page {
+  [self.pageControl highlightPageControlItem:TabGridPageIncognitoTabs];
 }
 
 - (void)resetLastPageControlHighlight {
@@ -449,6 +452,9 @@
   // The segmented control has an intrinsic size.
   _pageControl = [[TabGridPageControl alloc] init];
   _pageControl.translatesAutoresizingMaskIntoConstraints = NO;
+
+  LayoutGuideCenter* center = LayoutGuideCenterForBrowser(nil);
+  [center referenceView:_pageControl underName:kTabGridPageControlGuide];
   [_pageControl setScrollViewScrolledToEdge:_scrolledToEdge];
   _pageControlItem = [[UIBarButtonItem alloc] initWithCustomView:_pageControl];
 
diff --git a/ios/chrome/browser/tips_notifications/model/tips_notification_client.mm b/ios/chrome/browser/tips_notifications/model/tips_notification_client.mm
index 62cecb52..8526d792 100644
--- a/ios/chrome/browser/tips_notifications/model/tips_notification_client.mm
+++ b/ios/chrome/browser/tips_notifications/model/tips_notification_client.mm
@@ -672,7 +672,7 @@
 void TipsNotificationClient::ShowDocking(Browser* browser) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   [HandlerForProtocol(browser->GetCommandDispatcher(), DockingPromoCommands)
-      showDockingPromoWithTrigger:DockingPromoTrigger::kTipsModule];
+      showDockingPromo:YES];
 }
 
 void TipsNotificationClient::ShowOmniboxPosition(Browser* browser) {
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 b28617f..721f1f4 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
@@ -520,8 +520,7 @@
 TEST_F(TipsNotificationClientTest, DockingHandle) {
   StubPrepareToPresentModal();
   id mock_handler = MockHandler(@protocol(DockingPromoCommands));
-  OCMExpect([mock_handler
-      showDockingPromoWithTrigger:DockingPromoTrigger::kTipsModule]);
+  OCMExpect([mock_handler showDockingPromo:YES]);
 
   id mock_response = MockRequestResponse(TipsNotificationType::kDocking);
   client_->HandleNotificationInteraction(mock_response);
diff --git a/ios_internal b/ios_internal
index 3c0b477..5f6cd52 160000
--- a/ios_internal
+++ b/ios_internal
@@ -1 +1 @@
-Subproject commit 3c0b4772fb5fc185f901affc85f8c1aa315ca8a8
+Subproject commit 5f6cd5274af6da369855f92f45b3721d4707b7e4
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc
index 1e889c76..7974b20 100644
--- a/media/base/media_switches.cc
+++ b/media/base/media_switches.cc
@@ -327,7 +327,7 @@
              base::FEATURE_ENABLED_BY_DEFAULT);
 
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_ANDROID)
-// Enables HEVC hardware accelerated encoding for Windows, Mac, and Android.
+// Enables HEVC hardware accelerated encoding for Windows, Apple, and Android.
 BASE_FEATURE(kPlatformHEVCEncoderSupport,
              "PlatformHEVCEncoderSupport",
              base::FEATURE_ENABLED_BY_DEFAULT);
diff --git a/media/gpu/android/ndk_video_encode_accelerator.cc b/media/gpu/android/ndk_video_encode_accelerator.cc
index d6706a0e..dae5ec8 100644
--- a/media/gpu/android/ndk_video_encode_accelerator.cc
+++ b/media/gpu/android/ndk_video_encode_accelerator.cc
@@ -1074,7 +1074,7 @@
     }
 
     TemporalScalabilityIdExtractor::BitstreamMetadata bits_md;
-    if (!svc_parser_->ParseChunk(output_dst.subspan(mc_buffer_size),
+    if (!svc_parser_->ParseChunk(output_dst.first(mc_buffer_size),
                                  input_since_keyframe_count_, bits_md)) {
       NotifyErrorStatus({EncoderStatus::Codes::kEncoderHardwareDriverError,
                          "Parse bitstream failed"});
diff --git a/media/gpu/chromeos/mailbox_video_frame_converter_unittest.cc b/media/gpu/chromeos/mailbox_video_frame_converter_unittest.cc
index a26c421..f42b3a3 100644
--- a/media/gpu/chromeos/mailbox_video_frame_converter_unittest.cc
+++ b/media/gpu/chromeos/mailbox_video_frame_converter_unittest.cc
@@ -128,6 +128,12 @@
                scoped_refptr<gpu::ClientSharedImage>(
                    const gpu::SharedImageInfo& si_info,
                    gfx::GpuMemoryBufferHandle buffer_handle));
+  MOCK_METHOD4(
+      CreateSharedImageForMLTensor,
+      scoped_refptr<gpu::ClientSharedImage>(std::string debug_label,
+                                            viz::SharedImageFormat format,
+                                            const gfx::Size& size,
+                                            gpu::SharedImageUsageSet usage));
   MOCK_METHOD1(CreateSharedImageForSoftwareCompositor,
                scoped_refptr<gpu::ClientSharedImage>(
                    const gpu::SharedImageInfo& si_info));
diff --git a/media/gpu/mac/video_toolbox_vp9_accelerator.cc b/media/gpu/mac/video_toolbox_vp9_accelerator.cc
index fc9ac4baf..3a858a59 100644
--- a/media/gpu/mac/video_toolbox_vp9_accelerator.cc
+++ b/media/gpu/mac/video_toolbox_vp9_accelerator.cc
@@ -125,6 +125,11 @@
   DVLOG(4) << __func__;
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
+  if (pic->frame_hdr->show_existing_frame) {
+    *format_changed = false;
+    return true;
+  }
+
   // TODO(crbug.com/40227557): Consider merging with CreateFormatExtensions() to
   // avoid converting back and forth.
   VideoColorSpace color_space = pic->get_colorspace();
@@ -155,6 +160,10 @@
 
   gfx::Size coded_size(static_cast<int>(pic->frame_hdr->frame_width),
                        static_cast<int>(pic->frame_hdr->frame_height));
+  if (coded_size.IsEmpty()) {
+    DLOG(ERROR) << "Invalid frame size";
+    return false;
+  }
 
   // If the parameters have changed, generate a new format.
   if (color_space != active_color_space_ || profile != active_profile_ ||
diff --git a/media/gpu/mac/video_toolbox_vp9_accelerator_unittest.cc b/media/gpu/mac/video_toolbox_vp9_accelerator_unittest.cc
index a9c21e72..644569c0 100644
--- a/media/gpu/mac/video_toolbox_vp9_accelerator_unittest.cc
+++ b/media/gpu/mac/video_toolbox_vp9_accelerator_unittest.cc
@@ -52,6 +52,8 @@
   pic->frame_hdr = std::make_unique<Vp9FrameHeader>();
   pic->frame_hdr->show_frame = true;
   pic->frame_hdr->data = base::span(frame_data);
+  pic->frame_hdr->frame_width = 640;
+  pic->frame_hdr->frame_height = 480;
 
   // Save the resulting sample.
   base::apple::ScopedCFTypeRef<CMSampleBufferRef> sample;
@@ -77,11 +79,11 @@
   constexpr uint8_t frame_data2[] = {0x02};
 
   scoped_refptr<VP9Picture> pic1 = accelerator_->CreateVP9Picture();
-  pic1->frame_hdr = std::make_unique<Vp9FrameHeader>();
   pic1->frame_hdr->data = base::span(frame_data1);
+  pic1->frame_hdr->frame_width = 640;
+  pic1->frame_hdr->frame_height = 480;
 
-  scoped_refptr<VP9Picture> pic2 = accelerator_->CreateVP9Picture();
-  pic2->frame_hdr = std::make_unique<Vp9FrameHeader>();
+  scoped_refptr<VP9Picture> pic2 = pic1->Duplicate();
   pic2->frame_hdr->show_existing_frame = true;
   pic2->frame_hdr->data = base::span(frame_data2);
 
@@ -90,7 +92,7 @@
   EXPECT_CALL(*this, OnDecode(_, _, _)).WillOnce(SaveArg<0>(&sample));
   EXPECT_CALL(*this, OnOutput(_));
   accelerator_->SubmitDecode(pic1, segm_params, lf_params, reference_frames);
-  accelerator_->OutputPicture(pic2);
+  EXPECT_TRUE(accelerator_->OutputPicture(pic2));
 
   // Verify `sample`.
   CMBlockBufferRef buf = CMSampleBufferGetDataBuffer(sample.get());
diff --git a/media/gpu/vp9_picture.cc b/media/gpu/vp9_picture.cc
index 76afea8..9000e16 100644
--- a/media/gpu/vp9_picture.cc
+++ b/media/gpu/vp9_picture.cc
@@ -10,7 +10,7 @@
 
 namespace media {
 
-VP9Picture::VP9Picture() : frame_hdr(new Vp9FrameHeader()) {}
+VP9Picture::VP9Picture() : frame_hdr(std::make_unique<Vp9FrameHeader>()) {}
 VP9Picture::~VP9Picture() = default;
 
 V4L2VP9Picture* VP9Picture::AsV4L2VP9Picture() {
diff --git a/media/muxers/mp4_movie_box_writer.cc b/media/muxers/mp4_movie_box_writer.cc
index 72b5881..7b5255e 100644
--- a/media/muxers/mp4_movie_box_writer.cc
+++ b/media/muxers/mp4_movie_box_writer.cc
@@ -987,7 +987,7 @@
 void Mp4MovieVPCodecConfigurationBoxWriter::Write(BoxByteStream& writer) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  writer.StartFullBox(mp4::FOURCC_VPCC);
+  writer.StartFullBox(mp4::FOURCC_VPCC, /*flags=*/0, /*version=*/1);
 
   switch (box_->profile) {
     case VP9PROFILE_PROFILE0:
@@ -1045,7 +1045,7 @@
 void Mp4MovieAV1CodecConfigurationBoxWriter::Write(BoxByteStream& writer) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  writer.StartFullBox(mp4::FOURCC_AV1C);
+  writer.StartBox(mp4::FOURCC_AV1C);
   writer.WriteBytes(box_->av1_decoder_configuration_data.data(),
                     box_->av1_decoder_configuration_data.size());
   writer.EndBox();
diff --git a/media/muxers/mp4_muxer_box_writer_unittest.cc b/media/muxers/mp4_muxer_box_writer_unittest.cc
index 71fdf94d..b3755a2d9 100644
--- a/media/muxers/mp4_muxer_box_writer_unittest.cc
+++ b/media/muxers/mp4_muxer_box_writer_unittest.cc
@@ -711,6 +711,8 @@
   mp4::AVCDecoderConfigurationRecord avc_config_reader;
   EXPECT_TRUE(box_reader->ReadChild(&avc_config_reader));
 
+  EXPECT_EQ(1u, avc_config_reader.version);
+
   EXPECT_EQ(kProfileIndication, avc_config_reader.profile_indication);
   EXPECT_EQ(kProfileCompatibility, avc_config_reader.profile_compatibility);
   EXPECT_EQ(kLevelIndication, avc_config_reader.avc_level);
diff --git a/net/http/transport_security_state_static.pins b/net/http/transport_security_state_static.pins
index cacfa9c..fe0e5d3 100644
--- a/net/http/transport_security_state_static.pins
+++ b/net/http/transport_security_state_static.pins
@@ -43,9 +43,9 @@
 #   hash function for preloaded entries again (we have already done so once).
 #
 
-# Last updated: 2025-05-19 12:55 UTC
+# Last updated: 2025-05-20 12:52 UTC
 PinsListTimestamp
-1747659324
+1747745567
 
 TestSPKI
 sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
diff --git a/net/http/transport_security_state_static_pins.json b/net/http/transport_security_state_static_pins.json
index 94f3e76..2107d8fa 100644
--- a/net/http/transport_security_state_static_pins.json
+++ b/net/http/transport_security_state_static_pins.json
@@ -31,7 +31,7 @@
 // the 'static_spki_hashes' and 'bad_static_spki_hashes' fields in 'pinsets'
 // refer to, and the timestamp at which the pins list was last updated.
 //
-// Last updated: 2025-05-19 12:55 UTC
+// Last updated: 2025-05-20 12:52 UTC
 //
 {
   "pinsets": [
diff --git a/net/reporting/reporting_browsing_data_remover_unittest.cc b/net/reporting/reporting_browsing_data_remover_unittest.cc
index de059d7..caf64673 100644
--- a/net/reporting/reporting_browsing_data_remover_unittest.cc
+++ b/net/reporting/reporting_browsing_data_remover_unittest.cc
@@ -47,7 +47,7 @@
   void AddReport(const GURL& url) {
     cache()->AddReport(std::nullopt, NetworkAnonymizationKey(), url,
                        kUserAgent_, kGroup_, kType_, base::Value::Dict(), 0,
-                       tick_clock()->NowTicks(), 0,
+                       tick_clock()->NowTicks(),
                        ReportingTargetType::kDeveloper);
   }
 
diff --git a/net/reporting/reporting_cache.h b/net/reporting/reporting_cache.h
index b52f04d7..ba4d9f0 100644
--- a/net/reporting/reporting_cache.h
+++ b/net/reporting/reporting_cache.h
@@ -80,7 +80,6 @@
       base::Value::Dict body,
       int depth,
       base::TimeTicks queued,
-      int attempts,
       ReportingTargetType target_type) = 0;
 
   // Gets all reports in the cache. The returned pointers are valid as long as
diff --git a/net/reporting/reporting_cache_impl.cc b/net/reporting/reporting_cache_impl.cc
index c7bd974..5775c76a 100644
--- a/net/reporting/reporting_cache_impl.cc
+++ b/net/reporting/reporting_cache_impl.cc
@@ -44,7 +44,6 @@
     base::Value::Dict body,
     int depth,
     base::TimeTicks queued,
-    int attempts,
     ReportingTargetType target_type) {
   // If |reporting_source| is present, it must not be empty.
   DCHECK(!(reporting_source.has_value() && reporting_source->is_empty()));
@@ -59,7 +58,7 @@
 
   auto report = std::make_unique<ReportingReport>(
       reporting_source, network_anonymization_key, url, user_agent, group_name,
-      type, std::move(body), depth, queued, attempts, target_type);
+      type, std::move(body), depth, queued, target_type);
 
   auto inserted = reports_.insert(std::move(report));
   DCHECK(inserted.second);
diff --git a/net/reporting/reporting_cache_impl.h b/net/reporting/reporting_cache_impl.h
index 2de26d0..879ce91 100644
--- a/net/reporting/reporting_cache_impl.h
+++ b/net/reporting/reporting_cache_impl.h
@@ -56,7 +56,6 @@
                  base::Value::Dict body,
                  int depth,
                  base::TimeTicks queued,
-                 int attempts,
                  ReportingTargetType target_type) override;
   void GetReports(
       std::vector<raw_ptr<const ReportingReport, VectorExperimental>>*
diff --git a/net/reporting/reporting_cache_unittest.cc b/net/reporting/reporting_cache_unittest.cc
index ef39fcf..04396a90 100644
--- a/net/reporting/reporting_cache_unittest.cc
+++ b/net/reporting/reporting_cache_unittest.cc
@@ -116,8 +116,7 @@
       const std::string& type,
       base::Value::Dict body,
       int depth,
-      base::TimeTicks queued,
-      int attempts) {
+      base::TimeTicks queued) {
     const base::Value::Dict body_clone(body.Clone());
 
     // The public API will only give us the (unordered) full list of reports in
@@ -127,8 +126,9 @@
     std::vector<raw_ptr<const ReportingReport, VectorExperimental>> before;
     cache()->GetReports(&before);
     cache()->AddReport(std::nullopt, network_anonymization_key, url, user_agent,
-                       group, type, std::move(body), depth, queued, attempts,
+                       group, type, std::move(body), depth, queued,
                        ReportingTargetType::kDeveloper);
+
     std::vector<raw_ptr<const ReportingReport, VectorExperimental>> after;
     cache()->GetReports(&after);
 
@@ -143,7 +143,7 @@
         EXPECT_EQ(body_clone, report->body);
         EXPECT_EQ(depth, report->depth);
         EXPECT_EQ(queued, report->queued);
-        EXPECT_EQ(attempts, report->attempts);
+        EXPECT_EQ(0, report->attempts);
         return report;
       }
     }
@@ -273,7 +273,7 @@
   EXPECT_TRUE(reports.empty());
 
   cache()->AddReport(kReportingSource_, kNak_, kUrl1_, kUserAgent_, kGroup1_,
-                     kType_, base::Value::Dict(), 0, kNowTicks_, 0,
+                     kType_, base::Value::Dict(), 0, kNowTicks_,
                      ReportingTargetType::kEnterprise);
   EXPECT_EQ(1, observer()->cached_reports_update_count());
 
@@ -313,10 +313,10 @@
   LoadReportingClients();
 
   cache()->AddReport(kReportingSource_, kNak_, kUrl1_, kUserAgent_, kGroup1_,
-                     kType_, base::Value::Dict(), 0, kNowTicks_, 0,
+                     kType_, base::Value::Dict(), 0, kNowTicks_,
                      ReportingTargetType::kDeveloper);
   cache()->AddReport(kReportingSource_, kNak_, kUrl1_, kUserAgent_, kGroup1_,
-                     kType_, base::Value::Dict(), 0, kNowTicks_, 0,
+                     kType_, base::Value::Dict(), 0, kNowTicks_,
                      ReportingTargetType::kDeveloper);
   EXPECT_EQ(2, observer()->cached_reports_update_count());
 
@@ -335,7 +335,7 @@
   LoadReportingClients();
 
   cache()->AddReport(kReportingSource_, kNak_, kUrl1_, kUserAgent_, kGroup1_,
-                     kType_, base::Value::Dict(), 0, kNowTicks_, 0,
+                     kType_, base::Value::Dict(), 0, kNowTicks_,
                      ReportingTargetType::kDeveloper);
   EXPECT_EQ(1, observer()->cached_reports_update_count());
 
@@ -374,7 +374,7 @@
   LoadReportingClients();
 
   cache()->AddReport(kReportingSource_, kNak_, kUrl1_, kUserAgent_, kGroup1_,
-                     kType_, base::Value::Dict(), 0, kNowTicks_, 0,
+                     kType_, base::Value::Dict(), 0, kNowTicks_,
                      ReportingTargetType::kDeveloper);
   EXPECT_EQ(1, observer()->cached_reports_update_count());
 
@@ -416,10 +416,11 @@
   const base::TimeTicks now = base::TimeTicks();
   const ReportingReport* report1 =
       AddAndReturnReport(kNak_, kUrl1_, kUserAgent_, kGroup1_, kType_,
-                         base::Value::Dict(), 0, now + base::Seconds(200), 0);
+                         base::Value::Dict(), 0, now + base::Seconds(200));
   const ReportingReport* report2 =
       AddAndReturnReport(kOtherNak_, kUrl1_, kUserAgent_, kGroup2_, kType_,
-                         base::Value::Dict(), 0, now + base::Seconds(100), 1);
+                         base::Value::Dict(), 0, now + base::Seconds(100));
+  cache()->IncrementReportsAttempts({report2});
   // Mark report1 and report2 as pending.
   EXPECT_THAT(cache()->GetReportsToDeliver(),
               ::testing::UnorderedElementsAre(report1, report2));
@@ -460,10 +461,10 @@
   // Add two new reports that will show up as "queued".
   const ReportingReport* report3 =
       AddAndReturnReport(kNak_, kUrl2_, kUserAgent_, kGroup1_, kType_,
-                         base::Value::Dict(), 2, now + base::Seconds(200), 0);
+                         base::Value::Dict(), 2, now + base::Seconds(200));
   const ReportingReport* report4 =
       AddAndReturnReport(kOtherNak_, kUrl1_, kUserAgent_, kGroup1_, kType_,
-                         base::Value::Dict(), 0, now + base::Seconds(300), 0);
+                         base::Value::Dict(), 0, now + base::Seconds(300));
   actual = cache()->GetReportsAsValue();
   expected = base::test::ParseJson(base::StringPrintf(
       R"json(
@@ -532,13 +533,13 @@
   // Queue a V1 report for each of these sources, and a V0 report (with a null
   // source) for the same URL.
   cache()->AddReport(source1, kNak_, kUrl1_, kUserAgent_, kGroup1_, kType_,
-                     base::Value::Dict(), 0, kNowTicks_, 0,
+                     base::Value::Dict(), 0, kNowTicks_,
                      ReportingTargetType::kDeveloper);
   cache()->AddReport(source2, kNak_, kUrl1_, kUserAgent_, kGroup1_, kType_,
-                     base::Value::Dict(), 0, kNowTicks_, 0,
+                     base::Value::Dict(), 0, kNowTicks_,
                      ReportingTargetType::kDeveloper);
   cache()->AddReport(std::nullopt, kNak_, kUrl1_, kUserAgent_, kGroup1_, kType_,
-                     base::Value::Dict(), 0, kNowTicks_, 0,
+                     base::Value::Dict(), 0, kNowTicks_,
                      ReportingTargetType::kDeveloper);
   EXPECT_EQ(3, observer()->cached_reports_update_count());
 
@@ -1656,7 +1657,7 @@
   for (size_t i = 0; i < max_report_count; ++i) {
     cache()->AddReport(kReportingSource_, kNak_, kUrl1_, kUserAgent_, kGroup1_,
                        kType_, base::Value::Dict(), 0, tick_clock()->NowTicks(),
-                       0, ReportingTargetType::kDeveloper);
+                       ReportingTargetType::kDeveloper);
     tick_clock()->Advance(base::Minutes(1));
   }
   EXPECT_EQ(max_report_count, report_count());
@@ -1664,7 +1665,7 @@
   // Add one more report to force the cache to evict one.
   cache()->AddReport(kReportingSource_, kNak_, kUrl1_, kUserAgent_, kGroup1_,
                      kType_, base::Value::Dict(), 0, tick_clock()->NowTicks(),
-                     0, ReportingTargetType::kDeveloper);
+                     ReportingTargetType::kDeveloper);
 
   // Make sure the cache evicted a report to make room for the new one, and make
   // sure the report evicted was the earliest-queued one.
@@ -1688,7 +1689,7 @@
   for (size_t i = 0; i < max_report_count; ++i) {
     reports.push_back(AddAndReturnReport(kNak_, kUrl1_, kUserAgent_, kGroup1_,
                                          kType_, base::Value::Dict(), 0,
-                                         tick_clock()->NowTicks(), 0));
+                                         tick_clock()->NowTicks()));
     tick_clock()->Advance(base::Minutes(1));
   }
   EXPECT_EQ(max_report_count, report_count());
@@ -1700,7 +1701,7 @@
   // Add one more report to force the cache to evict one. Since the cache has
   // only pending reports, it will be forced to evict the *new* report!
   cache()->AddReport(kReportingSource_, kNak_, kUrl1_, kUserAgent_, kGroup1_,
-                     kType_, base::Value::Dict(), 0, kNowTicks_, 0,
+                     kType_, base::Value::Dict(), 0, kNowTicks_,
                      ReportingTargetType::kDeveloper);
 
   // Make sure the cache evicted a report, and make sure the report evicted was
diff --git a/net/reporting/reporting_delivery_agent_unittest.cc b/net/reporting/reporting_delivery_agent_unittest.cc
index 0058e73..5b636aa0 100644
--- a/net/reporting/reporting_delivery_agent_unittest.cc
+++ b/net/reporting/reporting_delivery_agent_unittest.cc
@@ -76,7 +76,7 @@
 
     cache()->AddReport(std::nullopt, kNak_, kUrl_, kUserAgent_, kGroup_, kType_,
                        std::move(report_body), /*depth=*/0,
-                       /*queued=*/tick_clock()->NowTicks(), /*attempts=*/0,
+                       /*queued=*/tick_clock()->NowTicks(),
                        ReportingTargetType::kDeveloper);
   }
 
@@ -89,17 +89,16 @@
     cache()->AddReport(reporting_source, network_anonymization_key, url,
                        kUserAgent_, group, kType_, std::move(report_body),
                        /*depth=*/0, /*queued=*/tick_clock()->NowTicks(),
-                       /*attempts=*/0, ReportingTargetType::kDeveloper);
+                       ReportingTargetType::kDeveloper);
   }
 
   void AddEnterpriseReport(const GURL& url, const std::string& group) {
     base::Value::Dict report_body;
     report_body.Set("key", "value");
-    cache()->AddReport(/*reporting_source=*/std::nullopt,
-                       net::NetworkAnonymizationKey(), url, kUserAgent_, group,
-                       kType_, std::move(report_body), /*depth=*/0,
-                       /*queued=*/tick_clock()->NowTicks(), /*attempts=*/0,
-                       ReportingTargetType::kEnterprise);
+    cache()->AddReport(
+        /*reporting_source=*/std::nullopt, net::NetworkAnonymizationKey(), url,
+        kUserAgent_, group, kType_, std::move(report_body), /*depth=*/0,
+        /*queued=*/tick_clock()->NowTicks(), ReportingTargetType::kEnterprise);
   }
 
   // The first report added to the cache is uploaded immediately, and a timer is
diff --git a/net/reporting/reporting_endpoint_manager_unittest.cc b/net/reporting/reporting_endpoint_manager_unittest.cc
index 3c78bdf..3921ce3a 100644
--- a/net/reporting/reporting_endpoint_manager_unittest.cc
+++ b/net/reporting/reporting_endpoint_manager_unittest.cc
@@ -69,7 +69,6 @@
                  base::Value::Dict body,
                  int depth,
                  base::TimeTicks queued,
-                 int attempts,
                  ReportingTargetType target_type) override {
     NOTREACHED();
   }
diff --git a/net/reporting/reporting_garbage_collector_unittest.cc b/net/reporting/reporting_garbage_collector_unittest.cc
index a6f5ec53..aac07e4 100644
--- a/net/reporting/reporting_garbage_collector_unittest.cc
+++ b/net/reporting/reporting_garbage_collector_unittest.cc
@@ -51,7 +51,7 @@
   EXPECT_FALSE(garbage_collection_timer()->IsRunning());
 
   cache()->AddReport(std::nullopt, kNak_, kUrl_, kUserAgent_, kGroup_, kType_,
-                     base::Value::Dict(), 0, tick_clock()->NowTicks(), 0,
+                     base::Value::Dict(), 0, tick_clock()->NowTicks(),
                      ReportingTargetType::kDeveloper);
 
   EXPECT_TRUE(garbage_collection_timer()->IsRunning());
@@ -63,7 +63,7 @@
 
 TEST_F(ReportingGarbageCollectorTest, Report) {
   cache()->AddReport(std::nullopt, kNak_, kUrl_, kUserAgent_, kGroup_, kType_,
-                     base::Value::Dict(), 0, tick_clock()->NowTicks(), 0,
+                     base::Value::Dict(), 0, tick_clock()->NowTicks(),
                      ReportingTargetType::kDeveloper);
   garbage_collection_timer()->Fire();
 
@@ -72,7 +72,7 @@
 
 TEST_F(ReportingGarbageCollectorTest, ExpiredReport) {
   cache()->AddReport(std::nullopt, kNak_, kUrl_, kUserAgent_, kGroup_, kType_,
-                     base::Value::Dict(), 0, tick_clock()->NowTicks(), 0,
+                     base::Value::Dict(), 0, tick_clock()->NowTicks(),
                      ReportingTargetType::kDeveloper);
   tick_clock()->Advance(2 * policy().max_report_age);
   garbage_collection_timer()->Fire();
@@ -82,7 +82,7 @@
 
 TEST_F(ReportingGarbageCollectorTest, FailedReport) {
   cache()->AddReport(std::nullopt, kNak_, kUrl_, kUserAgent_, kGroup_, kType_,
-                     base::Value::Dict(), 0, tick_clock()->NowTicks(), 0,
+                     base::Value::Dict(), 0, tick_clock()->NowTicks(),
                      ReportingTargetType::kDeveloper);
 
   std::vector<raw_ptr<const ReportingReport, VectorExperimental>> reports;
@@ -125,7 +125,7 @@
                                    kIsolationInfo_, kUrl_);
   cache()->AddReport(kReportingSource_, kNak_, kUrl_, kUserAgent_, kGroup_,
                      kType_, base::Value::Dict(), 0, tick_clock()->NowTicks(),
-                     0, ReportingTargetType::kDeveloper);
+                     ReportingTargetType::kDeveloper);
   // Mark the source as expired. The source data should be removed as soon as
   // all reports are delivered.
   cache()->SetExpiredSource(*kReportingSource_);
diff --git a/net/reporting/reporting_network_change_observer_unittest.cc b/net/reporting/reporting_network_change_observer_unittest.cc
index d4b46e9b..20028c8 100644
--- a/net/reporting/reporting_network_change_observer_unittest.cc
+++ b/net/reporting/reporting_network_change_observer_unittest.cc
@@ -70,7 +70,7 @@
 
   cache()->AddReport(kReportingSource_, kNak_, kUrl_, kUserAgent_, kGroup_,
                      kType_, base::Value::Dict(), 0, tick_clock()->NowTicks(),
-                     0, ReportingTargetType::kDeveloper);
+                     ReportingTargetType::kDeveloper);
   SetEndpoint();
   ASSERT_EQ(1u, report_count());
   ASSERT_EQ(1u, cache()->GetEndpointCount());
@@ -89,7 +89,7 @@
 
   cache()->AddReport(kReportingSource_, kNak_, kUrl_, kUserAgent_, kGroup_,
                      kType_, base::Value::Dict(), 0, tick_clock()->NowTicks(),
-                     0, ReportingTargetType::kDeveloper);
+                     ReportingTargetType::kDeveloper);
   SetEndpoint();
   ASSERT_EQ(1u, report_count());
   ASSERT_EQ(1u, cache()->GetEndpointCount());
@@ -108,7 +108,7 @@
 
   cache()->AddReport(kReportingSource_, kNak_, kUrl_, kUserAgent_, kGroup_,
                      kType_, base::Value::Dict(), 0, tick_clock()->NowTicks(),
-                     0, ReportingTargetType::kDeveloper);
+                     ReportingTargetType::kDeveloper);
   SetEndpoint();
   ASSERT_EQ(1u, report_count());
   ASSERT_EQ(1u, cache()->GetEndpointCount());
@@ -127,7 +127,7 @@
 
   cache()->AddReport(kReportingSource_, kNak_, kUrl_, kUserAgent_, kGroup_,
                      kType_, base::Value::Dict(), 0, tick_clock()->NowTicks(),
-                     0, ReportingTargetType::kDeveloper);
+                     ReportingTargetType::kDeveloper);
   SetEndpoint();
   ASSERT_EQ(1u, report_count());
   ASSERT_EQ(1u, cache()->GetEndpointCount());
diff --git a/net/reporting/reporting_report.cc b/net/reporting/reporting_report.cc
index 40a1ba6..3d167826 100644
--- a/net/reporting/reporting_report.cc
+++ b/net/reporting/reporting_report.cc
@@ -26,7 +26,6 @@
     base::Value::Dict body,
     int depth,
     base::TimeTicks queued,
-    int attempts,
     ReportingTargetType target_type)
     : reporting_source(reporting_source),
       network_anonymization_key(network_anonymization_key),
@@ -38,7 +37,6 @@
       body(std::move(body)),
       depth(depth),
       queued(queued),
-      attempts(attempts),
       target_type(target_type) {
   // If |reporting_source| is present, it must not be empty.
   DCHECK(!(reporting_source.has_value() && reporting_source->is_empty()));
diff --git a/net/reporting/reporting_report.h b/net/reporting/reporting_report.h
index da96e566..262468c9 100644
--- a/net/reporting/reporting_report.h
+++ b/net/reporting/reporting_report.h
@@ -40,7 +40,6 @@
     SUCCESS,
   };
 
-  // TODO(chlily): Remove |attempts| argument as it is (almost?) always 0.
   ReportingReport(const std::optional<base::UnguessableToken>& reporting_source,
                   const NetworkAnonymizationKey& network_anonymization_key,
                   const GURL& url,
@@ -50,7 +49,6 @@
                   base::Value::Dict body,
                   int depth,
                   base::TimeTicks queued,
-                  int attempts,
                   ReportingTargetType target_type);
 
   // Do NOT use this constructor outside of mojo deserialization context.
diff --git a/net/reporting/reporting_service.cc b/net/reporting/reporting_service.cc
index 3adb21f..8daacdc8 100644
--- a/net/reporting/reporting_service.cc
+++ b/net/reporting/reporting_service.cc
@@ -237,10 +237,9 @@
       base::TimeTicks queued_ticks,
       ReportingTargetType target_type) {
     DCHECK(initialized_);
-    context_->cache()->AddReport(reporting_source, network_anonymization_key,
-                                 sanitized_url, user_agent, group, type,
-                                 std::move(body), depth, queued_ticks,
-                                 0 /* attempts */, target_type);
+    context_->cache()->AddReport(
+        reporting_source, network_anonymization_key, sanitized_url, user_agent,
+        group, type, std::move(body), depth, queued_ticks, target_type);
   }
 
   void DoProcessReportToHeader(
diff --git a/pdf/pdf_ink_module_unittest.cc b/pdf/pdf_ink_module_unittest.cc
index d7245b4..b2ed015 100644
--- a/pdf/pdf_ink_module_unittest.cc
+++ b/pdf/pdf_ink_module_unittest.cc
@@ -464,7 +464,7 @@
   bool UseTextAnnotations() const { return GetParam().use_text_annotations; }
   bool UseTextHighlighting() const { return GetParam().use_text_highlighting; }
 
-  void EnableAnnotationMode() {
+  void EnableDrawAnnotationMode() {
     EXPECT_TRUE(
         ink_module().OnMessage(CreateSetAnnotationModeMessageForTesting(true)));
     EXPECT_TRUE(ink_module().enabled());
@@ -490,7 +490,7 @@
 
 // Verify that a get eraser message gets the eraser parameters.
 TEST_P(PdfInkModuleTest, HandleGetAnnotationBrushMessageEraser) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
 
   EXPECT_CALL(client(), PostMessage)
       .WillOnce([](const base::Value::Dict& dict) {
@@ -510,7 +510,7 @@
 
 // Verify that a get pen message gets the pen brush parameters.
 TEST_P(PdfInkModuleTest, HandleGetAnnotationBrushMessagePen) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
 
   EXPECT_CALL(client(), PostMessage)
       .WillOnce([](const base::Value::Dict& dict) {
@@ -536,7 +536,7 @@
 
 // Verify that a get highlighter message gets the highlighter brush parameters.
 TEST_P(PdfInkModuleTest, HandleGetAnnotationBrushMessageHighlighter) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
 
   EXPECT_CALL(client(), PostMessage)
       .WillOnce([](const base::Value::Dict& dict) {
@@ -563,7 +563,7 @@
 // Verify that a get brush message without a parameter gets the default brush
 // parameters.
 TEST_P(PdfInkModuleTest, HandleGetAnnotationBrushMessageDefault) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
 
   EXPECT_CALL(client(), PostMessage)
       .WillOnce([](const base::Value::Dict& dict) {
@@ -590,7 +590,7 @@
 // Verify that a get brush message without a parameter gets the current brush
 // parameters if the brush has changed from the default brush.
 TEST_P(PdfInkModuleTest, HandleGetAnnotationBrushMessageCurrent) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
 
   // Set the brush to eraser.
   EXPECT_TRUE(ink_module().OnMessage(
@@ -615,7 +615,7 @@
 // Verify that a set eraser message sets the annotation brush to an eraser. i.e.
 // There is no `PdfInkBrush`.
 TEST_P(PdfInkModuleTest, HandleSetAnnotationBrushMessageEraser) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
 
   base::Value::Dict message =
       CreateSetAnnotationBrushMessageForTesting("eraser", nullptr);
@@ -628,7 +628,7 @@
 // Verify that a set pen message sets the annotation brush to a pen, with the
 // given params.
 TEST_P(PdfInkModuleTest, HandleSetAnnotationBrushMessagePen) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
 
   // Select the "Yellow 1" color.
   TestAnnotationBrushMessageParams message_params{kYellow,
@@ -652,7 +652,7 @@
 // Verify that a set highlighter message sets the annotation brush to a
 // highlighter, with the given params.
 TEST_P(PdfInkModuleTest, HandleSetAnnotationBrushMessageHighlighter) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
 
   // Select the "Light Yellow" color.
   TestAnnotationBrushMessageParams message_params{kYellow,
@@ -676,7 +676,7 @@
 // Verify that brushes with zero color values can be set as the annotation
 // brush.
 TEST_P(PdfInkModuleTest, HandleSetAnnotationBrushMessageColorZero) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
 
   TestAnnotationBrushMessageParams message_params{
       SkColorSetRGB(0x00, 0x00, 0x00),
@@ -780,7 +780,7 @@
         });
   }
 
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
 
   TestAnnotationBrushMessageParams message_params{
       SkColorSetRGB(0x00, 0xFF, 0x00),
@@ -819,7 +819,7 @@
         });
   }
 
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
 
   TestAnnotationBrushMessageParams message_params{
       SkColorSetRGB(0x00, 0xFF, 0x00),
@@ -833,7 +833,7 @@
 }
 
 TEST_P(PdfInkModuleTest, ContentFocusedWithMouseWillPostMessage) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
 
   blink::WebMouseEvent mouse_down_event =
       MouseEventBuilder().CreateLeftClickAtPosition(gfx::PointF()).Build();
@@ -850,7 +850,7 @@
 }
 
 TEST_P(PdfInkModuleTest, ContentFocusedWithTouchWillPostMessage) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
 
   blink::WebTouchEvent touch_start_event =
       CreateTouchEvent(blink::WebInputEvent::Type::kTouchStart,
@@ -1321,7 +1321,7 @@
 }
 
 TEST_P(PdfInkModuleStrokeTest, IgnoreTouchEventsAfterPenEvent) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
   InitializeSimpleSinglePageBasicLayout();
 
   const std::vector<base::span<const gfx::PointF>> all_move_points{
@@ -1377,7 +1377,7 @@
 }
 
 TEST_P(PdfInkModuleStrokeTest, AnnotationWithMouseInterruptedByPenEvents) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
   InitializeSimpleSinglePageBasicLayout();
 
   blink::WebMouseEvent mouse_down_event =
@@ -1425,7 +1425,7 @@
 }
 
 TEST_P(PdfInkModuleStrokeTest, AnnotationWithPenIgnoresMouseEvents) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
   InitializeSimpleSinglePageBasicLayout();
 
   blink::WebTouchEvent pen_start_event =
@@ -1529,7 +1529,7 @@
 }
 
 TEST_P(PdfInkModuleStrokeTest, StrokeOutsidePage) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
   InitializeVerticalTwoPageLayout();
 
   // Start out without any strokes.
@@ -1546,7 +1546,7 @@
 }
 
 TEST_P(PdfInkModuleStrokeTest, StrokeInsidePages) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
   InitializeVerticalTwoPageLayout();
 
   // Start out without any strokes.
@@ -1571,7 +1571,7 @@
 }
 
 TEST_P(PdfInkModuleStrokeTest, StrokeAcrossPages) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
   InitializeVerticalTwoPageLayout();
 
   // Start out without any strokes.
@@ -1588,7 +1588,7 @@
 }
 
 TEST_P(PdfInkModuleStrokeTest, StrokePageExitAndReentry) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
   InitializeVerticalTwoPageLayout();
 
   // Start out without any strokes.
@@ -1609,7 +1609,7 @@
 }
 
 TEST_P(PdfInkModuleStrokeTest, StrokePageExitAndReentryWithQuickMoves) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
   InitializeVerticalTwoPageLayout();
 
   // Start out without any strokes.
@@ -1676,7 +1676,7 @@
 }
 
 TEST_P(PdfInkModuleStrokeTest, EraseOnPageWithoutStrokes) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
   InitializeSimpleSinglePageBasicLayout();
 
   // Verify there are no visible strokes to start with.
@@ -1784,7 +1784,7 @@
 }
 
 TEST_P(PdfInkModuleStrokeTest, EraseStrokesAcrossTwoPages) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
   InitializeVerticalTwoPageLayout();
 
   // Start out without any strokes.
@@ -1838,7 +1838,7 @@
 }
 
 TEST_P(PdfInkModuleStrokeTest, EraseStrokePageExitAndReentry) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
   InitializeVerticalTwoPageLayout();
 
   // Start out without any strokes.
@@ -2000,7 +2000,7 @@
 }
 
 TEST_P(PdfInkModuleStrokeTest, StrokeMissedEndEventThenMouseDown) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
   InitializeSimpleSinglePageBasicLayout();
 
   blink::WebMouseEvent mouse_down_event =
@@ -2017,7 +2017,7 @@
 }
 
 TEST_P(PdfInkModuleStrokeTest, StrokeMissedEndEventThenMouseMoveDuringDrawing) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
   InitializeSimpleSinglePageBasicLayout();
 
   // No need to distinguish between pen or highlighter here.
@@ -2028,7 +2028,7 @@
 }
 
 TEST_P(PdfInkModuleStrokeTest, StrokeMissedEndEventThenMouseMoveDuringErasing) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
   InitializeSimpleSinglePageBasicLayout();
 
   SelectEraserTool();
@@ -2037,7 +2037,7 @@
 }
 
 TEST_P(PdfInkModuleStrokeTest, ChangeBrushColorDuringDrawing) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
   InitializeSimpleSinglePageBasicLayout();
 
   // Start drawing a stroke with a black pen.  The stroke will not finish
@@ -2088,7 +2088,7 @@
 }
 
 TEST_P(PdfInkModuleStrokeTest, ChangeBrushSizeDuringDrawing) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
   InitializeSimpleSinglePageBasicLayout();
 
   // Start drawing a stroke with a black pen.  The stroke will not finish
@@ -2192,7 +2192,7 @@
 }
 
 TEST_P(PdfInkModuleStrokeTest, ChangeToDrawingDuringErasing) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
   InitializeSimpleSinglePageBasicLayout();
 
   // Initialize to have two strokes, so there is something to erase.
@@ -2256,7 +2256,7 @@
 }
 
 TEST_P(PdfInkModuleStrokeTest, ChangeDrawingBrushTypeDuringDrawing) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
   InitializeSimpleSinglePageBasicLayout();
 
   // Start drawing a stroke with a black pen.  The stroke will not finish
@@ -2333,7 +2333,7 @@
 
 TEST_P(PdfInkModuleUndoRedoTest, UndoRedoEmpty) {
   InitializeSimpleSinglePageBasicLayout();
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
 
   EXPECT_TRUE(StrokeInputPositions().empty());
   EXPECT_TRUE(VisibleStrokeInputPositions().empty());
@@ -2642,7 +2642,7 @@
 }
 
 TEST_P(PdfInkModuleUndoRedoTest, UndoRedoOnTwoPages) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
   InitializeVerticalTwoPageLayout();
 
   ApplyStrokeWithMouseAtPoints(
@@ -2728,7 +2728,7 @@
   EXPECT_CALL(client(), UpdateShapeActive(_, _, _)).Times(0);
 
   InitializeSimpleSinglePageBasicLayout();
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
   EXPECT_TRUE(updated_ink_thumbnail_page_indices().empty());
   EXPECT_TRUE(updated_pdf_thumbnail_page_indices().empty());
 
@@ -2847,7 +2847,7 @@
 }
 
 TEST_P(PdfInkModuleGetVisibleStrokesTest, MultiplePageStrokes) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
   InitializeVerticalTwoPageLayout();
 
   // Multiple strokes on one page, plus a stroke on another page.
@@ -2958,7 +2958,7 @@
 }
 
 TEST_P(PdfInkModuleMetricsTest, StrokeBrushColorHighlighter) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
   InitializeSimpleSinglePageBasicLayout();
 
   // Draw a stroke with "Light Red" color.
@@ -3010,7 +3010,7 @@
 }
 
 TEST_P(PdfInkModuleMetricsTest, StrokeBrushSizeHighlighter) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
   InitializeSimpleSinglePageBasicLayout();
 
   // Draw a stroke with medium size.
@@ -3227,7 +3227,7 @@
   // Sets up single selection test expectations before text selection strokes
   // have been applied.
   void SetUpSingleSelectionTest(const gfx::Rect& selection_rect) {
-    EnableAnnotationMode();
+    EnableDrawAnnotationMode();
     InitializeSimpleSinglePageBasicLayout();
 
     SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
@@ -3295,7 +3295,7 @@
 };
 
 TEST_P(PdfInkModuleTextHighlightTest, PenDoesNotSelectText) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
   InitializeSimpleSinglePageBasicLayout();
 
   // Select the pen tool with a "Light Red" color.
@@ -3427,7 +3427,7 @@
 }
 
 TEST_P(PdfInkModuleTextHighlightTest, SingleSelectionZoomedIn) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
   InitializeSimpleSinglePageBasicLayout();
   client().set_zoom(2.0f);
 
@@ -3440,7 +3440,7 @@
 }
 
 TEST_P(PdfInkModuleTextHighlightTest, SingleSelectionZoomedOut) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
   InitializeSimpleSinglePageBasicLayout();
   client().set_zoom(0.5f);
 
@@ -3453,7 +3453,7 @@
 }
 
 TEST_P(PdfInkModuleTextHighlightTest, MultipleSelection) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
   InitializeSimpleSinglePageBasicLayout();
 
   SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
@@ -3512,7 +3512,7 @@
 }
 
 TEST_P(PdfInkModuleTextHighlightTest, OneClickCount) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
   InitializeSimpleSinglePageBasicLayout();
 
   SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
@@ -3538,7 +3538,7 @@
 }
 
 TEST_P(PdfInkModuleTextHighlightTest, TwoClickCount) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
   InitializeSimpleSinglePageBasicLayout();
 
   SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
@@ -3596,7 +3596,7 @@
 }
 
 TEST_P(PdfInkModuleTextHighlightTest, ThreeClickCount) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
   InitializeSimpleSinglePageBasicLayout();
 
   SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
@@ -3663,7 +3663,7 @@
 }
 
 TEST_P(PdfInkModuleTextHighlightTest, MouseUpOnNonSelection) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
   InitializeSimpleSinglePageBasicLayout();
 
   SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
@@ -3720,7 +3720,7 @@
 }
 
 TEST_P(PdfInkModuleTextHighlightTest, MultiplePages) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
   InitializeVerticalTwoPageLayout();
 
   SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
@@ -3795,7 +3795,7 @@
 
 TEST_P(PdfInkModuleTextHighlightTest,
        StrokeMissedEndEventThenMouseMoveDuringTextSelecting) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
   InitializeSimpleSinglePageBasicLayout();
 
   SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
@@ -3826,7 +3826,7 @@
 }
 
 TEST_P(PdfInkModuleTextHighlightTest, TouchOneClickCount) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
   InitializeSimpleSinglePageBasicLayout();
 
   SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
@@ -3859,7 +3859,7 @@
 }
 
 TEST_P(PdfInkModuleTextHighlightTest, MultiTouchDoesNotSelectText) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
   InitializeSimpleSinglePageBasicLayout();
 
   SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
@@ -3887,7 +3887,7 @@
 }
 
 TEST_P(PdfInkModuleTextHighlightTest, PenOneClickCount) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
   InitializeSimpleSinglePageBasicLayout();
 
   SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
@@ -3920,7 +3920,7 @@
 }
 
 TEST_P(PdfInkModuleTextHighlightTest, CursorOnMouseMove) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
   InitializeSimpleSinglePageBasicLayout();
 
   // Select the pen tool with a "Light Red" color. The cursor should be the
@@ -3972,7 +3972,7 @@
 }
 
 TEST_P(PdfInkModuleTextHighlightTest, CursorOnMouseMoveWhileTextSelecting) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
   InitializeSimpleSinglePageBasicLayout();
 
   // Select the highlighter tool. The cursor should be the custom highlighter
@@ -4020,7 +4020,7 @@
 }
 
 TEST_P(PdfInkModuleTextHighlightTest, CursorOnMouseMoveWhileBrushDrawing) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
   InitializeSimpleSinglePageBasicLayout();
 
   // Select the highlighter tool. The cursor should be the custom highlighter
@@ -4103,7 +4103,7 @@
 
 TEST_P(PdfInkModuleTextHighlightMetricsTest,
        StrokeDoesNotAffectTextHighlightMetrics) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
   InitializeSimpleSinglePageBasicLayout();
 
   SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
@@ -4132,7 +4132,7 @@
 
 TEST_P(PdfInkModuleTextHighlightMetricsTest,
        TextHighlightUndoRedoDoesNotAffectMetrics) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
   InitializeSimpleSinglePageBasicLayout();
 
   RunSingleSelectionWithMouseTest(
@@ -4157,7 +4157,7 @@
 }
 
 TEST_P(PdfInkModuleTextHighlightMetricsTest, Color) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
   InitializeSimpleSinglePageBasicLayout();
 
   SelectBrushTool(PdfInkBrush::Type::kHighlighter, kRedBrushParams);
@@ -4186,7 +4186,7 @@
 }
 
 TEST_P(PdfInkModuleTextHighlightMetricsTest, InputDevice) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
   InitializeSimpleSinglePageBasicLayout();
 
   SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
@@ -4229,7 +4229,7 @@
 }
 
 TEST_P(PdfInkModuleTextHighlightMetricsTest, TwoClickDelay) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
   InitializeSimpleSinglePageBasicLayout();
 
   SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
@@ -4263,7 +4263,7 @@
 }
 
 TEST_P(PdfInkModuleTextHighlightMetricsTest, TwoClickMove) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
   InitializeSimpleSinglePageBasicLayout();
 
   SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
@@ -4312,7 +4312,7 @@
 }
 
 TEST_P(PdfInkModuleTextHighlightMetricsTest, TwoClickMoveHighlight) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
   InitializeSimpleSinglePageBasicLayout();
 
   SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
@@ -4353,7 +4353,7 @@
 }
 
 TEST_P(PdfInkModuleTextHighlightMetricsTest, ThreeClickDelay) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
   InitializeSimpleSinglePageBasicLayout();
 
   SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
@@ -4395,7 +4395,7 @@
 }
 
 TEST_P(PdfInkModuleTextHighlightMetricsTest, ThreeClickMove) {
-  EnableAnnotationMode();
+  EnableDrawAnnotationMode();
   InitializeSimpleSinglePageBasicLayout();
 
   SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
diff --git a/sandbox/mac/BUILD.gn b/sandbox/mac/BUILD.gn
index 453e218..7a69c5d3 100644
--- a/sandbox/mac/BUILD.gn
+++ b/sandbox/mac/BUILD.gn
@@ -79,3 +79,14 @@
     "//testing/gtest",
   ]
 }
+
+test("sandbox_mac_fuzztests") {
+  sources = [ "sandbox_serializer_fuzztest.cc" ]
+
+  fuzztests = [ "SandboxSerializerFuzzTest.CanDeserializeWithoutCrashing" ]
+
+  deps = [
+    ":seatbelt",
+    "//third_party/fuzztest:fuzztest_gtest_main",
+  ]
+}
diff --git a/sandbox/mac/DEPS b/sandbox/mac/DEPS
index db98e70..673f469 100644
--- a/sandbox/mac/DEPS
+++ b/sandbox/mac/DEPS
@@ -19,3 +19,7 @@
     "+mojo/core",
   ],
 }
+
+include_rules = [
+  "+third_party/fuzztest",
+]
diff --git a/sandbox/mac/sandbox_serializer.cc b/sandbox/mac/sandbox_serializer.cc
index 899f231..ea1627bd 100644
--- a/sandbox/mac/sandbox_serializer.cc
+++ b/sandbox/mac/sandbox_serializer.cc
@@ -4,6 +4,7 @@
 
 #include "sandbox/mac/sandbox_serializer.h"
 
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -80,6 +81,11 @@
 
 SandboxSerializer::~SandboxSerializer() = default;
 
+SandboxSerializer::DeserializedPolicy::DeserializedPolicy() = default;
+SandboxSerializer::DeserializedPolicy::DeserializedPolicy(
+    DeserializedPolicy&& other) = default;
+SandboxSerializer::DeserializedPolicy::~DeserializedPolicy() = default;
+
 void SandboxSerializer::SetProfile(const std::string& profile) {
   profile_ = profile;
 }
@@ -138,68 +144,88 @@
 }
 
 // static
+std::optional<SandboxSerializer::DeserializedPolicy>
+SandboxSerializer::DeserializePolicy(const std::string& serialized_policy,
+                                     std::string& error) {
+  std::string_view remaining_serialized_policy = serialized_policy;
+  uint64_t mode;
+  if (!DecodeVarInt(&remaining_serialized_policy, &mode)) {
+    error = "unexpected serialized policy mode";
+    return std::nullopt;
+  }
+  if (mode > static_cast<uint64_t>(Target::kMaxValue)) {
+    error = "unexpected policy mode";
+    return std::nullopt;
+  }
+  DeserializedPolicy deserialized_policy;
+  deserialized_policy.mode = static_cast<Target>(mode);
+  switch (deserialized_policy.mode) {
+    case Target::kCompiled:
+      if (!DecodeString(&remaining_serialized_policy,
+                        &(deserialized_policy.profile))) {
+        error = "could not decode compiled policy string";
+        return std::nullopt;
+      }
+      break;
+    case Target::kSource:
+      std::string profile;
+      if (!DecodeString(&remaining_serialized_policy,
+                        &(deserialized_policy.profile))) {
+        error = "could not decode source mode profile string";
+        return std::nullopt;
+      }
+      while (!remaining_serialized_policy.empty()) {
+        std::string key;
+        if (!DecodeString(&remaining_serialized_policy, &key)) {
+          error = "could not decode source mode parameter key";
+          return std::nullopt;
+        }
+        deserialized_policy.params.push_back(key);
+
+        std::string value;
+        if (!DecodeString(&remaining_serialized_policy, &value)) {
+          error = "could not decode source mode parameter value";
+          return std::nullopt;
+        }
+        deserialized_policy.params.push_back(value);
+      }
+      break;
+  }
+  return deserialized_policy;
+}
+
+// static
 bool SandboxSerializer::ApplySerializedPolicy(
     const std::string& serialized_policy) {
-  std::string_view policy = serialized_policy;
-  uint64_t mode;
-  if (!DecodeVarInt(&policy, &mode)) {
-    logging::Error(
-        "SandboxSerializer: unexpected serialized policy mode bytes");
-    return false;
-  }
-  if (mode == static_cast<int>(Target::kCompiled)) {
-    std::string compiled;
-    if (!DecodeString(&policy, &compiled)) {
-      logging::Error(
-          "SandboxSerializer: could not decode compiled policy string");
-      return false;
-    }
-    std::string error;
-    if (!Seatbelt::ApplyCompiledProfile(compiled, &error)) {
-      logging::Error("SandboxSerializer: Failed to apply compiled policy: %s",
-                     error.c_str());
-      return false;
-    }
-    return true;
-  }
-
-  if (mode != static_cast<int>(Target::kSource)) {
-    logging::Error("SandboxSerializer: got unexpected policy mode source");
-    return false;
-  }
-
-  std::string profile;
-  if (!DecodeString(&policy, &profile)) {
-    logging::Error(
-        "SandboxSerializer: could not decode source mode profile string");
-    return false;
-  }
-  std::vector<std::string> params;
-  while (!policy.empty()) {
-    std::string key, value;
-    if (!DecodeString(&policy, &key)) {
-      logging::Error(
-          "SandboxSerializer: could not decode source mode parameter key");
-      return false;
-    }
-    if (!DecodeString(&policy, &value)) {
-      logging::Error(
-          "SandboxSerializer: could not decode source mode parameter value");
-      return false;
-    }
-
-    params.push_back(key);
-    params.push_back(value);
-  }
-
   std::string error;
-  if (!Seatbelt::InitWithParams(profile, 0, params, &error)) {
-    logging::Error(
-        "SandboxSerializer: Failed to initialize sandbox with params: %s",
-        error.c_str());
+  std::optional<DeserializedPolicy> deserialized_policy =
+      DeserializePolicy(serialized_policy, error);
+  if (!deserialized_policy) {
+    logging::Error("SandboxSerializer: Failed to deserialize policy: %s",
+                   error.c_str());
     return false;
   }
 
+  switch (deserialized_policy->mode) {
+    case Target::kCompiled:
+      if (!Seatbelt::ApplyCompiledProfile(deserialized_policy->profile,
+                                          &error)) {
+        logging::Error("SandboxSerializer: Failed to apply compiled policy: %s",
+                       error.c_str());
+        return false;
+      }
+      break;
+    case Target::kSource:
+      if (!Seatbelt::InitWithParams(deserialized_policy->profile, 0,
+                                    deserialized_policy->params, &error)) {
+        logging::Error(
+            "SandboxSerializer: Failed to initialize sandbox with source mode "
+            "policy: %s",
+            error.c_str());
+        return false;
+      }
+      break;
+  }
   return true;
 }
 
diff --git a/sandbox/mac/sandbox_serializer.h b/sandbox/mac/sandbox_serializer.h
index bc440600..3dc46cd 100644
--- a/sandbox/mac/sandbox_serializer.h
+++ b/sandbox/mac/sandbox_serializer.h
@@ -6,7 +6,9 @@
 #define SANDBOX_MAC_SANDBOX_SERIALIZER_H_
 
 #include <map>
+#include <optional>
 #include <string>
+#include <vector>
 
 #include "sandbox/mac/seatbelt.h"
 #include "sandbox/mac/seatbelt_export.h"
@@ -26,6 +28,20 @@
     // The result of serialization is a string containing a sealed,
     // compiled, binary sandbox policy that can be applied immediately.
     kCompiled,
+    kMaxValue = kCompiled
+  };
+
+  struct DeserializedPolicy {
+    DeserializedPolicy();
+    DeserializedPolicy(DeserializedPolicy&& other);
+    DeserializedPolicy& operator=(DeserializedPolicy&& other) = default;
+    ~DeserializedPolicy();
+    DeserializedPolicy(const DeserializedPolicy& other) = delete;
+    DeserializedPolicy& operator=(const DeserializedPolicy& other) = delete;
+
+    Target mode;
+    std::string profile;
+    std::vector<std::string> params;
   };
 
   // Creates a serializer with the specified target mode.
@@ -55,6 +71,12 @@
   [[nodiscard]] bool SerializePolicy(std::string& serialized_policy,
                                      std::string& error);
 
+  // Attempts to deserialize `serialized_policy` and returns the policy if
+  // deserialization is successful, or `std::nullopt` if it fails, with a
+  // description of the failure in `error`.
+  [[nodiscard]] static std::optional<SandboxSerializer::DeserializedPolicy>
+  DeserializePolicy(const std::string& serialized_policy, std::string& error);
+
   // Applies the given sandbox policy, and returns whether or not the operation
   // succeeds.
   [[nodiscard]] static bool ApplySerializedPolicy(
diff --git a/sandbox/mac/sandbox_serializer_fuzztest.cc b/sandbox/mac/sandbox_serializer_fuzztest.cc
new file mode 100644
index 0000000..996eb5a5
--- /dev/null
+++ b/sandbox/mac/sandbox_serializer_fuzztest.cc
@@ -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.
+
+#include "sandbox/mac/sandbox_serializer.h"
+#include "third_party/fuzztest/src/fuzztest/fuzztest.h"
+
+namespace sandbox {
+
+static void CanDeserializeWithoutCrashing(std::string input) {
+  std::string error;
+  {
+    SandboxSerializer serializer(SandboxSerializer::Target::kSource);
+    std::ignore = serializer.DeserializePolicy(input, error);
+  }
+  {
+    SandboxSerializer serializer(SandboxSerializer::Target::kCompiled);
+    std::ignore = serializer.DeserializePolicy(input, error);
+  }
+}
+
+FUZZ_TEST(SandboxSerializerFuzzTest, CanDeserializeWithoutCrashing);
+
+}  // namespace sandbox
diff --git a/services/network/connection_change_observer.cc b/services/network/connection_change_observer.cc
index 2c0fd34..0573a6d1 100644
--- a/services/network/connection_change_observer.cc
+++ b/services/network/connection_change_observer.cc
@@ -6,12 +6,13 @@
 #include "base/functional/bind.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "net/base/reconnect_notifier.h"
-#include "services/network/public/mojom/reconnect_event_observer.mojom.h"
+#include "services/network/public/mojom/connection_change_observer_client.mojom.h"
 
 namespace network {
 
 ConnectionChangeObserver::ConnectionChangeObserver(
-    mojo::PendingRemote<network::mojom::ReconnectEventObserver> observer,
+    mojo::PendingRemote<network::mojom::ConnectionChangeObserverClient>
+        observer,
     raw_ptr<NetworkContext> network_context)
     : network_context_(std::move(network_context)) {
   observer_.Bind(std::move(observer));
diff --git a/services/network/connection_change_observer.h b/services/network/connection_change_observer.h
index 256f333..e6971ac 100644
--- a/services/network/connection_change_observer.h
+++ b/services/network/connection_change_observer.h
@@ -10,7 +10,7 @@
 #include "mojo/public/cpp/bindings/remote.h"
 #include "net/base/reconnect_notifier.h"
 #include "services/network/network_context.h"
-#include "services/network/public/mojom/reconnect_event_observer.mojom.h"
+#include "services/network/public/mojom/connection_change_observer_client.mojom.h"
 
 namespace network {
 
@@ -18,7 +18,8 @@
     : public net::ConnectionChangeNotifier::Observer {
  public:
   ConnectionChangeObserver(
-      mojo::PendingRemote<network::mojom::ReconnectEventObserver> observer,
+      mojo::PendingRemote<network::mojom::ConnectionChangeObserverClient>
+          observer,
       raw_ptr<NetworkContext> network_context);
   ~ConnectionChangeObserver() override;
 
@@ -31,7 +32,7 @@
   // called when the underlying mojo pipe has been disconnected.
   void OnDisconnectEvent();
 
-  mojo::Remote<network::mojom::ReconnectEventObserver> observer_;
+  mojo::Remote<network::mojom::ConnectionChangeObserverClient> observer_;
 
   raw_ptr<NetworkContext> network_context_;
 };
diff --git a/services/network/connection_change_observer_unittest.cc b/services/network/connection_change_observer_unittest.cc
index b3ee984..2b6f611 100644
--- a/services/network/connection_change_observer_unittest.cc
+++ b/services/network/connection_change_observer_unittest.cc
@@ -15,8 +15,7 @@
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "net/base/reconnect_notifier.h"
-#include "services/network/public/mojom/reconnect_event_observer.mojom-shared.h"
-#include "services/network/public/mojom/reconnect_event_observer.mojom.h"
+#include "services/network/public/mojom/connection_change_observer_client.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace network {
@@ -38,15 +37,17 @@
 };
 }  // namespace
 
-class TestReconnectEventObserver : public mojom::ReconnectEventObserver {
+class TestConnectionChangeObserverClient
+    : public mojom::ConnectionChangeObserverClient {
  public:
-  explicit TestReconnectEventObserver() = default;
+  explicit TestConnectionChangeObserverClient() = default;
 
-  TestReconnectEventObserver(const TestReconnectEventObserver&) = delete;
-  TestReconnectEventObserver& operator=(const TestReconnectEventObserver&) =
-      delete;
+  TestConnectionChangeObserverClient(
+      const TestConnectionChangeObserverClient&) = delete;
+  TestConnectionChangeObserverClient& operator=(
+      const TestConnectionChangeObserverClient&) = delete;
 
-  ~TestReconnectEventObserver() override = default;
+  ~TestConnectionChangeObserverClient() override = default;
 
   void OnSessionClosed() override {
     session_closed_++;
@@ -79,11 +80,12 @@
     run_loop_ = std::make_unique<base::RunLoop>();
   }
 
-  mojo::PendingRemote<mojom::ReconnectEventObserver> GetPendingRemote() {
+  mojo::PendingRemote<mojom::ConnectionChangeObserverClient>
+  GetPendingRemote() {
     CHECK(!receiver_.is_bound());
     auto remote = receiver_.BindNewPipeAndPassRemote();
     receiver_.set_disconnect_handler(
-        base::BindOnce(&TestReconnectEventObserver::OnPipeDisconnected,
+        base::BindOnce(&TestConnectionChangeObserverClient::OnPipeDisconnected,
                        base::Unretained(this)));
     return remote;
   }
@@ -115,15 +117,15 @@
   NotificationType waiting_notification_type_;
   std::optional<net::NetworkChangeEvent> last_network_event_ = std::nullopt;
   std::unique_ptr<base::RunLoop> run_loop_ = std::make_unique<base::RunLoop>();
-  mojo::Receiver<mojom::ReconnectEventObserver> receiver_{this};
+  mojo::Receiver<mojom::ConnectionChangeObserverClient> receiver_{this};
 };
 
 class ConnectionChangeObserverTest : public testing::Test {
  public:
   ConnectionChangeObserverTest()
-      : reconnect_event_observer_(
-            std::make_unique<TestReconnectEventObserver>()) {
-    auto remote = reconnect_event_observer_->GetPendingRemote();
+      : connection_change_observer_client_(
+            std::make_unique<TestConnectionChangeObserverClient>()) {
+    auto remote = connection_change_observer_client_->GetPendingRemote();
     connection_change_observer_ =
         std::make_unique<ConnectionChangeObserver>(std::move(remote), nullptr);
   }
@@ -139,8 +141,8 @@
     connection_change_observer_.reset();
   }
 
-  TestReconnectEventObserver* reconnect_event_observer() {
-    return reconnect_event_observer_.get();
+  TestConnectionChangeObserverClient* connection_change_observer_client() {
+    return connection_change_observer_client_.get();
   }
 
   ConnectionChangeObserver* connection_change_observer() const {
@@ -151,30 +153,31 @@
 
  private:
   base::test::TaskEnvironment task_environment_;
-  std::unique_ptr<TestReconnectEventObserver> reconnect_event_observer_;
+  std::unique_ptr<TestConnectionChangeObserverClient>
+      connection_change_observer_client_;
   std::unique_ptr<ConnectionChangeObserver> connection_change_observer_;
 };
 
 TEST_F(ConnectionChangeObserverTest, OberverNotifiedOnSessionClosed) {
   connection_change_observer()->OnSessionClosed();
-  reconnect_event_observer()->WaitForNotification(
+  connection_change_observer_client()->WaitForNotification(
       NotificationType::kSessionClosed);
-  EXPECT_EQ(1, reconnect_event_observer()->session_closed());
+  EXPECT_EQ(1, connection_change_observer_client()->session_closed());
 }
 
 TEST_F(ConnectionChangeObserverTest, OberverNotifiedOnConnectionFailed) {
   connection_change_observer()->OnConnectionFailed();
-  reconnect_event_observer()->WaitForNotification(
+  connection_change_observer_client()->WaitForNotification(
       NotificationType::kConnectionFailed);
-  EXPECT_EQ(1, reconnect_event_observer()->connection_failed());
+  EXPECT_EQ(1, connection_change_observer_client()->connection_failed());
 }
 
 TEST_F(ConnectionChangeObserverTest, OberverNotifiedOnNetworkEvent) {
   connection_change_observer()->OnNetworkEvent(
       net::NetworkChangeEvent::kConnected);
-  reconnect_event_observer()->WaitForNotification(
+  connection_change_observer_client()->WaitForNotification(
       NotificationType::kNetworkChanged);
-  EXPECT_EQ(1, reconnect_event_observer()->network_event());
+  EXPECT_EQ(1, connection_change_observer_client()->network_event());
 }
 
 TEST_F(ConnectionChangeObserverTest, OberverNotifiedOnNetworkEventTypes) {
@@ -183,9 +186,10 @@
        typeInt++) {
     auto event = static_cast<net::NetworkChangeEvent>(typeInt);
     connection_change_observer()->OnNetworkEvent(event);
-    reconnect_event_observer()->WaitForNotification(
+    connection_change_observer_client()->WaitForNotification(
         NotificationType::kNetworkChanged);
-    auto network_event = reconnect_event_observer()->last_network_event();
+    auto network_event =
+        connection_change_observer_client()->last_network_event();
     ASSERT_TRUE(network_event.has_value());
     EXPECT_EQ(event, network_event.value());
   }
@@ -194,9 +198,9 @@
 TEST_F(ConnectionChangeObserverTest, NotifierDestructed) {
   // Manually remove the observer
   RemoveConnectionChangeObserver();
-  reconnect_event_observer()->WaitForNotification(
+  connection_change_observer_client()->WaitForNotification(
       NotificationType::kPipeDisconnected);
-  EXPECT_TRUE(reconnect_event_observer()->pipe_disconnected());
+  EXPECT_TRUE(connection_change_observer_client()->pipe_disconnected());
 }
 
 class ConnectionChangeObserverWithNotifierTest
@@ -225,25 +229,25 @@
 TEST_F(ConnectionChangeObserverWithNotifierTest,
        OberverNotifiedOnSessionClosed) {
   notifier()->OnSessionClosed();
-  reconnect_event_observer()->WaitForNotification(
+  connection_change_observer_client()->WaitForNotification(
       NotificationType::kSessionClosed);
-  EXPECT_EQ(1, reconnect_event_observer()->session_closed());
+  EXPECT_EQ(1, connection_change_observer_client()->session_closed());
 }
 
 TEST_F(ConnectionChangeObserverWithNotifierTest,
        OberverNotifiedOnConnectionFailed) {
   notifier()->OnConnectionFailed();
-  reconnect_event_observer()->WaitForNotification(
+  connection_change_observer_client()->WaitForNotification(
       NotificationType::kConnectionFailed);
-  EXPECT_EQ(1, reconnect_event_observer()->connection_failed());
+  EXPECT_EQ(1, connection_change_observer_client()->connection_failed());
 }
 
 TEST_F(ConnectionChangeObserverWithNotifierTest,
        OberverNotifiedOnNetworkEvent) {
   notifier()->OnNetworkEvent(net::NetworkChangeEvent::kConnected);
-  reconnect_event_observer()->WaitForNotification(
+  connection_change_observer_client()->WaitForNotification(
       NotificationType::kNetworkChanged);
-  EXPECT_EQ(1, reconnect_event_observer()->network_event());
+  EXPECT_EQ(1, connection_change_observer_client()->network_event());
 }
 
 TEST_F(ConnectionChangeObserverWithNotifierTest,
@@ -253,9 +257,10 @@
        typeInt++) {
     auto event = static_cast<net::NetworkChangeEvent>(typeInt);
     notifier()->OnNetworkEvent(event);
-    reconnect_event_observer()->WaitForNotification(
+    connection_change_observer_client()->WaitForNotification(
         NotificationType::kNetworkChanged);
-    auto network_event = reconnect_event_observer()->last_network_event();
+    auto network_event =
+        connection_change_observer_client()->last_network_event();
     ASSERT_TRUE(network_event.has_value());
     EXPECT_EQ(event, network_event.value());
   }
diff --git a/services/network/network_context.cc b/services/network/network_context.cc
index 0339a37..65c057d 100644
--- a/services/network/network_context.cc
+++ b/services/network/network_context.cc
@@ -132,9 +132,9 @@
 #include "services/network/public/cpp/resource_request.h"
 #include "services/network/public/cpp/simple_host_resolver.h"
 #include "services/network/public/mojom/clear_data_filter.mojom.h"
+#include "services/network/public/mojom/connection_change_observer_client.mojom-forward.h"
 #include "services/network/public/mojom/cookie_encryption_provider.mojom.h"
 #include "services/network/public/mojom/network_context.mojom.h"
-#include "services/network/public/mojom/reconnect_event_observer.mojom-forward.h"
 #include "services/network/public/mojom/reporting_service.mojom.h"
 #include "services/network/public/mojom/trust_tokens.mojom-forward.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
@@ -2257,8 +2257,8 @@
     const net::NetworkAnonymizationKey& network_anonymization_key,
     const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
     const std::optional<net::ConnectionKeepAliveConfig>& keepalive_config,
-    mojo::PendingRemote<mojom::ReconnectEventObserver>
-        reconnect_event_observer) {
+    mojo::PendingRemote<mojom::ConnectionChangeObserverClient>
+        connection_change_observer_client) {
   DCHECK(!require_network_anonymization_key_ ||
          !network_anonymization_key.IsEmpty());
 
@@ -2289,14 +2289,15 @@
                                        user_agent);
   request_info.traffic_annotation = traffic_annotation;
 
-  if (keepalive_config.has_value() || reconnect_event_observer.is_valid()) {
+  if (keepalive_config.has_value() ||
+      connection_change_observer_client.is_valid()) {
     request_info.connection_management_config =
         net::ConnectionManagementConfig();
     request_info.connection_management_config->keep_alive_config =
         keepalive_config;
-    if (reconnect_event_observer.is_valid()) {
+    if (connection_change_observer_client.is_valid()) {
       auto change_observer = std::make_unique<ConnectionChangeObserver>(
-          std::move(reconnect_event_observer), this);
+          std::move(connection_change_observer_client), this);
 
       request_info.connection_management_config->connection_change_observer =
           change_observer.get();
diff --git a/services/network/network_context.h b/services/network/network_context.h
index 867d300..2f8fe079 100644
--- a/services/network/network_context.h
+++ b/services/network/network_context.h
@@ -57,6 +57,7 @@
 #include "services/network/public/cpp/network_service_buildflags.h"
 #include "services/network/public/cpp/transferable_directory.h"
 #include "services/network/public/mojom/clear_data_filter.mojom-forward.h"
+#include "services/network/public/mojom/connection_change_observer_client.mojom.h"
 #include "services/network/public/mojom/cookie_access_observer.mojom.h"
 #include "services/network/public/mojom/cookie_manager.mojom-shared.h"
 #include "services/network/public/mojom/host_resolver.mojom.h"
@@ -66,7 +67,6 @@
 #include "services/network/public/mojom/network_service.mojom-forward.h"
 #include "services/network/public/mojom/proxy_lookup_client.mojom.h"
 #include "services/network/public/mojom/proxy_resolving_socket.mojom.h"
-#include "services/network/public/mojom/reconnect_event_observer.mojom.h"
 #include "services/network/public/mojom/restricted_cookie_manager.mojom.h"
 #include "services/network/public/mojom/restricted_udp_socket.mojom.h"
 #include "services/network/public/mojom/tcp_socket.mojom.h"
@@ -458,8 +458,8 @@
       const net::NetworkAnonymizationKey& network_anonymization_key,
       const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
       const std::optional<net::ConnectionKeepAliveConfig>& keepalive_config,
-      mojo::PendingRemote<mojom::ReconnectEventObserver>
-          reconnect_event_observer) override;
+      mojo::PendingRemote<mojom::ConnectionChangeObserverClient>
+          connection_change_observer_client) override;
 #if BUILDFLAG(IS_P2P_ENABLED)
   void CreateP2PSocketManager(
       const net::NetworkAnonymizationKey& network_anonymization_key,
diff --git a/services/network/network_context_unittest.cc b/services/network/network_context_unittest.cc
index c4484bd..6e7766a 100644
--- a/services/network/network_context_unittest.cc
+++ b/services/network/network_context_unittest.cc
@@ -160,6 +160,7 @@
 #include "services/network/public/cpp/resolve_host_client_base.h"
 #include "services/network/public/cpp/resource_request.h"
 #include "services/network/public/mojom/clear_data_filter.mojom.h"
+#include "services/network/public/mojom/connection_change_observer_client.mojom.h"
 #include "services/network/public/mojom/cookie_manager.mojom.h"
 #include "services/network/public/mojom/fetch_api.mojom-shared.h"
 #include "services/network/public/mojom/host_resolver.mojom.h"
@@ -167,7 +168,6 @@
 #include "services/network/public/mojom/network_context.mojom.h"
 #include "services/network/public/mojom/network_service.mojom.h"
 #include "services/network/public/mojom/proxy_config.mojom.h"
-#include "services/network/public/mojom/reconnect_event_observer.mojom.h"
 #include "services/network/public/mojom/url_loader.mojom-shared.h"
 #include "services/network/public/mojom/url_loader_factory.mojom-forward.h"
 #include "services/network/test/fake_test_cert_verifier_params_factory.h"
diff --git a/services/network/public/cpp/reconnect_event_observer_mojom_traits.cc b/services/network/public/cpp/connection_change_observer_client_mojom_traits.cc
similarity index 93%
rename from services/network/public/cpp/reconnect_event_observer_mojom_traits.cc
rename to services/network/public/cpp/connection_change_observer_client_mojom_traits.cc
index 5eacacf..3222cf9 100644
--- a/services/network/public/cpp/reconnect_event_observer_mojom_traits.cc
+++ b/services/network/public/cpp/connection_change_observer_client_mojom_traits.cc
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "services/network/public/cpp/reconnect_event_observer_mojom_traits.h"
+#include "services/network/public/cpp/connection_change_observer_client_mojom_traits.h"
 
 #include "mojo/public/cpp/bindings/message.h"
 #include "net/base/reconnect_notifier.h"
-#include "services/network/public/mojom/reconnect_event_observer.mojom-shared.h"
+#include "services/network/public/mojom/connection_change_observer_client.mojom-shared.h"
 
 namespace mojo {
 
diff --git a/services/network/public/cpp/reconnect_event_observer_mojom_traits.h b/services/network/public/cpp/connection_change_observer_client_mojom_traits.h
similarity index 80%
rename from services/network/public/cpp/reconnect_event_observer_mojom_traits.h
rename to services/network/public/cpp/connection_change_observer_client_mojom_traits.h
index c4ad727..641352b3 100644
--- a/services/network/public/cpp/reconnect_event_observer_mojom_traits.h
+++ b/services/network/public/cpp/connection_change_observer_client_mojom_traits.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 SERVICES_NETWORK_PUBLIC_CPP_RECONNECT_EVENT_OBSERVER_MOJOM_TRAITS_H_
-#define SERVICES_NETWORK_PUBLIC_CPP_RECONNECT_EVENT_OBSERVER_MOJOM_TRAITS_H_
+#ifndef SERVICES_NETWORK_PUBLIC_CPP_CONNECTION_CHANGE_OBSERVER_CLIENT_MOJOM_TRAITS_H_
+#define SERVICES_NETWORK_PUBLIC_CPP_CONNECTION_CHANGE_OBSERVER_CLIENT_MOJOM_TRAITS_H_
 
 #include "base/component_export.h"
 #include "mojo/public/cpp/bindings/enum_traits.h"
 #include "mojo/public/cpp/bindings/struct_traits.h"
 #include "net/base/reconnect_notifier.h"
-#include "services/network/public/mojom/reconnect_event_observer.mojom-shared.h"
+#include "services/network/public/mojom/connection_change_observer_client.mojom-shared.h"
 
 namespace mojo {
 
@@ -47,4 +47,4 @@
 
 }  // namespace mojo
 
-#endif  // SERVICES_NETWORK_PUBLIC_CPP_RECONNECT_EVENT_OBSERVER_MOJOM_TRAITS_H_
+#endif  // SERVICES_NETWORK_PUBLIC_CPP_CONNECTION_CHANGE_OBSERVER_CLIENT_MOJOM_TRAITS_H_
diff --git a/services/network/public/mojom/BUILD.gn b/services/network/public/mojom/BUILD.gn
index 0d878bc..5aec2a9 100644
--- a/services/network/public/mojom/BUILD.gn
+++ b/services/network/public/mojom/BUILD.gn
@@ -1695,6 +1695,7 @@
   sources = [
     "cert_verifier_service.mojom",
     "cert_verifier_service_updater.mojom",
+    "connection_change_observer_client.mojom",
     "cookie_encryption_provider.mojom",
     "default_credentials.mojom",
     "dhcp_wpad_url_client.mojom",
@@ -1714,7 +1715,6 @@
     "oblivious_http_request.mojom",
     "proxy_lookup_client.mojom",
     "proxy_resolving_socket.mojom",
-    "reconnect_event_observer.mojom",
     "reporting_service.mojom",
     "restricted_udp_socket.mojom",
     "socket_broker.mojom",
@@ -1947,10 +1947,8 @@
           cpp = "::net::NetworkChangeEvent"
         },
       ]
-      traits_headers = [
-        "//services/network/public/cpp/reconnect_event_observer_mojom_traits.h",
-      ]
-      traits_sources = [ "//services/network/public/cpp/reconnect_event_observer_mojom_traits.cc" ]
+      traits_headers = [ "//services/network/public/cpp/connection_change_observer_client_mojom_traits.h" ]
+      traits_sources = [ "//services/network/public/cpp/connection_change_observer_client_mojom_traits.cc" ]
       traits_public_deps = [ "//net" ]
     },
   ]
diff --git a/services/network/public/mojom/reconnect_event_observer.mojom b/services/network/public/mojom/connection_change_observer_client.mojom
similarity index 94%
rename from services/network/public/mojom/reconnect_event_observer.mojom
rename to services/network/public/mojom/connection_change_observer_client.mojom
index ca6bb73..ad98d0ea 100644
--- a/services/network/public/mojom/reconnect_event_observer.mojom
+++ b/services/network/public/mojom/connection_change_observer_client.mojom
@@ -23,10 +23,7 @@
 // An observer to monitor whether a reconnect-emitting event has happened in a
 // connection. It primary use is to notify the browser process of the events
 // from the //net layer.
-//
-// TODO(crbug.com/406022435): Update the interface name to
-// `ConnectionChangeObserverClient`.
-interface ReconnectEventObserver {
+interface ConnectionChangeObserverClient {
     // Notify that the underlying network session has been closed. This means
     // that a connection was established, but was later closed (e.g. because
     // of idle timeout, GoAway from server, etc.).
diff --git a/services/network/public/mojom/network_context.mojom b/services/network/public/mojom/network_context.mojom
index 4ea464b..a0d482c 100644
--- a/services/network/public/mojom/network_context.mojom
+++ b/services/network/public/mojom/network_context.mojom
@@ -48,7 +48,7 @@
 import "services/network/public/mojom/proxy_config_with_annotation.mojom";
 import "services/network/public/mojom/proxy_lookup_client.mojom";
 import "services/network/public/mojom/proxy_resolving_socket.mojom";
-import "services/network/public/mojom/reconnect_event_observer.mojom";
+import "services/network/public/mojom/connection_change_observer_client.mojom";
 import "services/network/public/mojom/reporting_service.mojom";
 import "services/network/public/mojom/restricted_cookie_manager.mojom";
 import "services/network/public/mojom/restricted_udp_socket.mojom";
@@ -1487,7 +1487,7 @@
       NetworkAnonymizationKey network_anonymization_key,
       MutableNetworkTrafficAnnotationTag traffic_annotation,
       ConnectionKeepAliveConfig? keepalive_config,
-      pending_remote<ReconnectEventObserver>? reconnect_event_observer);
+      pending_remote<ConnectionChangeObserverClient>? observer_client);
 
   // Creates a P2PSocketManager instance, used for WebRTC.
   // `network_anonymization_key` is the network cache shard to associate with
diff --git a/services/network/test/test_network_context.h b/services/network/test/test_network_context.h
index 84bdf51..7628357c 100644
--- a/services/network/test/test_network_context.h
+++ b/services/network/test/test_network_context.h
@@ -24,6 +24,7 @@
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "services/network/public/cpp/network_service_buildflags.h"
 #include "services/network/public/mojom/clear_data_filter.mojom.h"
+#include "services/network/public/mojom/connection_change_observer_client.mojom.h"
 #include "services/network/public/mojom/cookie_access_observer.mojom.h"
 #include "services/network/public/mojom/cookie_manager.mojom.h"
 #include "services/network/public/mojom/device_bound_sessions.mojom.h"
@@ -33,7 +34,6 @@
 #include "services/network/public/mojom/network_service.mojom.h"
 #include "services/network/public/mojom/oblivious_http_request.mojom.h"
 #include "services/network/public/mojom/proxy_resolving_socket.mojom.h"
-#include "services/network/public/mojom/reconnect_event_observer.mojom.h"
 #include "services/network/public/mojom/restricted_cookie_manager.mojom.h"
 #include "services/network/public/mojom/restricted_udp_socket.mojom.h"
 #include "services/network/public/mojom/tcp_socket.mojom.h"
@@ -281,8 +281,8 @@
       const net::NetworkAnonymizationKey& network_anonymization_key,
       const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
       const std::optional<net::ConnectionKeepAliveConfig>& keepalive_config,
-      mojo::PendingRemote<network::mojom::ReconnectEventObserver>
-          reconnect_event_observer) override {}
+      mojo::PendingRemote<network::mojom::ConnectionChangeObserverClient>
+          observer_client) override {}
 #if BUILDFLAG(IS_P2P_ENABLED)
   void CreateP2PSocketManager(
       const net::NetworkAnonymizationKey& network_anonymization_key,
diff --git a/services/webnn/webnn_graph_mojolpm_fuzzer.cc b/services/webnn/webnn_graph_mojolpm_fuzzer.cc
index 7ca7f14..2a175ee 100644
--- a/services/webnn/webnn_graph_mojolpm_fuzzer.cc
+++ b/services/webnn/webnn_graph_mojolpm_fuzzer.cc
@@ -241,7 +241,10 @@
                testcase_->seed_for_input_data());
   }
 
-  bool IsFinished() { return action_index_ >= testcase_->actions_size(); }
+  // Cap the number of actions at 100 to avoid timeouts.
+  bool IsFinished() {
+    return action_index_ > 100 || action_index_ >= testcase_->actions_size();
+  }
 
  private:
   const raw_ref<const services::fuzzing::webnn_graph::proto::Testcase>
diff --git a/testing/scripts/run_performance_tests.py b/testing/scripts/run_performance_tests.py
index 23fb5f2..f9459b7 100755
--- a/testing/scripts/run_performance_tests.py
+++ b/testing/scripts/run_performance_tests.py
@@ -747,6 +747,7 @@
     self.network = self._get_network_arg(options.passthrough_args)
     self.is_chrome = (not self.cb_options.official_browser
                       or self.cb_options.official_browser.startswith('chrome'))
+    self.env = self._create_env_arg()
     if self.options.luci_chromium:
       # In luci.chromium the Chrome and driver are in the user path.
       self.browser = '--browser=%s' % get_abs_user_path('chrome')
@@ -786,6 +787,13 @@
       return self._create_fileserver_network(arg)
     return []
 
+  def _create_env_arg(self):
+    if (self.options.benchmarks.startswith('motionmark')
+        and sys.platform == 'darwin'):
+      # Set screen refresh rate to 60Hz on Mac due to crbug.com/415318275.
+      return ['--env={screen_refresh_rate:60}']
+    return []
+
   def _create_fileserver_network(self, arg):
     if '=' in arg:
       fileserver_path = arg.split('=', 1)[1]
@@ -886,7 +894,7 @@
     return (['vpython3', '-Xutf8'] + [self.options.executable] + [benchmark] +
             ['--env-validation=throw'] + [self.OUTDIR % working_dir] +
             [self.browser] + benchmark_args + self.driver_path_arg +
-            self.network + self._get_default_args())
+            self.network + self.env + self._get_default_args())
 
   def execute_benchmark(self,
                         benchmark,
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index e7e5559..a436e0e 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -107,21 +107,6 @@
             ]
         }
     ],
-    "AccessibilityAndroidEventInvestigations": [
-        {
-            "platforms": [
-                "android"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "AccessibilityAndroidEventInvestigations"
-                    ]
-                }
-            ]
-        }
-    ],
     "AccessibilityDeprecateJavaNodeCache": [
         {
             "platforms": [
@@ -526,6 +511,30 @@
             ]
         }
     ],
+    "AndroidAppIntegrationMultiDataSource": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled_SkipSchemaCheck",
+                    "params": {
+                        "multi_data_source_skip_schema_check": "true"
+                    },
+                    "enable_features": [
+                        "AndroidAppIntegrationMultiDataSource"
+                    ]
+                },
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "AndroidAppIntegrationMultiDataSource"
+                    ]
+                }
+            ]
+        }
+    ],
     "AndroidBookmarkBar": [
         {
             "platforms": [
@@ -5487,30 +5496,6 @@
             ]
         }
     ],
-    "ClientSideDetectionShowScamVerdictWarning": [
-        {
-            "platforms": [
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "EnabledWithWarning",
-                    "enable_features": [
-                        "ClientSideDetectionBrandAndIntentForScamDetection",
-                        "ClientSideDetectionShowScamVerdictWarning"
-                    ]
-                },
-                {
-                    "name": "EnabledMetricsCollection",
-                    "enable_features": [
-                        "ClientSideDetectionBrandAndIntentForScamDetection"
-                    ]
-                }
-            ]
-        }
-    ],
     "ClipboardChangeEvent": [
         {
             "platforms": [
@@ -14419,6 +14404,66 @@
             ]
         }
     ],
+    "MessagesAccessibilityEventInvestigations": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "EnabledDefaultImpl",
+                    "params": {
+                        "messages_accessibility_events_investigations_param": "1"
+                    },
+                    "enable_features": [
+                        "MessagesAccessibilityEventInvestigations"
+                    ]
+                },
+                {
+                    "name": "EnabledWithAccessibilityState",
+                    "params": {
+                        "messages_accessibility_events_investigations_param": "2"
+                    },
+                    "enable_features": [
+                        "MessagesAccessibilityEventInvestigations"
+                    ]
+                },
+                {
+                    "name": "EnabledWithRestrictiveServiceCheck",
+                    "params": {
+                        "messages_accessibility_events_investigations_param": "3"
+                    },
+                    "enable_features": [
+                        "MessagesAccessibilityEventInvestigations"
+                    ]
+                },
+                {
+                    "name": "EnabledWithMaskCheck",
+                    "params": {
+                        "messages_accessibility_events_investigations_param": "4"
+                    },
+                    "enable_features": [
+                        "MessagesAccessibilityEventInvestigations"
+                    ]
+                },
+                {
+                    "name": "EnabledWithDirectQuery",
+                    "params": {
+                        "messages_accessibility_events_investigations_param": "5"
+                    },
+                    "enable_features": [
+                        "MessagesAccessibilityEventInvestigations"
+                    ]
+                },
+                {
+                    "name": "Control",
+                    "disable_features": [
+                        "MessagesAccessibilityEventInvestigations"
+                    ]
+                }
+            ]
+        }
+    ],
     "MessagesPreinstall": [
         {
             "platforms": [
@@ -23387,6 +23432,25 @@
             ]
         }
     ],
+    "TabGroupShortcuts": [
+        {
+            "platforms": [
+                "chromeos",
+                "fuchsia",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "TabGroupShortcuts"
+                    ]
+                }
+            ]
+        }
+    ],
     "TabGroupSuggestionMetricsOnly": [
         {
             "platforms": [
@@ -24882,7 +24946,7 @@
                 {
                     "name": "Enabled_for_all_frames",
                     "params": {
-                        "Target": "AllFrames"
+                        "Target": "Interaction"
                     },
                     "enable_features": [
                         "VSyncAlignedPresent"
diff --git a/third_party/android_deps/autorolled/VERSION.txt b/third_party/android_deps/autorolled/VERSION.txt
index c8c2d0d..21b02bf 100644
--- a/third_party/android_deps/autorolled/VERSION.txt
+++ b/third_party/android_deps/autorolled/VERSION.txt
@@ -1 +1 @@
-8a2e368003c66e6.0ffd63e510b7037
\ No newline at end of file
+9cf783fd9ae7fea.61e2bf31c2a1d83
\ No newline at end of file
diff --git a/third_party/android_deps/autorolled/bill_of_materials.json b/third_party/android_deps/autorolled/bill_of_materials.json
index ab3302c9..c14e477 100644
--- a/third_party/android_deps/autorolled/bill_of_materials.json
+++ b/third_party/android_deps/autorolled/bill_of_materials.json
@@ -1092,22 +1092,22 @@
     {
         "name": "arcore",
         "group": "androidx.xr.arcore",
-        "version": "1.0.0-alpha02"
+        "version": "1.0.0-alpha04"
     },
     {
         "name": "runtime",
         "group": "androidx.xr.runtime",
-        "version": "1.0.0-alpha02"
+        "version": "1.0.0-alpha04"
     },
     {
         "name": "runtime-openxr",
         "group": "androidx.xr.runtime",
-        "version": "1.0.0-alpha02"
+        "version": "1.0.0-alpha04"
     },
     {
         "name": "scenecore",
         "group": "androidx.xr.scenecore",
-        "version": "1.0.0-alpha02"
+        "version": "1.0.0-alpha04"
     },
     {
         "name": "sqlite4java",
@@ -1287,7 +1287,7 @@
     {
         "name": "impress",
         "group": "com.google.ar",
-        "version": "0.0.2"
+        "version": "0.0.3"
     },
     {
         "name": "auto-service-annotations",
@@ -1652,12 +1652,12 @@
     {
         "name": "kotlinx-coroutines-reactive",
         "group": "org.jetbrains.kotlinx",
-        "version": "1.7.3"
+        "version": "1.8.1"
     },
     {
         "name": "kotlinx-coroutines-rx3",
         "group": "org.jetbrains.kotlinx",
-        "version": "1.7.3"
+        "version": "1.8.1"
     },
     {
         "name": "kotlinx-coroutines-test",
diff --git a/third_party/android_deps/autorolled/build.gradle b/third_party/android_deps/autorolled/build.gradle
index 9984e2f..8d90888 100644
--- a/third_party/android_deps/autorolled/build.gradle
+++ b/third_party/android_deps/autorolled/build.gradle
@@ -232,10 +232,10 @@
 versionCache['androidx.window:window'] = '1.5.0-SNAPSHOT'
 versionCache['androidx.window:window-core'] = '1.5.0-SNAPSHOT'
 versionCache['androidx.window:window-core-android'] = '1.5.0-SNAPSHOT'
-versionCache['androidx.xr.arcore:arcore'] = '1.0.0-alpha02'
-versionCache['androidx.xr.runtime:runtime'] = '1.0.0-alpha02'
-versionCache['androidx.xr.runtime:runtime-openxr'] = '1.0.0-alpha02'
-versionCache['androidx.xr.scenecore:scenecore'] = '1.0.0-alpha02'
+versionCache['androidx.xr.arcore:arcore'] = '1.0.0-alpha04'
+versionCache['androidx.xr.runtime:runtime'] = '1.0.0-alpha04'
+versionCache['androidx.xr.runtime:runtime-openxr'] = '1.0.0-alpha04'
+versionCache['androidx.xr.scenecore:scenecore'] = '1.0.0-alpha04'
 versionCache['com.almworks.sqlite4java:sqlite4java'] = '1.0.392'
 versionCache['com.android.support:support-annotations'] = '28.0.0'
 versionCache['com.android.tools.layoutlib:layoutlib-api'] = '30.2.0-beta01'
@@ -271,7 +271,7 @@
 versionCache['com.google.android.material:material'] = '1.14.0-alpha01'
 versionCache['com.google.android.play:core-common'] = '2.0.3'
 versionCache['com.google.android.play:feature-delivery'] = '2.1.0'
-versionCache['com.google.ar:impress'] = '0.0.2'
+versionCache['com.google.ar:impress'] = '0.0.3'
 versionCache['com.google.auto.service:auto-service-annotations'] = '1.0-rc6'
 versionCache['com.google.auto.value:auto-value-annotations'] = '1.11.0'
 versionCache['com.google.code.findbugs:jsr305'] = '3.0.2'
@@ -344,8 +344,8 @@
 versionCache['org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm'] = '1.8.1'
 versionCache['org.jetbrains.kotlinx:kotlinx-coroutines-guava'] = '1.8.1'
 versionCache['org.jetbrains.kotlinx:kotlinx-coroutines-play-services'] = '1.10.1'
-versionCache['org.jetbrains.kotlinx:kotlinx-coroutines-reactive'] = '1.7.3'
-versionCache['org.jetbrains.kotlinx:kotlinx-coroutines-rx3'] = '1.7.3'
+versionCache['org.jetbrains.kotlinx:kotlinx-coroutines-reactive'] = '1.8.1'
+versionCache['org.jetbrains.kotlinx:kotlinx-coroutines-rx3'] = '1.8.1'
 versionCache['org.jetbrains.kotlinx:kotlinx-coroutines-test'] = '1.8.1'
 versionCache['org.jetbrains.kotlinx:kotlinx-coroutines-test-jvm'] = '1.8.1'
 versionCache['org.jetbrains.kotlinx:kotlinx-serialization-bom'] = '1.7.3'
diff --git a/third_party/angle b/third_party/angle
index 6518078..71b58cd 160000
--- a/third_party/angle
+++ b/third_party/angle
@@ -1 +1 @@
-Subproject commit 65180785da62e5d12f0d2a28fa0cca1fe98f83ca
+Subproject commit 71b58cdcd7311f9572b064da0a2871e7c313535e
diff --git a/third_party/blink/perf_tests/MotionMark/README.md b/third_party/blink/perf_tests/MotionMark/README.md
index 4adab54..7722cbda3 100644
--- a/third_party/blink/perf_tests/MotionMark/README.md
+++ b/third_party/blink/perf_tests/MotionMark/README.md
@@ -1,7 +1,8 @@
-This folder is copy from PerformanceTests folder in webkit's repo
-(https://github.com/WebKit/webkit/tree/master/PerformanceTests).
+This folder is copy from MotionMark folder in webkit's repo
+(https://github.com/WebKit/MotionMark/).
 
-Revision: 4b28b98d37306eb5ae9ab8bca504590295f64210
+Revision: be2a5fea89b6ef411b053ebeb95a6302b3dc0ecb
+Tag: release/MotionMark1.3.1
 This is a temporary solution for Chrome to contribute to MotionMark tests
 before Webkit moves it to a separate project. "no-check" is added to bypass
 Chromium PRESUBMIT.
diff --git a/third_party/blink/perf_tests/MotionMark/about.html b/third_party/blink/perf_tests/MotionMark/about.html
index 5881f7ab..d81bf33 100644
--- a/third_party/blink/perf_tests/MotionMark/about.html
+++ b/third_party/blink/perf_tests/MotionMark/about.html
@@ -44,7 +44,7 @@
 
             <p>MotionMark is a web benchmark that focuses on graphics performance. It draws multiple rendering elements, each of which uses the same set of graphics primitives. An element could be an SVG node, an HTML element with CSS style, or a series of canvas operations. Slight variations among the elements avoid trivial caching optimizations by the browser. Although fairly simple, the effects were chosen to reflect techniques commonly used on the web. Tests are visually rich, being designed to stress the graphics system rather than JavaScript.</p>
 
-            <p>After an initial warm-up, each test runs for a fixed period of time. Based on measurements of the browser’s frame rate, MotionMark adjusts the number of elements to draw, and concentrates around a narrow range where the browser starts to fail animating at 60 frames per second (fps). A piecewise linear regression is applied to the data, and the change point is reported as the test's score. The confidence interval is calculated through <a href="https://en.wikipedia.org/wiki/Bootstrapping_(statistics)">bootstrapping</a>. MotionMark calculates the geometric mean of all of the tests’ scores to report the single score for the run.</p>
+            <p>After an initial warm-up, each test runs for a fixed period of time. Based on measurements of the browser’s frame rate, MotionMark adjusts the number of elements to draw, and concentrates around a narrow range where the browser starts to fail animating at the target frame rate (typically 60 frames per second). A piecewise linear regression is applied to the data, and the change point is reported as the test's score. The confidence interval is calculated through <a href="https://en.wikipedia.org/wiki/Bootstrapping_(statistics)">bootstrapping</a>. MotionMark calculates the geometric mean of all of the tests’ scores to report the single score for the run.</p>
 
             <p>MotionMark can be run on a wide variety of devices. Using the device’s screen dimensions it adjusts the drawing area into one of three sizes:</p>
 
@@ -54,15 +54,14 @@
                 <li>Large (1600 x 800), targeted at desktops</li>
             </ol>
 
-            <p>The design of the benchmark is modular. This makes it easy to write new tests and use different controllers, which can assist a developer working on improving the performance of a web engine. For the purpose of a public benchmark, the MotionMark main suite tests a variety of drawing operations using techniques including CSS, SVG, and Canvas:</p> <!-- nocheck -->
+            <p>The design of the benchmark is modular. This makes it easy to write new tests and use different controllers, which can assist a developer working on improving the performance of a web engine. For the purpose of a public benchmark, the MotionMark core suite tests a variety of drawing operations using techniques including CSS, SVG, and Canvas:</p>
 
             <ul>
                 <li><strong>Multiply</strong>: CSS border radius, transforms, opacity</li>
-                <li><strong>Arcs and Fills</strong>: Canvas path fills and arcs</li>
+                <li><strong>Canvas Arcs</strong>: Canvas path fills and arcs</li>
                 <li><strong>Leaves</strong>: CSS-transformed elements, opacity</li>
-                <li><strong>Paths</strong>: Canvas line, quadratic, and Bezier paths</li>
-                <li><strong>Lines</strong>: Canvas line segments</li>
-                <li><strong>Focus</strong>: CSS blur filter, opacity</li>
+                <li><strong>Canvas Paths</strong>: Canvas line, quadratic, and Bezier paths</li>
+                <li><strong>Canvas Lines</strong>: Canvas line segments</li>
                 <li><strong>Images</strong>: Canvas <code>getImageData()</code> and <code>putImageData()</code></li>
                 <li><strong>Design</strong>: HTML text rendering</li>
                 <li><strong>Suits</strong>: SVG clip paths, gradients and transforms</li>
@@ -70,10 +69,30 @@
 
             <p>To achieve consistent results on mobile devices, put the device in landscape orientation. On laptops and desktops, use the default display resolution and make the browser window fullscreen. Make sure that screen automatic display sleep is turned off or set to longer than 8 minutes.</p>
 
-            <h3>Version log</h3>
+            <p id="set-display-fps">The MotionMark benchmark relies on the requestAnimationFrame() JavaScript API, which provides callbacks at a consistent frequency related to screen refresh rate. However, browsers have made different choices about whether requestAnimationFrame() should strictly follow screen refresh rate. Safari currently fires requestAnimationFrame() callbacks at 60Hz on 120Hz screens, while other browsers fire it at 120Hz. This affects the benchmark score, so to compare browser scores across browsers, be sure to set the screen refresh rate to 60Hz (for example on macOS, this can be done in the Displays panel in System Settings).</p>
 
+            <h3>Version history</h3>
             <ul id="log">
-                <li><strong>1.3</strong>: Add support for non-60Hz <code>requestAnimationFrame</code> rates to the <a href="developer.html">developer settings</a>.</li>
+                <li><strong>1.3.1</strong>: 
+                    <ul>
+                        <li><a href="https://github.com/WebKit/MotionMark/commit/c5f2326e9a982cd0d6c8bc15320020ac57147156">Increase the max complexity of the Multiply subtest</a></li>
+                        <li><a href="https://github.com/WebKit/MotionMark/commit/f3034d6f7754eefcebd5df76657082344fc629ee">When a subtest hits maximum complexity, make its reported score be the maximum score.</a></li>
+                        <li><a href="https://github.com/WebKit/MotionMark/commit/3f6115f9ea26fc74e723cb2eb55580dd93bac3d9">Ensure the stage is correctly sized when running from a URL with test parameters</a></li>
+                        <li><a href="https://github.com/WebKit/MotionMark/commit/f1c7edb54b8d5aaa47303a577487a0a6bbe0e144">Make frame rate detection more reliable</a></li>
+                    </ul>
+                </li>
+                <li><strong>1.3</strong>: 
+                    <ul>
+                        <li><a href="https://github.com/WebKit/MotionMark/commit/37c610115c8c3fae8a74a18305727064c1fd4edc">Ignore mutation frames when scoring</li>
+                        <li><a href="https://github.com/WebKit/MotionMark/commit/da952728b532387c1d93789f36a44115e2d3cb84">The first frame of each ramp has an erroneously long frame duration</li>
+                        <li><a href="https://github.com/WebKit/MotionMark/commit/c0a884c2df3287362a126484cd8c07c940015463">Add support for non-60Hz <code>requestAnimationFrame</code> rates</a></li>
+                        <li><a href="https://github.com/WebKit/MotionMark/commit/9edd41ad33d24cc10b60576b8eb9626178f66122">Change the Multiply subtest to use <code>display:none</code> for non-animating elements</a></li>
+                        <li><a href="https://github.com/WebKit/MotionMark/commit/2b2e9d4e89dbd83922e7960872e8ad5f2d23d043">Increase the maximum complexity of the Multiply subtest</a></li>
+                        <li><a href="https://github.com/WebKit/MotionMark/commit/3090053c0fca81a9feb037075ef934275c690f3e">Make the Paths test workload more consistent</a></li>
+                        <li><a href="https://github.com/WebKit/MotionMark/commit/ff00967f5d6e92624d4e50e92f719a5ad7de95b7">Fix instability in the Design subtest</a></li>
+                        <li><a href="https://github.com/WebKit/MotionMark/commit/465dd49026a40a7bd8d30d64a264492165c711e8">Make the 'slope' profile the default profile</a></li>
+                    </ul>
+                </li>
                 <li><strong>1.2</strong>: Fix <a href="https://bugs.webkit.org/show_bug.cgi?id=220847">bug</a>, <a href="https://bugs.webkit.org/show_bug.cgi?id=221075">bug</a>, and <a href="https://bugs.webkit.org/show_bug.cgi?id=219984">bug</a> to reduce test variance and sensitivity to individual long frames.</li>
                 <li><strong>1.1.1</strong>: Fix <a href="https://bugs.webkit.org/show_bug.cgi?id=210640">bug</a> in the calculation of timestamps used for animation during warm up phase of tests.</li>
                 <li><a href="https://webkit.org/blog/8434/motionmark-1-1/"><strong>1.1</strong></a>: Update Multiply test to increase max capacity and expand methods for hiding elements. Update Leaves test to use range of sizes and opacity.</li>
diff --git a/third_party/blink/perf_tests/MotionMark/developer.html b/third_party/blink/perf_tests/MotionMark/developer.html
index e00d274d..3783708 100644
--- a/third_party/blink/perf_tests/MotionMark/developer.html
+++ b/third_party/blink/perf_tests/MotionMark/developer.html
@@ -40,7 +40,7 @@
     <script src="resources/runner/tests.js" charset="utf-8"></script>
     <script src="resources/debug-runner/tests.js" charset="utf-8"></script>
     <script src="resources/runner/motionmark.js"></script>
-    <script src="resources/debug-runner/motionmark.js" charset="utf-8"></script>
+    <script src="resources/debug-runner/debug-runner.js" charset="utf-8"></script>
 
     <script src="resources/runner/benchmark-runner.js"></script>
     <script src="resources/debug-runner/d3.min.js"></script>
@@ -100,8 +100,7 @@
                                 </li>
                                 <li>
                                     <label>System frame rate: <input type="number" id="system-frame-rate" value="60"> FPS</label><br>
-                                    <label>Target frame rate: <input type="number" id="frame-rate" value="50"> FPS</label><br>
-                                    (Guide: should be about 5/6th of the system frame rate)
+                                    <label>Target frame rate: <input type="number" id="frame-rate" value="60"> FPS</label>
                                 </li>
                                 <li>
                                     <h3>Time measurement method:</h3>
@@ -124,8 +123,8 @@
                     Attempting to detect system frame rate: <span>0</span> FPS (in progress).
                 </p>
                 <div class="start-benchmark">
-                    <p class="hidden">Please rotate the device to orientation before starting.</p>
-                    <button id="run-benchmark" onclick="benchmarkController.startBenchmark()">Run benchmark</button>
+                    <p class="portrait-orientation-check hidden">Please rotate the device to orientation before starting.</p>
+                    <button id="start-button" onclick="benchmarkController.startBenchmark()">Run benchmark</button>
                 </div>
             </div>
         </section>
@@ -167,8 +166,8 @@
                 <header>
                     <button onclick="benchmarkController.showResults()">&lt; Results</button>
                     <h1>Graph:</h1>
-                    <p class="score"></p>
-                    <p class="confidence"></p>
+                    <p class="score">&nbsp;</p>
+                    <p class="confidence">&nbsp;</p>
                 </header>
                 <nav>
                     <form name="graph-type">
diff --git a/third_party/blink/perf_tests/MotionMark/index.html b/third_party/blink/perf_tests/MotionMark/index.html
index 876e0de..925a5f1 100644
--- a/third_party/blink/perf_tests/MotionMark/index.html
+++ b/third_party/blink/perf_tests/MotionMark/index.html
@@ -61,7 +61,8 @@
                 <p><a href="about.html">More details</a> about the benchmark are available. Bigger scores are better.</p>
                 <p>For accurate results, please take your browser window full screen, or rotate your device to landscape orientation.</p>
                 <p class="portrait-orientation-check"><b>Please rotate your device.</b></p>
-                <button class="landscape-orientation-check" onclick="benchmarkController.startBenchmark()">Run Benchmark</button>
+                <button id="start-button" class="landscape-orientation-check" onclick="benchmarkController.startBenchmark()">Run Benchmark</button>
+                <p id="frame-rate-label">&nbsp;</p>
             </div>
         </section>
 
diff --git a/third_party/blink/perf_tests/MotionMark/resources/debug-runner/motionmark.js b/third_party/blink/perf_tests/MotionMark/resources/debug-runner/debug-runner.js
similarity index 90%
rename from third_party/blink/perf_tests/MotionMark/resources/debug-runner/motionmark.js
rename to third_party/blink/perf_tests/MotionMark/resources/debug-runner/debug-runner.js
index 5df2466..d27fa04 100644
--- a/third_party/blink/perf_tests/MotionMark/resources/debug-runner/motionmark.js
+++ b/third_party/blink/perf_tests/MotionMark/resources/debug-runner/debug-runner.js
@@ -158,8 +158,7 @@
     }
 });
 
-window.optionsManager =
-{
+window.optionsManager = {
     valueForOption: function(name)
     {
         var formElement = document.forms["benchmark-options"].elements[name];
@@ -251,8 +250,7 @@
     }
 };
 
-window.suitesManager =
-{
+window.suitesManager = {
     _treeElement: function()
     {
         return document.querySelector("#suites > .tree");
@@ -530,7 +528,7 @@
 }
 
 Utilities.extendObject(window.benchmarkController, {
-    initialize: function()
+    initialize: async function()
     {
         document.title = Strings.text.title.replace("%s", Strings.version);
         document.querySelectorAll(".version").forEach(function(e) {
@@ -553,33 +551,43 @@
         suitesManager.updateUIFromLocalStorage();
         suitesManager.updateEditsElementsState();
 
-        benchmarkController.detectSystemFrameRate();
-
         var dropTarget = document.getElementById("drop-target");
         function stopEvent(e) {
             e.stopPropagation();
             e.preventDefault();
         }
-        dropTarget.addEventListener("dragenter", stopEvent, false);
-        dropTarget.addEventListener("dragover", stopEvent, false);
-        dropTarget.addEventListener("dragleave", stopEvent, false);
-        dropTarget.addEventListener("drop", function (e) {
-            e.stopPropagation();
-            e.preventDefault();
+        dropTarget.addEventListener("dragenter", (e) => {
+            dropTarget.classList.add("drag-over");
+            stopEvent(e);
+        }, false);
 
-            if (!e.dataTransfer.files.length)
+        dropTarget.addEventListener("dragover", stopEvent, false);
+
+        dropTarget.addEventListener("dragleave", (e) => {
+            dropTarget.classList.remove("drag-over");
+            stopEvent(e);
+        }, false);
+
+        dropTarget.addEventListener("drop", function (e) {
+            stopEvent(e);
+
+            if (!e.dataTransfer.files.length) {
+                dropTarget.classList.remove("drag-over");
                 return;
+            }
+
+            dropTarget.textContent = 'Processing…';
 
             var file = e.dataTransfer.files[0];
 
             var reader = new FileReader();
             reader.filename = file.name;
-            reader.onload = function(e) {
+            reader.onload = (e) => {
                 var run = JSON.parse(e.target.result);
                 if (run.debugOutput instanceof Array)
                     run = run.debugOutput[0];
-                if (!("version" in run))
-                    run.version = "1.0";
+
+                benchmarkController.migrateImportedData(run);
                 benchmarkRunnerClient.results = new ResultsDashboard(run.version, run.options, run.data);
                 benchmarkController.showResults();
             };
@@ -587,16 +595,63 @@
             reader.readAsText(file);
             document.title = "File: " + reader.filename;
         }, false);
+
+        this.frameRateDetectionComplete = false;
+        this.updateStartButtonState();
+
+        let progressElement = document.querySelector("#frame-rate-detection span");
+
+        let targetFrameRate;
+        try {
+            targetFrameRate = await benchmarkController.determineFrameRate(progressElement);
+        } catch (e) {
+        }
+        
+        this.frameRateDeterminationComplete(targetFrameRate);
+    },
+
+    migrateImportedData: function(runData)
+    {
+        if (!("version" in runData))
+            runData.version = "1.0";
+        
+        if (!("frame-rate" in runData.options)) {
+            runData.options["frame-rate"] = 60;
+            console.log("No frame-rate data; assuming 60fps")
+        }
+
+        if (!("system-frame-rate" in runData.options)) {
+            runData.options["system-frame-rate"] = 60;
+            console.log("No system-frame-rate data; assuming 60fps")
+        }
+    },
+
+    frameRateDeterminationComplete: function(targetFrameRate)
+    {
+        let frameRateLabelContent = Strings.text.usingFrameRate.replace("%s", targetFrameRate);
+        
+        if (!targetFrameRate) {
+            frameRateLabelContent = Strings.text.frameRateDetectionFailure;
+            targetFrameRate = 60;
+        }
+
+        document.getElementById("frame-rate-detection").textContent = frameRateLabelContent;
+        document.getElementById("system-frame-rate").value = targetFrameRate;
+        document.getElementById("frame-rate").value = targetFrameRate;
+
+        this.frameRateDetectionComplete = true;
+        this.updateStartButtonState();
     },
 
     updateStartButtonState: function()
     {
-        var startButton = document.getElementById("run-benchmark");
+        var startButton = document.getElementById("start-button");
         if ("isInLandscapeOrientation" in this && !this.isInLandscapeOrientation) {
             startButton.disabled = true;
             return;
         }
-        startButton.disabled = !suitesManager.isAtLeastOneTestSelected();
+        
+        startButton.disabled = (!suitesManager.isAtLeastOneTestSelected()) || !this.frameRateDetectionComplete;
     },
 
     onBenchmarkOptionsChanged: function(event)
@@ -624,6 +679,7 @@
 
     startBenchmarkImmediatelyIfEncoded: function()
     {
+        benchmarkController.determineCanvasSize();
         benchmarkController.options = Utilities.convertQueryStringToObject(location.search);
         if (!benchmarkController.options)
             return false;
@@ -682,47 +738,5 @@
         sectionsManager.setSectionHeader("test-graph", testName);
         sectionsManager.showSection("test-graph", true);
         this.updateGraphData(testResult, testData, benchmarkRunnerClient.results.options);
-    },
-    detectSystemFrameRate: function()
-    {
-        let last = 0;
-        let average = 0;
-        let count = 0;
-
-        const finish = function()
-        {
-            const commonFrameRates = [15, 30, 45, 60, 90, 120, 144];
-            const distanceFromFrameRates = commonFrameRates.map(rate => {
-                return Math.abs(Math.round(rate - average));
-            });
-            let shortestDistance = Number.MAX_VALUE;
-            let targetFrameRate = undefined;
-            for (let i = 0; i < commonFrameRates.length; i++) {
-                if (distanceFromFrameRates[i] < shortestDistance) {
-                    targetFrameRate = commonFrameRates[i];
-                    shortestDistance = distanceFromFrameRates[i];
-                }
-            }
-            targetFrameRate = targetFrameRate || 60;
-            document.getElementById("frame-rate-detection").textContent = `Detected system frame rate as ${targetFrameRate} FPS`;
-            document.getElementById("system-frame-rate").value = targetFrameRate;
-            document.getElementById("frame-rate").value = Math.round(targetFrameRate * 5 / 6);
-        }
-
-        const tick = function(timestamp)
-        {
-            average -= average / 30;
-            average += 1000. / (timestamp - last) / 30;
-            document.querySelector("#frame-rate-detection span").textContent = Math.round(average);
-            last = timestamp;
-            count++;
-            if (count < 300)
-                requestAnimationFrame(tick);
-            else
-                finish();
-        }
-
-        requestAnimationFrame(tick);
     }
-
 });
diff --git a/third_party/blink/perf_tests/MotionMark/resources/debug-runner/graph.js b/third_party/blink/perf_tests/MotionMark/resources/debug-runner/graph.js
index 4b420e8..82e6f3f 100644
--- a/third_party/blink/perf_tests/MotionMark/resources/debug-runner/graph.js
+++ b/third_party/blink/perf_tests/MotionMark/resources/debug-runner/graph.js
@@ -22,6 +22,9 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  * THE POSSIBILITY OF SUCH DAMAGE.
  */
+
+const msPerSecond = 1000;
+
 Utilities.extendObject(window.benchmarkController, {
     updateGraphData: function(testResult, testData, options)
     {
@@ -30,10 +33,11 @@
         element._testResult = testResult;
         element._options = options;
 
-        var margins = new Insets(30, 30, 30, 40);
+        // top, right, bottom, left.
+        var margins = new Insets(30, 30, 50, 40);
+        // Note that changes to header content (in onGraphTypeChanged()) can change the available size, so we prepopulate
+        // "score" and "confidence" elements with non-breaking spaces in the HTML.
         var size = Point.elementClientSize(element);
-        size.y = window.innerHeight - element.offsetTop;
-        size = size.subtract(margins.size);
 
         // Convert from compact JSON output to propertied data
         var samplesWithProperties = {};
@@ -42,7 +46,7 @@
             samplesWithProperties[seriesName] = series.toArray();
         })
 
-        this._targetFrameRate = options["frame-rate"] || 60;
+        this._targetFrameRate = options["frame-rate"];
 
         this.createTimeGraph(testResult, samplesWithProperties[Strings.json.controller], testData[Strings.json.marks], testData[Strings.json.controller], options, margins, size);
         this.onTimeGraphOptionsChanged();
@@ -98,14 +102,53 @@
         this._addRegressionLine(svg, xScale, yScale, data.segment1, data.stdev);
         this._addRegressionLine(svg, xScale, yScale, data.segment2, data.stdev);
     },
+    
+    _tickValuesForFrameRate: function(frameRate, minValue, maxValue)
+    {
+        // Tick labels go up to 1.5x frame rate
+        const buildInFrameRates = {
+            15 : [5, 10, 15, 20],
+            30 : [5, 10, 15, 20, 25, 30, 35, 40],
+            45 : [30, 35, 40, 45, 50, 55, 60],
+            60 : [30, 35, 40, 45, 50, 55, 60, 90],
+            90 : [30, 35, 40, 45, 50, 55, 60, 90, 120],
+            120 : [30, 40, 50, 60, 70, 80, 100, 120, 150],
+            144 : [40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 200],
+        };
+        
+        let tickValues = buildInFrameRates[frameRate];
+        if (!tickValues) {
+            const minLabel = Math.round(minValue / 10) * 10;
+            const maxLabel = Math.round(maxValue / 10) * 10;
+            tickValues = [];
+            let curValue = minLabel;
+            while (curValue <= maxLabel) {
+                tickValues.push(curValue);
+                curValue += 20;
+            }
+        }
+        
+        tickValues = tickValues.map((x) => msPerSecond / x);
+        return tickValues;
+    },
+    
+    _minFrameRate: function()
+    {
+        return this._targetFrameRate / 4;
+    },
+
+    _maxFrameRate: function()
+    {
+        return this._targetFrameRate * 1.5;
+    },
 
     createComplexityGraph: function(result, timeRegressions, data, options, margins, size)
     {
         var svg = d3.select("#test-graph-data").append("svg")
             .attr("id", "complexity-graph")
             .attr("class", "hidden")
-            .attr("width", size.width + margins.left + margins.right)
-            .attr("height", size.height + margins.top + margins.bottom)
+            .attr("width", size.width)
+            .attr("height", size.height)
             .append("g")
                 .attr("transform", "translate(" + margins.left + "," + margins.top + ")");
 
@@ -124,30 +167,40 @@
             xMax = d3.max(timeSamples, function(s) { return s.complexity; });
         }
 
+        const axisWidth = size.width - margins.left - margins.right;
+        const axisHeight = size.height - margins.top - margins.bottom;
+
+        // The y axis is frameLength in ms, inverted with the axis labels showing fps.
+        const minFrameRate = this._minFrameRate();
+        const maxFrameRate = this._maxFrameRate();
+
+        const yMin = msPerSecond / minFrameRate;
+        const yMax = msPerSecond / maxFrameRate;
+
         var xScale = d3.scale.linear()
-            .range([0, size.width])
+            .range([0, axisWidth])
             .domain([xMin, xMax]);
         var yScale = d3.scale.linear()
-            .range([size.height, 0])
-            .domain([1000/(this._targetFrameRate/3), 1000/this._targetFrameRate]);
+            .range([axisHeight, 0])
+            .domain([yMin, yMax]);
 
         var xAxis = d3.svg.axis()
             .scale(xScale)
             .orient("bottom");
         var yAxis = d3.svg.axis()
             .scale(yScale)
-            .tickValues([1000/20, 1000/25, 1000/30, 1000/35, 1000/40, 1000/45, 1000/50, 1000/55, 1000/60, 1000/90, 1000/120])
-            .tickFormat(function(d) { return (1000 / d).toFixed(0); })
+            .tickValues(this._tickValuesForFrameRate(this._targetFrameRate, minFrameRate, maxFrameRate))
+            .tickFormat(function(d) { return (msPerSecond / d).toFixed(0); })
             .orient("left");
 
         // x-axis
         svg.append("g")
             .attr("class", "x axis")
-            .attr("transform", "translate(0," + size.height + ")")
+            .attr("transform", "translate(0," + axisHeight + ")")
             .call(xAxis);
 
         // y-axis
-        svg.append("g")
+        var yAxisGroup = svg.append("g")
             .attr("class", "y axis")
             .call(yAxis);
 
@@ -155,7 +208,6 @@
         var mean = svg.append("g")
             .attr("class", "mean complexity");
         var timeResult = result[Strings.json.controller];
-        var yMin = yScale.domain()[0], yMax = yScale.domain()[1];
         this._addRegressionLine(mean, xScale, yScale, [[timeResult.average, yMin], [timeResult.average, yMax]], timeResult.stdev, true);
 
         // regression
@@ -166,7 +218,7 @@
             var histogram = d3.layout.histogram()
                 .bins(xScale.ticks(100))(bootstrapResult.data);
             var yBootstrapScale = d3.scale.linear()
-                .range([size.height/2, 0])
+                .range([axisHeight/2, 0])
                 .domain([0, d3.max(histogram, function(d) { return d.y; })]);
             group = svg.append("g").attr("class", "bootstrap");
             var bar = group.selectAll(".bar")
@@ -176,14 +228,14 @@
                     .attr("transform", function(d) { return "translate(" + xScale(d.x) + "," + yBootstrapScale(d.y) + ")"; });
             bar.append("rect")
                 .attr("x", 1)
-                .attr("y", size.height/2)
+                .attr("y", axisHeight/2)
                 .attr("width", xScale(histogram[1].x) - xScale(histogram[0].x) - 1)
-                .attr("height", function(d) { return size.height/2 - yBootstrapScale(d.y); });
+                .attr("height", function(d) { return axisHeight/2 - yBootstrapScale(d.y); });
             group = group.append("g").attr("class", "median");
             this._addRegressionLine(group, xScale, yScale, [[bootstrapResult.median, yMin], [bootstrapResult.median, yMax]], [bootstrapResult.confidenceLow, bootstrapResult.confidenceHigh], true);
             group.append("circle")
                 .attr("cx", xScale(bootstrapResult.median))
-                .attr("cy", yScale(1000/60))
+                .attr("cy", yScale(msPerSecond / this._targetFrameRate))
                 .attr("r", 5);
         }
 
@@ -193,16 +245,19 @@
             .selectAll("line")
                 .data(data[Strings.json.complexity])
                 .enter();
+
         group.append("line")
             .attr("x1", function(d) { return xScale(d.complexity) - 3; })
             .attr("x2", function(d) { return xScale(d.complexity) + 3; })
             .attr("y1", function(d) { return yScale(d.frameLength) - 3; })
-            .attr("y2", function(d) { return yScale(d.frameLength) + 3; });
-        group.append("line")
+            .attr("y2", function(d) { return yScale(d.frameLength) + 3; })
+            .attr("class", function(d) { return d.frameType === "m" ? 'mutation' : 'animation'; });
+        group.append("line") 
             .attr("x1", function(d) { return xScale(d.complexity) - 3; })
             .attr("x2", function(d) { return xScale(d.complexity) + 3; })
             .attr("y1", function(d) { return yScale(d.frameLength) + 3; })
-            .attr("y2", function(d) { return yScale(d.frameLength) - 3; });
+            .attr("y2", function(d) { return yScale(d.frameLength) - 3; })
+            .attr("class", function(d) { return d.frameType === "m" ? 'mutation' : 'animation'; });
 
         // Cursor
         var cursorGroup = svg.append("g").attr("class", "cursor hidden");
@@ -236,7 +291,7 @@
             .attr("x", 0)
             .attr("y", 0)
             .attr("width", size.width)
-            .attr("height", size.height);
+            .attr("height", axisHeight);
 
         area.on("mouseover", function() {
             document.querySelector("#complexity-graph .cursor").classList.remove("hidden");
@@ -256,22 +311,25 @@
                 .attr("y2", location[1]);
             cursorGroup.select("text.y")
                 .attr("y", location[1])
-                .text((1000 / location_domain[1]).toFixed(1));
+                .text((msPerSecond / location_domain[1]).toFixed(1));
         });
     },
 
     createTimeGraph: function(result, samples, marks, regressions, options, margins, size)
     {
+        const axisWidth = size.width - margins.left - margins.right;
+        const axisHeight = size.height - margins.top - margins.bottom;
+
         var svg = d3.select("#test-graph-data").append("svg")
             .attr("id", "time-graph")
-            .attr("width", size.width + margins.left + margins.right)
-            .attr("height", size.height + margins.top + margins.bottom)
+            .attr("width", size.width)
+            .attr("height", size.height)
             .append("g")
                 .attr("transform", "translate(" + margins.left + "," + margins.top + ")");
 
         // Axis scales
         var x = d3.scale.linear()
-                .range([0, size.width])
+                .range([0, axisWidth])
                 .domain([
                     Math.min(d3.min(samples, function(s) { return s.time; }), 0),
                     d3.max(samples, function(s) { return s.time; })]);
@@ -280,37 +338,48 @@
                 return s.complexity;
             return 0;
         });
+        complexityMax *= 1.2;
 
+        const graphTop = 10;
         var yLeft = d3.scale.linear()
-                .range([size.height, 0])
+                .range([axisHeight, graphTop])
                 .domain([0, complexityMax]);
+
+
+        const minFrameRate = this._minFrameRate();
+        const maxFrameRate = this._maxFrameRate();
+
+        const yRightMin = msPerSecond / minFrameRate;
+        const yRightMax = msPerSecond / maxFrameRate;
+
         var yRight = d3.scale.linear()
-                .range([size.height, 0])
-                .domain([1000/(this._targetFrameRate/3), 1000/this._targetFrameRate]);
+                .range([axisHeight, graphTop])
+                .domain([yRightMin, yRightMax]);
 
         // Axes
         var xAxis = d3.svg.axis()
                 .scale(x)
                 .orient("bottom")
-                .tickFormat(function(d) { return (d/1000).toFixed(0); });
+                .tickFormat(function(d) { return (d / msPerSecond).toFixed(0); });
         var yAxisLeft = d3.svg.axis()
                 .scale(yLeft)
                 .orient("left");
+
         var yAxisRight = d3.svg.axis()
                 .scale(yRight)
-                .tickValues([1000/20, 1000/25, 1000/30, 1000/35, 1000/40, 1000/45, 1000/50, 1000/55, 1000/60, 1000/90, 1000/120])
-                .tickFormat(function(d) { return (1000/d).toFixed(0); })
+                .tickValues(this._tickValuesForFrameRate(this._targetFrameRate, minFrameRate, maxFrameRate))
+                .tickFormat(function(d) { return (msPerSecond / d).toFixed(0); })
                 .orient("right");
 
         // x-axis
         svg.append("g")
             .attr("class", "x axis")
             .attr("fill", "rgb(235, 235, 235)")
-            .attr("transform", "translate(0," + size.height + ")")
+            .attr("transform", "translate(0," + axisHeight + ")")
             .call(xAxis)
             .append("text")
                 .attr("class", "label")
-                .attr("x", size.width)
+                .attr("x", axisWidth)
                 .attr("y", -6)
                 .attr("fill", "rgb(235, 235, 235)")
                 .style("text-anchor", "end")
@@ -334,7 +403,7 @@
         svg.append("g")
             .attr("class", "yRight axis")
             .attr("fill", "#FA4925")
-            .attr("transform", "translate(" + size.width + ", 0)")
+            .attr("transform", "translate(" + axisWidth + ", 0)")
             .call(yAxisRight)
             .append("text")
                 .attr("class", "label")
@@ -376,15 +445,15 @@
             var frameLength = result[Strings.json.frameLength];
             var regression = svg.append("g")
                 .attr("class", "fps mean");
-            this._addRegressionLine(regression, x, yRight, [[samples[0].time, 1000/frameLength.average], [samples[samples.length - 1].time, 1000/frameLength.average]], frameLength.stdev);
+            this._addRegressionLine(regression, x, yRight, [[samples[0].time, msPerSecond / frameLength.average], [samples[samples.length - 1].time, msPerSecond / frameLength.average]], frameLength.stdev);
         }
 
         // right-target
         if (options["controller"] == "adaptive") {
-            var targetFrameLength = 1000 / options["frame-rate"];
+            var targetFrameLength = msPerSecond / options["frame-rate"];
             svg.append("line")
                 .attr("x1", x(0))
-                .attr("x2", size.width)
+                .attr("x2", axisWidth)
                 .attr("y1", yRight(targetFrameLength))
                 .attr("y2", yRight(targetFrameLength))
                 .attr("class", "target-fps marker");
@@ -485,8 +554,8 @@
             .attr("fill", "transparent")
             .attr("x", 0)
             .attr("y", 0)
-            .attr("width", size.width)
-            .attr("height", size.height);
+            .attr("width", axisWidth)
+            .attr("height", axisHeight);
 
         var timeBisect = d3.bisector(function(d) { return d.time; }).right;
         var statsToHighlight = ["complexity", "rawFPS", "filteredFPS"];
@@ -506,7 +575,7 @@
             var cursor_y = yAxisRight.scale().domain()[1];
             var ys = [yRight(yAxisRight.scale().domain()[0]), yRight(yAxisRight.scale().domain()[1])];
 
-            document.querySelector("#test-graph nav .time").textContent = (data.time / 1000).toFixed(4) + "s (" + index + ")";
+            document.querySelector("#test-graph nav .time").textContent = (data.time / msPerSecond).toFixed(4) + "s (" + index + ")";
             statsToHighlight.forEach(function(name) {
                 var element = document.querySelector("#test-graph nav ." + name);
                 var content = "";
@@ -517,12 +586,12 @@
                     data_y = yLeft(data.complexity);
                     break;
                 case "rawFPS":
-                    content = (1000/data.frameLength).toFixed(2);
+                    content = (msPerSecond / data.frameLength).toFixed(2);
                     data_y = yRight(data.frameLength);
                     break;
                 case "filteredFPS":
                     if ("smoothedFrameLength" in data) {
-                        content = (1000/data.smoothedFrameLength).toFixed(2);
+                        content = (msPerSecond / data.smoothedFrameLength).toFixed(2);
                         data_y = yRight(data.smoothedFrameLength);
                     }
                     break;
diff --git a/third_party/blink/perf_tests/MotionMark/resources/debug-runner/motionmark.css b/third_party/blink/perf_tests/MotionMark/resources/debug-runner/motionmark.css
index b3079b9..0927fa5 100644
--- a/third_party/blink/perf_tests/MotionMark/resources/debug-runner/motionmark.css
+++ b/third_party/blink/perf_tests/MotionMark/resources/debug-runner/motionmark.css
@@ -287,9 +287,8 @@
     color: rgb(235, 235, 235);
 }
 
-#drop-target:hover {
+#drop-target.drag-over {
     background-color: rgba(255, 255, 255, .1);
-    cursor: pointer;
 }
 
 #options ul {
@@ -571,10 +570,6 @@
     flex-direction: row;
 }
 
-#test-graph {
-    flex: 1 0 calc(100% - 40px);
-}
-
 #test-graph h1 {
     margin-bottom: 0;
 }
@@ -627,7 +622,29 @@
 /*                           Graph Section                                    */
 /* -------------------------------------------------------------------------- */
 
+body.showing-test-graph {
+    height: 100vh;
+}
+
+body.showing-test-graph main, #test-graph {
+    height: 100%;
+}
+
+body.showing-test-graph header, body.showing-test-graph nav {
+    flex-basis: auto;
+}
+
+#test-graph .body {
+    display: flex;
+    flex-direction: column;
+    margin-trim: block-start;
+    margin: 1em;
+    height: calc(100% - 2em);
+}
+
 #test-graph-data {
+    flex-grow: 1;
+    min-height: 0;
     z-index: 1;
     font: 10px sans-serif;
     color: rgb(235, 235, 235);
@@ -635,7 +652,7 @@
 
 #test-graph-data > svg {
     fill: none;
-    overflow: visible;
+/*    overflow: hidden;*/
 }
 
 .axis path,
@@ -775,6 +792,10 @@
     stroke-width: 1px;
 }
 
+#complexity-graph .raw.series line.mutation {
+    stroke: rgba(255, 255, 255, .15);
+}
+
 #complexity-graph .raw.regression line {
     stroke: rgba(30, 96%, 86%, .6);
 }
diff --git a/third_party/blink/perf_tests/MotionMark/resources/debug-runner/tests.js b/third_party/blink/perf_tests/MotionMark/resources/debug-runner/tests.js
index ed96349..f48d0b1 100644
--- a/third_party/blink/perf_tests/MotionMark/resources/debug-runner/tests.js
+++ b/third_party/blink/perf_tests/MotionMark/resources/debug-runner/tests.js
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015-2018 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2023 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -183,7 +183,7 @@
             name: "CSS bouncing filter circles"
         },
         {
-            url: "bouncing-particles/bouncing-css-images.html?particleWidth=80&particleHeight=80&imageSrc=../resources/yin-yang.svg",
+            url: "bouncing-particles/bouncing-css-images.html?particleWidth=80&particleHeight=80",
             name: "CSS bouncing SVG images"
         },
         {
@@ -199,7 +199,7 @@
             name: "DOM particles, SVG masks"
         },
         {
-            url: "dom/compositing-transforms.html?particleWidth=50&particleHeight=50&filters=yes&imageSrc=../resources/yin-yang.svg",
+            url: "dom/compositing-transforms.html?particleWidth=50&particleHeight=50&filters=yes",
             name: "Composited Transforms"
         }
     ]
@@ -216,11 +216,11 @@
             name: "canvas bouncing gradient circles"
         },
         {
-            url: "bouncing-particles/bouncing-canvas-images.html?particleWidth=80&particleHeight=80&imageSrc=../resources/yin-yang.svg",
+            url: "bouncing-particles/bouncing-canvas-images.html?particleWidth=80&particleHeight=80",
             name: "canvas bouncing SVG images"
         },
         {
-            url: "bouncing-particles/bouncing-canvas-images.html?particleWidth=80&particleHeight=80&imageSrc=../resources/yin-yang.png",
+            url: "bouncing-particles/bouncing-canvas-images.html?particleWidth=80&particleHeight=80",
             name: "canvas bouncing PNG images"
         },
         {
@@ -253,11 +253,11 @@
             name: "SVG bouncing gradient circles"
         },
         {
-            url: "bouncing-particles/bouncing-svg-images.html?particleWidth=80&particleHeight=80&imageSrc=../resources/yin-yang.svg",
+            url: "bouncing-particles/bouncing-svg-images.html?particleWidth=80&particleHeight=80",
             name: "SVG bouncing SVG images"
         },
         {
-            url: "bouncing-particles/bouncing-svg-images.html?particleWidth=80&particleHeight=80&imageSrc=../resources/yin-yang.png",
+            url: "bouncing-particles/bouncing-svg-images.html?particleWidth=80&particleHeight=80",
             name: "SVG bouncing PNG images"
         },
     ]
@@ -427,10 +427,6 @@
             name: "Canvas ellipses"
         },
         {
-            url: "simple/simple-canvas-paths.html?pathType=spreadSheets",
-            name: "Canvas Spreadsheets"
-        },
-        {
             url: "simple/simple-canvas-paths.html?pathType=lineFill",
             name: "Canvas line path, fill"
         },
diff --git a/third_party/blink/perf_tests/MotionMark/resources/extensions.js b/third_party/blink/perf_tests/MotionMark/resources/extensions.js
index c303c3f..3fb5794d 100644
--- a/third_party/blink/perf_tests/MotionMark/resources/extensions.js
+++ b/third_party/blink/perf_tests/MotionMark/resources/extensions.js
@@ -22,8 +22,7 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  * THE POSSIBILITY OF SUCH DAMAGE.
  */
-Utilities =
-{
+Utilities = {
     _parse: function(str, sep)
     {
         var output = {};
diff --git a/third_party/blink/perf_tests/MotionMark/resources/runner/motionmark.css b/third_party/blink/perf_tests/MotionMark/resources/runner/motionmark.css
index 84a5d418..4ead52a4 100644
--- a/third_party/blink/perf_tests/MotionMark/resources/runner/motionmark.css
+++ b/third_party/blink/perf_tests/MotionMark/resources/runner/motionmark.css
@@ -78,6 +78,10 @@
     }
 }
 
+#frame-rate-label {
+    font-size: 75%;
+}
+
 ::selection {
     background-color: black;
     color: white;
diff --git a/third_party/blink/perf_tests/MotionMark/resources/runner/motionmark.js b/third_party/blink/perf_tests/MotionMark/resources/runner/motionmark.js
index a89ccd4..6009988 100644
--- a/third_party/blink/perf_tests/MotionMark/resources/runner/motionmark.js
+++ b/third_party/blink/perf_tests/MotionMark/resources/runner/motionmark.js
@@ -29,8 +29,8 @@
         this._options = options;
         this._results = null;
         this._version = version;
-        this._targetFrameRate = options["frame-rate"] || 60;
-        this._systemFrameRate = options["system-frame-rate"] || 60;
+        this._targetFrameRate = options["frame-rate"];
+        this._systemFrameRate = options["system-frame-rate"];
         if (testData) {
             this._iterationsSamplers = testData;
             this._processData();
@@ -96,6 +96,7 @@
         var result = {};
         data[Strings.json.result] = result;
         var samples = data[Strings.json.samples];
+        const desiredFrameLength = 1000 / this._targetFrameRate;
 
         function findRegression(series, profile) {
             var minIndex = Math.round(.025 * series.length);
@@ -110,20 +111,23 @@
                 maxComplexity = series.getFieldInDatum(maxIndex, Strings.json.complexity);
             }
 
+            var frameTypeIndex = series.fieldMap[Strings.json.frameType];
             var complexityIndex = series.fieldMap[Strings.json.complexity];
             var frameLengthIndex = series.fieldMap[Strings.json.frameLength];
-            var regressionOptions = { desiredFrameLength: 1000/this._targetFrameRate };
+            var regressionOptions = { desiredFrameLength: desiredFrameLength };
             if (profile)
                 regressionOptions.preferredProfile = profile;
+
+            var regressionSamples = series.slice(minIndex, maxIndex + 1);
+            var animationSamples = regressionSamples.data.filter((sample) => sample[frameTypeIndex] == Strings.json.animationFrameType);
+            var regressionData = animationSamples.map((sample) => [ sample[complexityIndex], sample[frameLengthIndex] ]);
+
+            var regression = new Regression(regressionData, minIndex, maxIndex, regressionOptions);
             return {
                 minComplexity: minComplexity,
                 maxComplexity: maxComplexity,
-                samples: series.slice(minIndex, maxIndex + 1),
-                regression: new Regression(
-                    series.data,
-                    function (data, i) { return data[i][complexityIndex]; },
-                    function (data, i) { return data[i][frameLengthIndex]; },
-                    minIndex, maxIndex, regressionOptions)
+                samples: regressionSamples,
+                regression: regression,
             };
         }
 
@@ -168,7 +172,7 @@
         result[Strings.json.complexity][Strings.json.complexity] = calculation.complexity;
         result[Strings.json.complexity][Strings.json.measurements.stdev] = Math.sqrt(calculation.error / samples[Strings.json.complexity].length);
 
-        result[Strings.json.fps] = data.targetFPS || 60;
+        result[Strings.json.fps] = data.targetFPS;
 
         if (isRampController) {
             var timeComplexity = new Experiment;
@@ -192,6 +196,15 @@
 
                 var resample = new SampleData(regressionResult.samples.fieldMap, resampleData);
                 var bootstrapRegressionResult = findRegression(resample, predominantProfile);
+                if (bootstrapRegressionResult.regression.t2 < 0) {
+                  // A positive slope means the frame rate decreased with increased complexity (which is the expected
+                  // benavior). OTOH, a negative slope means the framerate increased as the complexity increased. This
+                  // likely means the max complexity needs to be increased. None-the-less, if the slope is negative use
+                  // the max-complexity as the computed complexity (intersection of the two lines) does not tell us
+                  // the point when the browser could not handle the complexity, rather it tells us when the framerate
+                  // increased.
+                  return bootstrapRegressionResult.maxComplexity;
+                }
                 return bootstrapRegressionResult.regression.complexity;
             }, .8);
 
@@ -419,8 +432,7 @@
     }
 };
 
-window.sectionsManager =
-{
+window.sectionsManager = {
     showSection: function(sectionIdentifier, pushState)
     {
         var sections = document.querySelectorAll("main > section");
@@ -473,19 +485,55 @@
         "time-measurement": "performance",
         "warmup-length": 2000,
         "warmup-frame-count": 30,
-        "first-frame-minimum-length": 0
+        "first-frame-minimum-length": 0,
+        "system-frame-rate": 60,
+        "frame-rate": 60,
     },
 
-    initialize: function()
+    initialize: async function()
     {
         document.title = Strings.text.title.replace("%s", Strings.version);
         document.querySelectorAll(".version").forEach(function(e) {
             e.textContent = Strings.version;
         });
         benchmarkController.addOrientationListenerIfNecessary();
+
+        this._startButton = document.getElementById("start-button");
+        this._startButton.disabled = true;
+        this._startButton.textContent = Strings.text.determininingFrameRate;
+
+        let targetFrameRate;
+        try {
+            targetFrameRate = await benchmarkController.determineFrameRate();
+        } catch (e) {
+        }
+        this.frameRateDeterminationComplete(targetFrameRate);
+    },
+    
+    frameRateDeterminationComplete: function(frameRate)
+    {
+        const frameRateLabel = document.getElementById("frame-rate-label");
+
+        let labelContent = "";
+        if (!frameRate) {
+            labelContent = Strings.text.frameRateDetectionFailure;
+            frameRate = 60;
+        } else if (frameRate != 60)
+            labelContent = Strings.text.non60FrameRate.replace("%s", frameRate);
+        else 
+            labelContent = Strings.text.usingFrameRate.replace("%s", frameRate);
+
+        frameRateLabel.innerHTML = labelContent;
+
+        this.benchmarkDefaultParameters["system-frame-rate"] = frameRate;
+        this.benchmarkDefaultParameters["frame-rate"] = frameRate;
+
+        this._startButton.textContent = Strings.text.runBenchmark;
+        this._startButton.disabled = false;
     },
 
-    determineCanvasSize: function() {
+    determineCanvasSize: function()
+    {
         var match = window.matchMedia("(max-device-width: 760px)");
         if (match.matches) {
             document.body.classList.add("small");
@@ -507,7 +555,59 @@
         document.body.classList.add("large");
     },
 
-    addOrientationListenerIfNecessary: function() {
+    determineFrameRate: function(detectionProgressElement)
+    {
+        return new Promise((resolve, reject) => {
+            let firstTimestamp;
+            let count = 0;
+
+            const averageFrameRate = function(timestamp)
+            {
+                return 1000. / ((timestamp - firstTimestamp) / count);
+            }
+
+            const finish = function(average)
+            {
+                const commonFrameRates = [15, 30, 45, 60, 90, 120, 144];
+                const distanceFromFrameRates = commonFrameRates.map(rate => {
+                    return Math.abs(Math.round(rate - average));
+                });
+
+                let shortestDistance = Number.MAX_VALUE;
+                let targetFrameRate = undefined;
+                for (let i = 0; i < commonFrameRates.length; i++) {
+                    if (distanceFromFrameRates[i] < shortestDistance) {
+                        targetFrameRate = commonFrameRates[i];
+                        shortestDistance = distanceFromFrameRates[i];
+                    }
+                }
+                if (!targetFrameRate)
+                    reject("Failed to map frame rate to a common frame rate");
+
+                resolve(targetFrameRate);
+            }
+
+            const tick = function(timestamp)
+            {
+                if (!firstTimestamp)
+                    firstTimestamp = timestamp;
+                else if (detectionProgressElement)
+                    detectionProgressElement.textContent = Math.round(averageFrameRate(timestamp));
+
+                count++;
+
+                if (count < 300)
+                    requestAnimationFrame(tick);
+                else
+                    finish(averageFrameRate(timestamp));
+            }
+
+            requestAnimationFrame(tick);
+        })
+    },
+
+    addOrientationListenerIfNecessary: function()
+    {
         if (!("orientation" in window))
             return;
 
@@ -520,21 +620,20 @@
     {
         benchmarkController.isInLandscapeOrientation = match.matches;
         if (match.matches)
-            document.querySelector(".start-benchmark p").classList.add("hidden");
+            document.querySelector(".portrait-orientation-check").classList.add("hidden");
         else
-            document.querySelector(".start-benchmark p").classList.remove("hidden");
+            document.querySelector(".portrait-orientation-check").classList.remove("hidden");
+
         benchmarkController.updateStartButtonState();
     },
 
     updateStartButtonState: function()
     {
-        document.getElementById("run-benchmark").disabled = !this.isInLandscapeOrientation;
+        document.getElementById("start-button").disabled = !this.isInLandscapeOrientation;
     },
 
     _startBenchmark: function(suites, options, frameContainerID)
     {
-        benchmarkController.determineCanvasSize();
-
         var configuration = document.body.className.match(/small|medium|large/);
         if (configuration)
             options[Strings.json.configuration] = configuration[0];
@@ -547,9 +646,11 @@
         sectionsManager.showSection("test-container");
     },
 
-    startBenchmark: function()
+    startBenchmark: async function()
     {
-        var options = this.benchmarkDefaultParameters;
+        benchmarkController.determineCanvasSize();
+
+        let options = this.benchmarkDefaultParameters;
         this._startBenchmark(Suites, options, "test-container");
     },
 
@@ -560,10 +661,10 @@
             this.addedKeyEvent = true;
         }
 
-        var dashboard = benchmarkRunnerClient.results;
-        var score = dashboard.score;
-        var confidence = "±" + (Statistics.largestDeviationPercentage(dashboard.scoreLowerBound, score, dashboard.scoreUpperBound) * 100).toFixed(2) + "%";
-        var fps = dashboard._systemFrameRate;
+        const dashboard = benchmarkRunnerClient.results;
+        const score = dashboard.score;
+        const confidence = "±" + (Statistics.largestDeviationPercentage(dashboard.scoreLowerBound, score, dashboard.scoreUpperBound) * 100).toFixed(2) + "%";
+        const fps = dashboard._targetFrameRate;
         sectionsManager.setSectionVersion("results", dashboard.version);
         sectionsManager.setSectionScore("results", score.toFixed(2), confidence, fps);
         sectionsManager.populateTable("results-header", Headers.testName, dashboard);
diff --git a/third_party/blink/perf_tests/MotionMark/resources/runner/tests.js b/third_party/blink/perf_tests/MotionMark/resources/runner/tests.js
index 33987f3b..ea6cbf8 100644
--- a/third_party/blink/perf_tests/MotionMark/resources/runner/tests.js
+++ b/third_party/blink/perf_tests/MotionMark/resources/runner/tests.js
@@ -32,35 +32,35 @@
 Suites.push(new Suite("MotionMark",
     [
         {
-            url: "master/multiply.html", // nocheck
+            url: "core/multiply.html",
             name: "Multiply"
         },
         {
-            url: "master/canvas-stage.html?pathType=arcs", // nocheck
+            url: "core/canvas-stage.html?pathType=arcs",
             name: "Canvas Arcs"
         },
         {
-            url: "master/leaves.html", // nocheck
+            url: "core/leaves.html",
             name: "Leaves"
         },
         {
-            url: "master/canvas-stage.html?pathType=linePath", // nocheck
+            url: "core/canvas-stage.html?pathType=linePath",
             name: "Paths"
         },
         {
-            url: "master/canvas-stage.html?pathType=line&lineCap=square", // nocheck
+            url: "core/canvas-stage.html?pathType=line&lineCap=square",
             name: "Canvas Lines"
         },
         {
-            url: "master/image-data.html", // nocheck
+            url: "core/image-data.html",
             name: "Images"
         },
         {
-            url: "master/design.html", // nocheck
+            url: "core/design.html",
             name: "Design"
         },
         {
-            url: "master/suits.html", // nocheck
+            url: "core/suits.html",
             name: "Suits"
         },
     ]
diff --git a/third_party/blink/perf_tests/MotionMark/resources/statistics.js b/third_party/blink/perf_tests/MotionMark/resources/statistics.js
index 14c254a..b306f2d 100644
--- a/third_party/blink/perf_tests/MotionMark/resources/statistics.js
+++ b/third_party/blink/perf_tests/MotionMark/resources/statistics.js
@@ -177,50 +177,43 @@
 };
 
 Regression = Utilities.createClass(
-    function(samples, getComplexity, getFrameLength, startIndex, endIndex, options)
+    // `samples` is [ [ complexity, frameLength ], [ complexity, frameLength ], ... ]
+    // All samples are analyzed. startIndex, endIndex are just stored for use by the caller.
+    function(samples, startIndex, endIndex, options)
     {
-        var targetFrameRate = options["frame-rate"] || 60;
-        var desiredFrameLength = options.desiredFrameLength || 1000/targetFrameRate;
-        var bestProfile;
+        const desiredFrameLength = options.desiredFrameLength;
+        var profile;
 
         if (!options.preferredProfile || options.preferredProfile == Strings.json.profiles.slope) {
-            var slope = this._calculateRegression(samples, getComplexity, getFrameLength, startIndex, endIndex, {
+            profile = this._calculateRegression(samples, {
                 shouldClip: true,
                 s1: desiredFrameLength,
                 t1: 0
             });
-            if (!bestProfile || slope.error < bestProfile.error) {
-                bestProfile = slope;
-                this.profile = Strings.json.profiles.slope;
-            }
-        }
-        if (!options.preferredProfile || options.preferredProfile == Strings.json.profiles.flat) {
-            var flat = this._calculateRegression(samples, getComplexity, getFrameLength, startIndex, endIndex, {
+            this.profile = Strings.json.profiles.slope;
+        } else if (options.preferredProfile == Strings.json.profiles.flat) {
+            profile = this._calculateRegression(samples, {
                 shouldClip: true,
                 s1: desiredFrameLength,
                 t1: 0,
                 t2: 0
             });
-
-            if (!bestProfile || flat.error < bestProfile.error) {
-                bestProfile = flat;
-                this.profile = Strings.json.profiles.flat;
-            }
+            this.profile = Strings.json.profiles.flat;
         }
 
         this.startIndex = Math.min(startIndex, endIndex);
         this.endIndex = Math.max(startIndex, endIndex);
 
-        this.complexity = bestProfile.complexity;
-        this.s1 = bestProfile.s1;
-        this.t1 = bestProfile.t1;
-        this.s2 = bestProfile.s2;
-        this.t2 = bestProfile.t2;
-        this.stdev1 = bestProfile.stdev1;
-        this.stdev2 = bestProfile.stdev2;
-        this.n1 = bestProfile.n1;
-        this.n2 = bestProfile.n2;
-        this.error = bestProfile.error;
+        this.complexity = profile.complexity;
+        this.s1 = profile.s1;
+        this.t1 = profile.t1;
+        this.s2 = profile.s2;
+        this.t2 = profile.t2;
+        this.stdev1 = profile.stdev1;
+        this.stdev2 = profile.stdev2;
+        this.n1 = profile.n1;
+        this.n2 = profile.n2;
+        this.error = profile.error;
     }, {
 
     valueAt: function(complexity)
@@ -241,11 +234,14 @@
     //
     // x is assumed to be complexity, y is frame length. Can be used for pure complexity-FPS
     // analysis or for ramp controllers since complexity monotonically decreases with time.
-    _calculateRegression: function(samples, getComplexity, getFrameLength, startIndex, endIndex, options)
+    _calculateRegression: function(samples, options)
     {
-        if (startIndex == endIndex) {
+        const complexityIndex = 0;
+        const frameLengthIndex = 1;
+
+        if (samples.length == 1) {
             // Only one sample point; we can't calculate any regression.
-            var x = getComplexity(samples, startIndex);
+            var x = samples[0][complexityIndex];
             return {
                 complexity: x,
                 s1: x,
@@ -257,17 +253,19 @@
             };
         }
 
+        // Sort by increasing complexity.
+        var sortedSamples = samples.slice().sort((a, b) => a[complexityIndex] - b[complexityIndex]);
+        
         // x is expected to increase in complexity
-        var iterationDirection = endIndex > startIndex ? 1 : -1;
-        var lowComplexity = getComplexity(samples, startIndex);
-        var highComplexity = getComplexity(samples, endIndex);
+        var lowComplexity = sortedSamples[0][complexityIndex];
+        var highComplexity = sortedSamples[samples.length - 1][complexityIndex];
+
         var a1 = 0, b1 = 0, c1 = 0, d1 = 0, h1 = 0, k1 = 0;
         var a2 = 0, b2 = 0, c2 = 0, d2 = 0, h2 = 0, k2 = 0;
 
-        // Iterate from low to high complexity
-        for (var i = startIndex; iterationDirection * (endIndex - i) > -1; i += iterationDirection) {
-            var x = getComplexity(samples, i);
-            var y = getFrameLength(samples, i);
+        for (var i = 0; i < sortedSamples.length; ++i) {
+            var x = sortedSamples[i][complexityIndex];
+            var y = sortedSamples[i][frameLengthIndex];
             a2 += 1;
             b2 += x;
             c2 += x * x;
@@ -287,9 +285,9 @@
             t2_best = t2;
             error2_best = error2;
             // Number of samples included in the first segment, inclusive of splitIndex
-            n1_best = iterationDirection * (splitIndex - startIndex) + 1;
+            n1_best = splitIndex + 1;
             // Number of samples included in the second segment
-            n2_best = iterationDirection * (endIndex - splitIndex);
+            n2_best = samples.length - splitIndex - 1;
             if (!options.shouldClip || (x_prime >= lowComplexity && x_prime <= highComplexity))
                 x_best = x_prime;
             else {
@@ -298,21 +296,21 @@
             }
         }
 
-        // Iterate from startIndex to endIndex - 1, inclusive
-        for (var i = startIndex; iterationDirection * (endIndex - i) > 0; i += iterationDirection) {
-            var x = getComplexity(samples, i);
-            var y = getFrameLength(samples, i);
+        // Iterate from 0 to n - 2, inclusive
+        for (var i = 0; i < sortedSamples.length - 1; ++i) {
+            var x = sortedSamples[i][complexityIndex];
+            var y = sortedSamples[i][frameLengthIndex];
             var xx = x * x;
             var yx = y * x;
             var yy = y * y;
-            // a1, b1, etc. is sum from startIndex to i, inclusive
+            // a1, b1, etc. is sum from 0 to i, inclusive
             a1 += 1;
             b1 += x;
             c1 += xx;
             d1 += y;
             h1 += yx;
             k1 += yy;
-            // a2, b2, etc. is sum from i+1 to endIndex, inclusive
+            // a2, b2, etc. is sum from i+1 to sortedSamples.length - 1, inclusive
             a2 -= 1;
             b2 -= x;
             c2 -= xx;
@@ -336,7 +334,7 @@
             var error1 = (k1 + a1*s1*s1 + c1*t1*t1 - 2*d1*s1 - 2*h1*t1 + 2*b1*s1*t1) || Number.MAX_VALUE;
             var error2 = (k2 + a2*s2*s2 + c2*t2*t2 - 2*d2*s2 - 2*h2*t2 + 2*b2*s2*t2) || Number.MAX_VALUE;
 
-            if (i == startIndex) {
+            if (i == 0) {
                 setBest(s1, t1, error1, s2, t2, error2, i, x_prime, x);
                 continue;
             }
@@ -345,7 +343,8 @@
                 continue;
 
             // Projected point is not between this and the next sample
-            if (x_prime > getComplexity(samples, i + iterationDirection) || x_prime < x) {
+            var nextSampleComplexity = sortedSamples[i + 1][complexityIndex];
+            if (x_prime > nextSampleComplexity || x_prime < x) {
                 // Calculate lambda, which divides the weight of this sample between the two lines
 
                 // These values remove the influence of this sample
diff --git a/third_party/blink/perf_tests/MotionMark/resources/strings.js b/third_party/blink/perf_tests/MotionMark/resources/strings.js
index 32a22c8..7a542a2 100644
--- a/third_party/blink/perf_tests/MotionMark/resources/strings.js
+++ b/third_party/blink/perf_tests/MotionMark/resources/strings.js
@@ -23,11 +23,16 @@
  * THE POSSIBILITY OF SUCH DAMAGE.
  */
 var Strings = {
-    version: "1.3",
+    version: "1.3.1",
     text: {
         testName: "Test Name",
         score: "Score",
         title: "MotionMark %s",
+        determininingFrameRate: "Detecting Frame Rate…",
+        runBenchmark: "Run Benchmark",
+        usingFrameRate: "Framerate %sfps",
+        frameRateDetectionFailure: "Failed to determine framerate, using 60fps",
+        non60FrameRate: "Framerate %sfps. If comparing browsers, be sure to <a href='about.html#set-display-fps'>set your display refresh rate to 60Hz</a>."
     },
     json: {
         version: "version",
@@ -39,11 +44,15 @@
         samples: "samples",
         dataFieldMap: "dataFieldMap",
         controller: "controller",
+        frameType: "frameType",
         time: "time",
         complexity: "complexity",
         frameLength: "frameLength",
         smoothedFrameLength: "smoothedFrameLength",
 
+        mutationFrameType: "m",
+        animationFrameType: "a",
+
         result: "result",
         configuration: "configuration",
         score: "score",
diff --git a/third_party/blink/perf_tests/MotionMark/tests/bouncing-particles/resources/bouncing-canvas-images.js b/third_party/blink/perf_tests/MotionMark/tests/bouncing-particles/resources/bouncing-canvas-images.js
index c39ec4d..733f28f 100644
--- a/third_party/blink/perf_tests/MotionMark/tests/bouncing-particles/resources/bouncing-canvas-images.js
+++ b/third_party/blink/perf_tests/MotionMark/tests/bouncing-particles/resources/bouncing-canvas-images.js
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015-2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2023 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -49,7 +49,7 @@
     initialize: function(benchmark, options)
     {
         BouncingCanvasParticlesStage.prototype.initialize.call(this, benchmark, options);
-        var imageSrc = options["imageSrc"] || "resources/yin-yang.svg";
+        var imageSrc = options["imageSrc"] || "../resources/yin-yang.svg";
         this.imageElement = document.querySelector(".hidden[src=\"" + imageSrc + "\"]");
     },
 
diff --git a/third_party/blink/perf_tests/MotionMark/tests/bouncing-particles/resources/bouncing-css-shapes.js b/third_party/blink/perf_tests/MotionMark/tests/bouncing-particles/resources/bouncing-css-shapes.js
index 19ebca0..d3048d4 100644
--- a/third_party/blink/perf_tests/MotionMark/tests/bouncing-particles/resources/bouncing-css-shapes.js
+++ b/third_party/blink/perf_tests/MotionMark/tests/bouncing-particles/resources/bouncing-css-shapes.js
@@ -44,7 +44,7 @@
 
         if (stage.blend)
             this.element.style.mixBlendMode = Stage.randomStyleMixBlendMode();
-
+        
         // Some browsers have not un-prefixed the css filter yet.
         if (stage.filter)
             Utilities.setElementPrefixedProperty(this.element, "filter", Stage.randomStyleFilter());
diff --git a/third_party/blink/perf_tests/MotionMark/tests/bouncing-particles/resources/bouncing-svg-images.js b/third_party/blink/perf_tests/MotionMark/tests/bouncing-particles/resources/bouncing-svg-images.js
index 7ae9308..e9efc4c 100644
--- a/third_party/blink/perf_tests/MotionMark/tests/bouncing-particles/resources/bouncing-svg-images.js
+++ b/third_party/blink/perf_tests/MotionMark/tests/bouncing-particles/resources/bouncing-svg-images.js
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015-2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2023 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -45,7 +45,7 @@
     initialize: function(benchmark, options)
     {
         BouncingSvgParticlesStage.prototype.initialize.call(this, benchmark, options);
-        this.imageSrc = options["imageSrc"] || "resources/yin-yang.svg";
+        this.imageSrc = options["imageSrc"] || "../resources/yin-yang.svg";
     },
 
     createParticle: function()
diff --git a/third_party/blink/perf_tests/MotionMark/tests/master/canvas-stage.html b/third_party/blink/perf_tests/MotionMark/tests/core/canvas-stage.html
similarity index 100%
rename from third_party/blink/perf_tests/MotionMark/tests/master/canvas-stage.html
rename to third_party/blink/perf_tests/MotionMark/tests/core/canvas-stage.html
diff --git a/third_party/blink/perf_tests/MotionMark/tests/master/design.html b/third_party/blink/perf_tests/MotionMark/tests/core/design.html
similarity index 97%
rename from third_party/blink/perf_tests/MotionMark/tests/master/design.html
rename to third_party/blink/perf_tests/MotionMark/tests/core/design.html
index a8246a30..0f30dfd 100644
--- a/third_party/blink/perf_tests/MotionMark/tests/master/design.html
+++ b/third_party/blink/perf_tests/MotionMark/tests/core/design.html
@@ -28,12 +28,12 @@
     <meta charset="utf-8">
     <link rel="stylesheet" type="text/css" href="../resources/stage.css">
     <style type="text/css">
-
         #stage {
             font-family: Helvetica;
             font-size: 52px;
             background-color: #313534;
         }
+
         @media (max-width: 900px) {
             #stage {
                 font-size: 40px;
@@ -56,14 +56,15 @@
         }
         table {
             position: relative;
+            top: 10%;
             width: 100%;
-            height: 100%;
+            height: 80%;
         }
         td {
             width: 33%;
         }
         tr {
-            height: 20%;
+            height: 12%;
         }
     </style>
 </head>
diff --git a/third_party/blink/perf_tests/MotionMark/tests/master/image-data.html b/third_party/blink/perf_tests/MotionMark/tests/core/image-data.html
similarity index 100%
rename from third_party/blink/perf_tests/MotionMark/tests/master/image-data.html
rename to third_party/blink/perf_tests/MotionMark/tests/core/image-data.html
diff --git a/third_party/blink/perf_tests/MotionMark/tests/master/leaves.html b/third_party/blink/perf_tests/MotionMark/tests/core/leaves.html
similarity index 100%
rename from third_party/blink/perf_tests/MotionMark/tests/master/leaves.html
rename to third_party/blink/perf_tests/MotionMark/tests/core/leaves.html
diff --git a/third_party/blink/perf_tests/MotionMark/tests/master/multiply.html b/third_party/blink/perf_tests/MotionMark/tests/core/multiply.html
similarity index 100%
rename from third_party/blink/perf_tests/MotionMark/tests/master/multiply.html
rename to third_party/blink/perf_tests/MotionMark/tests/core/multiply.html
diff --git a/third_party/blink/perf_tests/MotionMark/tests/master/resources/canvas-stage.js b/third_party/blink/perf_tests/MotionMark/tests/core/resources/canvas-stage.js
similarity index 100%
rename from third_party/blink/perf_tests/MotionMark/tests/master/resources/canvas-stage.js
rename to third_party/blink/perf_tests/MotionMark/tests/core/resources/canvas-stage.js
diff --git a/third_party/blink/perf_tests/MotionMark/tests/master/resources/canvas-tests.js b/third_party/blink/perf_tests/MotionMark/tests/core/resources/canvas-tests.js
similarity index 99%
rename from third_party/blink/perf_tests/MotionMark/tests/master/resources/canvas-tests.js
rename to third_party/blink/perf_tests/MotionMark/tests/core/resources/canvas-tests.js
index 015cf8a..3676e70 100644
--- a/third_party/blink/perf_tests/MotionMark/tests/master/resources/canvas-tests.js
+++ b/third_party/blink/perf_tests/MotionMark/tests/core/resources/canvas-tests.js
@@ -110,7 +110,7 @@
         var colors = ["#101010", "#808080", "#c0c0c0", "#101010", "#808080", "#c0c0c0", "#e01040"];
         this.color = Stage.randomElementInArray(colors);
         this.width = Math.pow(Pseudo.random(), 5) * 20 + 1;
-        this.isSplit = Pseudo.random() > 0.95;
+        this.isSplit = Pseudo.random() > 0.5;
 
         var nextPoint;
         if (stage.objects.length)
diff --git a/third_party/blink/perf_tests/MotionMark/tests/master/resources/compass.svg b/third_party/blink/perf_tests/MotionMark/tests/core/resources/compass.svg
similarity index 100%
rename from third_party/blink/perf_tests/MotionMark/tests/master/resources/compass.svg
rename to third_party/blink/perf_tests/MotionMark/tests/core/resources/compass.svg
diff --git a/third_party/blink/perf_tests/MotionMark/tests/master/resources/compass100.png b/third_party/blink/perf_tests/MotionMark/tests/core/resources/compass100.png
similarity index 100%
rename from third_party/blink/perf_tests/MotionMark/tests/master/resources/compass100.png
rename to third_party/blink/perf_tests/MotionMark/tests/core/resources/compass100.png
Binary files differ
diff --git a/third_party/blink/perf_tests/MotionMark/tests/master/resources/console.svg b/third_party/blink/perf_tests/MotionMark/tests/core/resources/console.svg
similarity index 100%
rename from third_party/blink/perf_tests/MotionMark/tests/master/resources/console.svg
rename to third_party/blink/perf_tests/MotionMark/tests/core/resources/console.svg
diff --git a/third_party/blink/perf_tests/MotionMark/tests/master/resources/console100.png b/third_party/blink/perf_tests/MotionMark/tests/core/resources/console100.png
similarity index 100%
rename from third_party/blink/perf_tests/MotionMark/tests/master/resources/console100.png
rename to third_party/blink/perf_tests/MotionMark/tests/core/resources/console100.png
Binary files differ
diff --git a/third_party/blink/perf_tests/MotionMark/tests/master/resources/contribute.svg b/third_party/blink/perf_tests/MotionMark/tests/core/resources/contribute.svg
similarity index 100%
rename from third_party/blink/perf_tests/MotionMark/tests/master/resources/contribute.svg
rename to third_party/blink/perf_tests/MotionMark/tests/core/resources/contribute.svg
diff --git a/third_party/blink/perf_tests/MotionMark/tests/master/resources/contribute100.png b/third_party/blink/perf_tests/MotionMark/tests/core/resources/contribute100.png
similarity index 100%
rename from third_party/blink/perf_tests/MotionMark/tests/master/resources/contribute100.png
rename to third_party/blink/perf_tests/MotionMark/tests/core/resources/contribute100.png
Binary files differ
diff --git a/third_party/blink/perf_tests/MotionMark/tests/master/resources/debugger.svg b/third_party/blink/perf_tests/MotionMark/tests/core/resources/debugger.svg
similarity index 100%
rename from third_party/blink/perf_tests/MotionMark/tests/master/resources/debugger.svg
rename to third_party/blink/perf_tests/MotionMark/tests/core/resources/debugger.svg
diff --git a/third_party/blink/perf_tests/MotionMark/tests/master/resources/debugger100.png b/third_party/blink/perf_tests/MotionMark/tests/core/resources/debugger100.png
similarity index 100%
rename from third_party/blink/perf_tests/MotionMark/tests/master/resources/debugger100.png
rename to third_party/blink/perf_tests/MotionMark/tests/core/resources/debugger100.png
Binary files differ
diff --git a/third_party/blink/perf_tests/MotionMark/tests/master/resources/design.js b/third_party/blink/perf_tests/MotionMark/tests/core/resources/design.js
similarity index 70%
rename from third_party/blink/perf_tests/MotionMark/tests/master/resources/design.js
rename to third_party/blink/perf_tests/MotionMark/tests/core/resources/design.js
index 91c6968..ff09468c 100644
--- a/third_party/blink/perf_tests/MotionMark/tests/master/resources/design.js
+++ b/third_party/blink/perf_tests/MotionMark/tests/core/resources/design.js
@@ -54,7 +54,11 @@
         Stage.prototype.initialize.call(this, benchmark);
 
         this._template = document.getElementById("template");
-        this._offset = this.size.subtract(Point.elementClientSize(this._template)).multiply(.5);
+        
+        const templateSize = Point.elementClientSize(this._template);
+        this._offset = this.size.subtract(templateSize).multiply(.5);
+        this._maxOffset = templateSize.height / 4;
+
         this._template.style.left = this._offset.width + "px";
         this._template.style.top = this._offset.height + "px";
 
@@ -68,7 +72,7 @@
 
         if (count < 0) {
             this._offsetIndex = Math.max(this._offsetIndex + count, 0);
-            for (var i = this._offsetIndex; i < this.testElements.length; ++i)
+            for (let i = this._offsetIndex; i < this.testElements.length; ++i)
                 this.testElements[i].style.visibility = "hidden";
 
             this._stepProgress = 1 / this._offsetIndex;
@@ -78,44 +82,43 @@
         this._offsetIndex = this._offsetIndex + count;
         this._stepProgress = 1 / this._offsetIndex;
 
-        var index = Math.min(this._offsetIndex, this.testElements.length);
-        for (var i = 0; i < index; ++i)
+        const index = Math.min(this._offsetIndex, this.testElements.length);
+        for (let i = 0; i < index; ++i)
             this.testElements[i].style.visibility = "visible";
 
         if (this._offsetIndex <= this.testElements.length)
             return;
 
-        for (var i = this.testElements.length; i < this._offsetIndex; ++i) {
-            var clone = this._template.cloneNode(true);
+        for (let i = this.testElements.length; i < this._offsetIndex; ++i) {
+            const clone = this._template.cloneNode(true);
             this.testElements.push(clone);
             this.element.insertBefore(clone, this.element.firstChild);
         }
     },
 
-    animate: function(timeDelta) {
-        var angle = Stage.dateCounterValue(this.millisecondsPerRotation);
+    animate: function(timeDelta) 
+    {
+        const angle = Stage.dateCounterValue(this.millisecondsPerRotation);
 
-        var progress = 0;
-        var stepX = Math.sin(angle) * this.particleDistanceX;
-        var stepY = Math.cos(angle) * this.particleDistanceY;
-        var x = -stepX * 3;
-        var y = -stepY * 3;
-        var gradient = this.gradients[Math.floor(angle/(Math.PI * 2)) % this.gradients.length];
-        var offset = Stage.dateCounterValue(200);
-        this._template.style.transform = "translate(" + Math.floor(x) + "px," + Math.floor(y) + "px)";
-        for (var i = 0; i < this._offsetIndex; ++i) {
-            var element = this.testElements[i];
+        const gradient = this.gradients[Math.floor(angle / (Math.PI * 2)) % this.gradients.length];
+        const offset = Stage.dateCounterValue(200);
+        const maxX = Math.sin(angle) * this._maxOffset;
+        const maxY = Math.cos(angle) * this._maxOffset;
 
-            var colorProgress = this.shadowFalloff.solve(progress);
-            var shimmer = Math.sin(offset - colorProgress);
+        let progress = 0;
+        for (let i = 0; i < this._offsetIndex; ++i) {
+            const element = this.testElements[i];
+
+            let colorProgress = this.shadowFalloff.solve(progress);
+            const shimmer = Math.sin(offset - colorProgress);
             colorProgress = Math.max(Math.min(colorProgress + Utilities.lerp(shimmer, this.shimmerAverage, this.shimmerMax), 1), 0);
-            var r = Math.round(Utilities.lerp(colorProgress, gradient[0], gradient[3]));
-            var g = Math.round(Utilities.lerp(colorProgress, gradient[1], gradient[4]));
-            var b = Math.round(Utilities.lerp(colorProgress, gradient[2], gradient[5]));
+            const r = Math.round(Utilities.lerp(colorProgress, gradient[0], gradient[3]));
+            const g = Math.round(Utilities.lerp(colorProgress, gradient[1], gradient[4]));
+            const b = Math.round(Utilities.lerp(colorProgress, gradient[2], gradient[5]));
             element.style.color = "rgb(" + r + "," + g + "," + b + ")";
 
-            x += stepX;
-            y += stepY;
+            const x = Utilities.lerp(i / this._offsetIndex, 0, maxX);
+            const y = Utilities.lerp(i / this._offsetIndex, 0, maxY);
             element.style.transform = "translate(" + Math.floor(x) + "px," + Math.floor(y) + "px)";
 
             progress += this._stepProgress;
diff --git a/third_party/blink/perf_tests/MotionMark/tests/master/resources/focus.js b/third_party/blink/perf_tests/MotionMark/tests/core/resources/focus.js
similarity index 100%
rename from third_party/blink/perf_tests/MotionMark/tests/master/resources/focus.js
rename to third_party/blink/perf_tests/MotionMark/tests/core/resources/focus.js
diff --git a/third_party/blink/perf_tests/MotionMark/tests/master/resources/image-data.js b/third_party/blink/perf_tests/MotionMark/tests/core/resources/image-data.js
similarity index 100%
rename from third_party/blink/perf_tests/MotionMark/tests/master/resources/image-data.js
rename to third_party/blink/perf_tests/MotionMark/tests/core/resources/image-data.js
diff --git a/third_party/blink/perf_tests/MotionMark/tests/master/resources/inspector.svg b/third_party/blink/perf_tests/MotionMark/tests/core/resources/inspector.svg
similarity index 100%
rename from third_party/blink/perf_tests/MotionMark/tests/master/resources/inspector.svg
rename to third_party/blink/perf_tests/MotionMark/tests/core/resources/inspector.svg
diff --git a/third_party/blink/perf_tests/MotionMark/tests/master/resources/inspector100.png b/third_party/blink/perf_tests/MotionMark/tests/core/resources/inspector100.png
similarity index 100%
rename from third_party/blink/perf_tests/MotionMark/tests/master/resources/inspector100.png
rename to third_party/blink/perf_tests/MotionMark/tests/core/resources/inspector100.png
Binary files differ
diff --git a/third_party/blink/perf_tests/MotionMark/tests/master/resources/layout.svg b/third_party/blink/perf_tests/MotionMark/tests/core/resources/layout.svg
similarity index 100%
rename from third_party/blink/perf_tests/MotionMark/tests/master/resources/layout.svg
rename to third_party/blink/perf_tests/MotionMark/tests/core/resources/layout.svg
diff --git a/third_party/blink/perf_tests/MotionMark/tests/master/resources/layout100.png b/third_party/blink/perf_tests/MotionMark/tests/core/resources/layout100.png
similarity index 100%
rename from third_party/blink/perf_tests/MotionMark/tests/master/resources/layout100.png
rename to third_party/blink/perf_tests/MotionMark/tests/core/resources/layout100.png
Binary files differ
diff --git a/third_party/blink/perf_tests/MotionMark/tests/master/resources/leaves.js b/third_party/blink/perf_tests/MotionMark/tests/core/resources/leaves.js
similarity index 97%
rename from third_party/blink/perf_tests/MotionMark/tests/master/resources/leaves.js
rename to third_party/blink/perf_tests/MotionMark/tests/core/resources/leaves.js
index dc2cf71a..e23e383f 100644
--- a/third_party/blink/perf_tests/MotionMark/tests/master/resources/leaves.js
+++ b/third_party/blink/perf_tests/MotionMark/tests/core/resources/leaves.js
@@ -112,7 +112,7 @@
         var lastPromise;
         var images = this.images;
         this.imageSrcs.forEach(function(imageSrc) {
-            var promise = this._loadImage("../master/resources/" + imageSrc + "100.png"); // nocheck
+            var promise = this._loadImage("../core/resources/" + imageSrc + "100.png");
             if (!lastPromise)
                 lastPromise = promise;
             else {
diff --git a/third_party/blink/perf_tests/MotionMark/tests/master/resources/multiply.js b/third_party/blink/perf_tests/MotionMark/tests/core/resources/multiply.js
similarity index 98%
rename from third_party/blink/perf_tests/MotionMark/tests/master/resources/multiply.js
rename to third_party/blink/perf_tests/MotionMark/tests/core/resources/multiply.js
index 67dab33..eca885a8 100644
--- a/third_party/blink/perf_tests/MotionMark/tests/master/resources/multiply.js
+++ b/third_party/blink/perf_tests/MotionMark/tests/core/resources/multiply.js
@@ -33,11 +33,9 @@
     }, {
 
     visibleCSS: [
-        ["visibility", "hidden", "visible"],
-        ["opacity", 0, 1],
         ["display", "none", "block"]
     ],
-    totalRows: 55,
+    totalRows: 71,
 
     initialize: function(benchmark, options)
     {
diff --git a/third_party/blink/perf_tests/MotionMark/tests/master/resources/particles.js b/third_party/blink/perf_tests/MotionMark/tests/core/resources/particles.js
similarity index 100%
rename from third_party/blink/perf_tests/MotionMark/tests/master/resources/particles.js
rename to third_party/blink/perf_tests/MotionMark/tests/core/resources/particles.js
diff --git a/third_party/blink/perf_tests/MotionMark/tests/master/resources/performance.svg b/third_party/blink/perf_tests/MotionMark/tests/core/resources/performance.svg
similarity index 100%
rename from third_party/blink/perf_tests/MotionMark/tests/master/resources/performance.svg
rename to third_party/blink/perf_tests/MotionMark/tests/core/resources/performance.svg
diff --git a/third_party/blink/perf_tests/MotionMark/tests/master/resources/performance100.png b/third_party/blink/perf_tests/MotionMark/tests/core/resources/performance100.png
similarity index 100%
rename from third_party/blink/perf_tests/MotionMark/tests/master/resources/performance100.png
rename to third_party/blink/perf_tests/MotionMark/tests/core/resources/performance100.png
Binary files differ
diff --git a/third_party/blink/perf_tests/MotionMark/tests/master/resources/script.svg b/third_party/blink/perf_tests/MotionMark/tests/core/resources/script.svg
similarity index 100%
rename from third_party/blink/perf_tests/MotionMark/tests/master/resources/script.svg
rename to third_party/blink/perf_tests/MotionMark/tests/core/resources/script.svg
diff --git a/third_party/blink/perf_tests/MotionMark/tests/master/resources/script100.png b/third_party/blink/perf_tests/MotionMark/tests/core/resources/script100.png
similarity index 100%
rename from third_party/blink/perf_tests/MotionMark/tests/master/resources/script100.png
rename to third_party/blink/perf_tests/MotionMark/tests/core/resources/script100.png
Binary files differ
diff --git a/third_party/blink/perf_tests/MotionMark/tests/master/resources/shortcuts.svg b/third_party/blink/perf_tests/MotionMark/tests/core/resources/shortcuts.svg
similarity index 100%
rename from third_party/blink/perf_tests/MotionMark/tests/master/resources/shortcuts.svg
rename to third_party/blink/perf_tests/MotionMark/tests/core/resources/shortcuts.svg
diff --git a/third_party/blink/perf_tests/MotionMark/tests/master/resources/shortcuts100.png b/third_party/blink/perf_tests/MotionMark/tests/core/resources/shortcuts100.png
similarity index 100%
rename from third_party/blink/perf_tests/MotionMark/tests/master/resources/shortcuts100.png
rename to third_party/blink/perf_tests/MotionMark/tests/core/resources/shortcuts100.png
Binary files differ
diff --git a/third_party/blink/perf_tests/MotionMark/tests/master/resources/standards.svg b/third_party/blink/perf_tests/MotionMark/tests/core/resources/standards.svg
similarity index 100%
rename from third_party/blink/perf_tests/MotionMark/tests/master/resources/standards.svg
rename to third_party/blink/perf_tests/MotionMark/tests/core/resources/standards.svg
diff --git a/third_party/blink/perf_tests/MotionMark/tests/master/resources/standards100.png b/third_party/blink/perf_tests/MotionMark/tests/core/resources/standards100.png
similarity index 100%
rename from third_party/blink/perf_tests/MotionMark/tests/master/resources/standards100.png
rename to third_party/blink/perf_tests/MotionMark/tests/core/resources/standards100.png
Binary files differ
diff --git a/third_party/blink/perf_tests/MotionMark/tests/master/resources/storage.svg b/third_party/blink/perf_tests/MotionMark/tests/core/resources/storage.svg
similarity index 100%
rename from third_party/blink/perf_tests/MotionMark/tests/master/resources/storage.svg
rename to third_party/blink/perf_tests/MotionMark/tests/core/resources/storage.svg
diff --git a/third_party/blink/perf_tests/MotionMark/tests/master/resources/storage100.png b/third_party/blink/perf_tests/MotionMark/tests/core/resources/storage100.png
similarity index 100%
rename from third_party/blink/perf_tests/MotionMark/tests/master/resources/storage100.png
rename to third_party/blink/perf_tests/MotionMark/tests/core/resources/storage100.png
Binary files differ
diff --git a/third_party/blink/perf_tests/MotionMark/tests/master/resources/styles.svg b/third_party/blink/perf_tests/MotionMark/tests/core/resources/styles.svg
similarity index 100%
rename from third_party/blink/perf_tests/MotionMark/tests/master/resources/styles.svg
rename to third_party/blink/perf_tests/MotionMark/tests/core/resources/styles.svg
diff --git a/third_party/blink/perf_tests/MotionMark/tests/master/resources/styles100.png b/third_party/blink/perf_tests/MotionMark/tests/core/resources/styles100.png
similarity index 100%
rename from third_party/blink/perf_tests/MotionMark/tests/master/resources/styles100.png
rename to third_party/blink/perf_tests/MotionMark/tests/core/resources/styles100.png
Binary files differ
diff --git a/third_party/blink/perf_tests/MotionMark/tests/master/resources/suits.js b/third_party/blink/perf_tests/MotionMark/tests/core/resources/suits.js
similarity index 100%
rename from third_party/blink/perf_tests/MotionMark/tests/master/resources/suits.js
rename to third_party/blink/perf_tests/MotionMark/tests/core/resources/suits.js
diff --git a/third_party/blink/perf_tests/MotionMark/tests/master/resources/timeline.svg b/third_party/blink/perf_tests/MotionMark/tests/core/resources/timeline.svg
similarity index 100%
rename from third_party/blink/perf_tests/MotionMark/tests/master/resources/timeline.svg
rename to third_party/blink/perf_tests/MotionMark/tests/core/resources/timeline.svg
diff --git a/third_party/blink/perf_tests/MotionMark/tests/master/resources/timeline100.png b/third_party/blink/perf_tests/MotionMark/tests/core/resources/timeline100.png
similarity index 100%
rename from third_party/blink/perf_tests/MotionMark/tests/master/resources/timeline100.png
rename to third_party/blink/perf_tests/MotionMark/tests/core/resources/timeline100.png
Binary files differ
diff --git a/third_party/blink/perf_tests/MotionMark/tests/master/suits.html b/third_party/blink/perf_tests/MotionMark/tests/core/suits.html
similarity index 100%
rename from third_party/blink/perf_tests/MotionMark/tests/master/suits.html
rename to third_party/blink/perf_tests/MotionMark/tests/core/suits.html
diff --git a/third_party/blink/perf_tests/MotionMark/tests/dom/leaves.html b/third_party/blink/perf_tests/MotionMark/tests/dom/leaves.html
index 3349268..e931fc3c 100644
--- a/third_party/blink/perf_tests/MotionMark/tests/dom/leaves.html
+++ b/third_party/blink/perf_tests/MotionMark/tests/dom/leaves.html
@@ -43,8 +43,8 @@
     <script src="../../resources/statistics.js"></script>
     <script src="../resources/math.js"></script>
     <script src="../resources/main.js"></script>
-    <script src="../master/resources/particles.js"></script> <!-- nocheck -->
-    <script src="../master/resources/leaves.js"></script> <!-- nocheck -->
+    <script src="../core/resources/particles.js"></script>
+    <script src="../core/resources/leaves.js"></script>
     <script src="resources/leaves.js"></script>
 </body>
 </html>
diff --git a/third_party/blink/perf_tests/MotionMark/tests/dom/multiply.html b/third_party/blink/perf_tests/MotionMark/tests/dom/multiply.html
index def555a..b5bef91 100644
--- a/third_party/blink/perf_tests/MotionMark/tests/dom/multiply.html
+++ b/third_party/blink/perf_tests/MotionMark/tests/dom/multiply.html
@@ -72,7 +72,7 @@
     <script src="../../resources/statistics.js"></script>
     <script src="../resources/math.js"></script>
     <script src="../resources/main.js"></script>
-    <script src="../master/resources/multiply.js"></script> <!-- nocheck -->
+    <script src="../core/resources/multiply.js"></script>
     <script src="resources/multiply.js"></script>
 </body>
 </html>
diff --git a/third_party/blink/perf_tests/MotionMark/tests/dom/particles.html b/third_party/blink/perf_tests/MotionMark/tests/dom/particles.html
index c1bb8783..02b0786d 100644
--- a/third_party/blink/perf_tests/MotionMark/tests/dom/particles.html
+++ b/third_party/blink/perf_tests/MotionMark/tests/dom/particles.html
@@ -42,7 +42,7 @@
     <script src="../../resources/statistics.js"></script>
     <script src="../resources/math.js"></script>
     <script src="../resources/main.js"></script>
-    <script src="../master/resources/particles.js"></script> <!-- nocheck -->
+    <script src="../core/resources/particles.js"></script>
     <script src="resources/dom-particles.js"></script>
 </body>
 </html>
diff --git a/third_party/blink/perf_tests/MotionMark/tests/master/focus.html b/third_party/blink/perf_tests/MotionMark/tests/master/focus.html
deleted file mode 100644
index ab545b7..0000000
--- a/third_party/blink/perf_tests/MotionMark/tests/master/focus.html
+++ /dev/null
@@ -1,53 +0,0 @@
-<!--
-  Copyright (C) 2015-2017 Apple Inc. All rights reserved.
-
-  Redistribution and use in source and binary forms, with or without
-  modification, are permitted provided that the following conditions
-  are met:
-  1. Redistributions of source code must retain the above copyright
-     notice, this list of conditions and the following disclaimer.
-  2. Redistributions in binary form must reproduce the above copyright
-     notice, this list of conditions and the following disclaimer in the
-     documentation and/or other materials provided with the distribution.
-
-  THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
-  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
-  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
-  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
-  THE POSSIBILITY OF SUCH DAMAGE.
--->
-<!DOCTYPE html>
-<html>
-<head>
-    <meta charset="utf-8">
-    <link rel="stylesheet" type="text/css" href="../resources/stage.css">
-    <style type="text/css">
-
-    #stage {
-        background-color: #201A1F;
-    }
-
-    #stage div {
-        position: absolute;
-        background-color: #DEDADD;
-        border-radius: 50%;
-        display: none;
-    }
-    </style>
-</head>
-<body>
-    <div id="stage"></div>
-    <script src="../../resources/strings.js"></script>
-    <script src="../../resources/extensions.js"></script>
-    <script src="../../resources/statistics.js"></script>
-    <script src="../resources/math.js"></script>
-    <script src="../resources/main.js"></script>
-    <script src="resources/focus.js"></script>
-</body>
-</html>
diff --git a/third_party/blink/perf_tests/MotionMark/tests/resources/main.js b/third_party/blink/perf_tests/MotionMark/tests/resources/main.js
index c96f02d..ca7f2a97 100644
--- a/third_party/blink/perf_tests/MotionMark/tests/resources/main.js
+++ b/third_party/blink/perf_tests/MotionMark/tests/resources/main.js
@@ -36,7 +36,8 @@
         this.sampleCount = 0;
     }, {
 
-    record: function() {
+    record: function()
+    {
         // Assume that arguments.length == this.samples.length
         for (var i = 0; i < arguments.length; i++) {
             this.samples[i][this.sampleCount] = arguments[i];
@@ -59,6 +60,11 @@
     }
 });
 
+const sampleTypeIndex = 0;
+const sampleTimeIndex = 1;
+const sampleComplexityIndex = 2;
+const sampleFrameLengthEstimateIndex = 3;
+
 Controller = Utilities.createClass(
     function(benchmark, options)
     {
@@ -66,10 +72,10 @@
         // In start() the timestamps are offset by the start timestamp
         this._startTimestamp = 0;
         this._endTimestamp = options["test-interval"];
-        this._targetFrameRate = options["frame-rate"] || 60;
+        this._targetFrameRate = options["frame-rate"];
         // Default data series: timestamp, complexity, estimatedFrameLength
         var sampleSize = options["sample-capacity"] || (this._targetFrameRate * options["test-interval"] / 1000);
-        this._sampler = new Sampler(options["series-count"] || 3, sampleSize, this);
+        this._sampler = new Sampler(options["series-count"] || 4, sampleSize, this);
         this._marks = {};
 
         this._frameLengthEstimator = new SimpleKalmanEstimator(options["kalman-process-error"], options["kalman-measurement-error"]);
@@ -81,7 +87,8 @@
         this.initialComplexity = 1;
     }, {
 
-    set isFrameLengthEstimatorEnabled(enabled) {
+    set isFrameLengthEstimatorEnabled(enabled)
+    {
         this._isFrameLengthEstimatorEnabled = enabled;
     },
 
@@ -96,18 +103,20 @@
 
     recordFirstSample: function(startTimestamp, stage)
     {
-        this._sampler.record(startTimestamp, stage.complexity(), -1);
+        this._sampler.record(Strings.json.mutationFrameType, startTimestamp, stage.complexity(), -1);
         this.mark(Strings.json.samplingStartTimeOffset, startTimestamp);
     },
 
-    mark: function(comment, timestamp, data) {
+    mark: function(comment, timestamp, data)
+    {
         data = data || {};
         data.time = timestamp;
         data.index = this._sampler.sampleCount;
         this._marks[comment] = data;
     },
 
-    containsMark: function(comment) {
+    containsMark: function(comment)
+    {
         return comment in this._marks;
     },
 
@@ -133,7 +142,7 @@
         if (this._intervalEndTimestamp) {
             var durations = [];
             for (var i = Math.max(this._intervalStartIndex, 1); i < sampleCount; ++i) {
-                durations.push(this._sampler.samples[0][i] - this._sampler.samples[0][i - 1]);
+                durations.push(this._sampler.samples[sampleTimeIndex][i] - this._sampler.samples[sampleTimeIndex][i - 1]);
             }
             var filteredDurations = this.filterOutOutliers(durations);
             if (filteredDurations.length > 0)
@@ -146,8 +155,32 @@
         return averageFrameLength;
     },
 
+    _getFrameType: function(samples, i)
+    {
+        return samples[sampleTypeIndex][i];
+    },
+
+    _getComplexity: function(samples, i)
+    {
+        return samples[sampleComplexityIndex][i];
+    },
+
+    _getFrameLength: function(samples, i)
+    {
+        return samples[sampleTimeIndex][i] - samples[sampleTimeIndex][i - 1];
+    },
+    
+    _previousFrameComplexity: function(samples, i)
+    {
+        if (i > 0)
+            return this._getComplexity(samples, i - 1);
+
+        return 0;
+    },
+
     update: function(timestamp, stage)
     {
+        const frameType = this._previousFrameComplexity(this._sampler.samples, this._sampler.sampleCount) != stage.complexity() ? Strings.json.mutationFrameType : Strings.json.animationFrameType
         var lastFrameLength = timestamp - this._previousTimestamp;
         this._previousTimestamp = timestamp;
 
@@ -158,22 +191,25 @@
                 this._frameLengthEstimator.sample(lastFrameLength);
                 frameLengthEstimate = this._frameLengthEstimator.estimate;
             }
+            this._sampler.record(frameType, timestamp, stage.complexity(), frameLengthEstimate);
         } else {
             this.registerFrameTime(lastFrameLength);
             if (this.intervalHasConcluded(timestamp)) {
-                var intervalStartTimestamp = this._sampler.samples[0][this._intervalStartIndex];
+                var intervalStartTimestamp = this._sampler.samples[sampleTimeIndex][this._intervalStartIndex];
                 intervalAverageFrameLength = this._measureAndResetInterval(timestamp);
                 if (this._isFrameLengthEstimatorEnabled) {
                     this._frameLengthEstimator.sample(intervalAverageFrameLength);
                     frameLengthEstimate = this._frameLengthEstimator.estimate;
                 }
+                this._sampler.record(frameType, timestamp, stage.complexity(), frameLengthEstimate);
+
                 didFinishInterval = true;
                 this.didFinishInterval(timestamp, stage, intervalAverageFrameLength);
                 this._frameLengthEstimator.reset();
-            }
+            } else
+                this._sampler.record(frameType, timestamp, stage.complexity(), frameLengthEstimate);
         }
 
-        this._sampler.record(timestamp, stage.complexity(), frameLengthEstimate);
         this.tune(timestamp, stage, lastFrameLength, didFinishInterval, intervalAverageFrameLength);
     },
 
@@ -217,30 +253,40 @@
             this._marks[markName].time -= this._startTimestamp;
         return this._marks;
     },
+
     _processControllerSamples: function()
     {
+        const processedSampleTypeIndex = 0;
+        const processedSampleTimeIndex = 1;
+        const processedSampleComplexityIndex = 2;
+        const processedSampleFrameLengthIndex = 3;
+        const processedSampleSmoothedFrameLengthIndex = 4;
+
         var controllerSamples = new SampleData;
-        controllerSamples.addField(Strings.json.time, 0);
-        controllerSamples.addField(Strings.json.complexity, 1);
-        controllerSamples.addField(Strings.json.frameLength, 2);
-        controllerSamples.addField(Strings.json.smoothedFrameLength, 3);
+        controllerSamples.addField(Strings.json.frameType, processedSampleTypeIndex);
+        controllerSamples.addField(Strings.json.time, processedSampleTimeIndex);
+        controllerSamples.addField(Strings.json.complexity, processedSampleComplexityIndex);
+
+        controllerSamples.addField(Strings.json.frameLength, processedSampleFrameLengthIndex);
+        controllerSamples.addField(Strings.json.smoothedFrameLength, processedSampleSmoothedFrameLengthIndex);
 
         var samples = this._sampler.samples;
-        samples[0].forEach(function(timestamp, i) {
+        samples[sampleTimeIndex].forEach(function(timestamp, i) {
             var sample = controllerSamples.createDatum();
             controllerSamples.push(sample);
 
             // Represent time in milliseconds
+            controllerSamples.setFieldInDatum(sample, Strings.json.frameType, samples[sampleTypeIndex][i]);
             controllerSamples.setFieldInDatum(sample, Strings.json.time, timestamp - this._startTimestamp);
-            controllerSamples.setFieldInDatum(sample, Strings.json.complexity, samples[1][i]);
+            controllerSamples.setFieldInDatum(sample, Strings.json.complexity, samples[sampleComplexityIndex][i]);
 
             if (i == 0)
                 controllerSamples.setFieldInDatum(sample, Strings.json.frameLength, 1000/this._targetFrameRate);
             else
-                controllerSamples.setFieldInDatum(sample, Strings.json.frameLength, timestamp - samples[0][i - 1]);
+                controllerSamples.setFieldInDatum(sample, Strings.json.frameLength, timestamp - samples[sampleTimeIndex][i - 1]);
 
-            if (samples[2][i] != -1)
-                controllerSamples.setFieldInDatum(sample, Strings.json.smoothedFrameLength, samples[2][i]);
+            if (samples[sampleFrameLengthEstimateIndex][i] != -1)
+                controllerSamples.setFieldInDatum(sample, Strings.json.smoothedFrameLength, samples[sampleFrameLengthEstimateIndex][i]);
         }, this);
 
         return controllerSamples;
@@ -298,7 +344,7 @@
 
     recordFirstSample: function(startTimestamp, stage)
     {
-        this._sampler.record(startTimestamp, stage.complexity(), -1);
+        this._sampler.record(Strings.json.mutationFrameType, startTimestamp, stage.complexity(), -1);
     },
 
     update: function(timestamp, stage)
@@ -312,7 +358,7 @@
         ++this._intervalFrameCount;
 
         if (this._intervalFrameCount < this._numberOfFramesToMeasurePerInterval) {
-            this._sampler.record(timestamp, stage.complexity(), -1);
+            this._sampler.record(Strings.json.animationFrameType, timestamp, stage.complexity(), -1);
             return;
         }
 
@@ -324,7 +370,7 @@
         tuneValue = tuneValue > 0 ? Math.floor(tuneValue) : Math.ceil(tuneValue);
         stage.tune(tuneValue);
 
-        this._sampler.record(timestamp, stage.complexity(), this._frameLengthEstimator.estimate);
+        this._sampler.record(Strings.json.mutationFrameType, timestamp, stage.complexity(), this._frameLengthEstimator.estimate);
 
         // Start the next interval.
         this._intervalFrameCount = 0;
@@ -335,9 +381,9 @@
 RampController = Utilities.createSubclass(Controller,
     function(benchmark, options)
     {
-        this.targetFPS = options["frame-rate"] || 60;
+        this.targetFPS = options["frame-rate"];
 
-        // The tier warm-up takes at most 5 seconds
+        // The tier warmup takes at most 5 seconds
         options["sample-capacity"] = (options["test-interval"] / 1000 + 5) * this.targetFPS;
         Controller.call(this, benchmark, options);
 
@@ -532,15 +578,21 @@
             return;
         }
 
-        var regression = new Regression(this._sampler.samples, this._getComplexity, this._getFrameLength,
-            this._sampler.sampleCount - 1, this._rampStartIndex, { desiredFrameLength: this.frameLengthDesired });
+        var regressionData = [];
+        for (var i = this._rampStartIndex; i < this._sampler.sampleCount; ++i) {
+            if (this._getFrameType(this._sampler.samples, i) == Strings.json.mutationFrameType)
+                continue;
+            regressionData.push([ this._getComplexity(this._sampler.samples, i), this._getFrameLength(this._sampler.samples, i) ]);
+        }
+
+        var regression = new Regression(regressionData, this._sampler.sampleCount - 1, this._rampStartIndex, { desiredFrameLength: this.frameLengthDesired });
         this._rampRegressions.push(regression);
 
         var frameLengthAtMaxComplexity = regression.valueAt(this._maximumComplexity);
         if (frameLengthAtMaxComplexity < this.frameLengthRampLowerThreshold)
             this._possibleMaximumComplexity = Math.floor(Utilities.lerp(Utilities.progressValue(this.frameLengthRampLowerThreshold, frameLengthAtMaxComplexity, this._lastTierFrameLength), this._maximumComplexity, this._lastTierComplexity));
         // If the regression doesn't fit the first segment at all, keep the minimum bound at 1
-        if ((timestamp - this._sampler.samples[0][this._sampler.sampleCount - regression.n1]) / this._currentRampLength < .25)
+        if ((timestamp - this._sampler.samples[sampleTimeIndex][this._sampler.sampleCount - regression.n1]) / this._currentRampLength < .25)
             this._possibleMinimumComplexity = 1;
 
         this._minimumComplexityEstimator.sample(this._possibleMinimumComplexity);
@@ -569,14 +621,6 @@
         this._possibleMaximumComplexity = this._maximumComplexity;
     },
 
-    _getComplexity: function(samples, i) {
-        return samples[1][i];
-    },
-
-    _getFrameLength: function(samples, i) {
-        return samples[0][i] - samples[0][i - 1];
-    },
-
     processSamples: function(results)
     {
         results[Strings.json.marks] = this._processMarks();
diff --git a/third_party/blink/perf_tests/MotionMark/tests/resources/math.js b/third_party/blink/perf_tests/MotionMark/tests/resources/math.js
index 4080493..e96a517 100644
--- a/third_party/blink/perf_tests/MotionMark/tests/resources/math.js
+++ b/third_party/blink/perf_tests/MotionMark/tests/resources/math.js
@@ -150,7 +150,7 @@
         // Derivative term is the slope of the curve at the current time.
         var D = (this._Kp * this._Td) * (e - this._eold) / h;
 
-        // The output is a PID function.
+        // The ouput is a PID function.
        return P + this._I + D;
     },
 
@@ -179,7 +179,7 @@
             // This is the second stage of the Zieglerâ€Nichols method. It measures the
             // oscillation period.
             if (typeof this._t0 == "undefined") {
-                // t is the time of the beginning of the first overshot
+                // t is the time of the begining of the first overshot
                 this._t0 = t;
                 this._Kp /= 2;
             }
diff --git a/third_party/blink/perf_tests/MotionMark/tests/simple/resources/simple-canvas-paths.js b/third_party/blink/perf_tests/MotionMark/tests/simple/resources/simple-canvas-paths.js
index b7a31e2..143630d 100644
--- a/third_party/blink/perf_tests/MotionMark/tests/simple/resources/simple-canvas-paths.js
+++ b/third_party/blink/perf_tests/MotionMark/tests/simple/resources/simple-canvas-paths.js
@@ -269,51 +269,6 @@
     }
 });
 
-CanvasSpreadSheets = Utilities.createClass(
-    function(stage) {
-        // Some good dark color for writing text in spreadsheet.
-        var dark_colors = ['#000000', '#404040', '#00008B', '#442D16', '#7E0000'];
-        // Some good light color for filling cells in spreadsheet.
-        var light_colors = ['#ADFF2F', '#ADD8E6', '#FFC0CB', '#E3C8C8', '#EBEEAF'];
-        // Possible text alignment in spreadsheet.
-        var align = ['left', 'right', 'center', 'start' , 'end']
-        this._text_color = dark_colors[Stage.randomInt(0, 5)];
-        this._fill_color = light_colors[Stage.randomInt(0, 5)];
-        this._text_align = align[Stage.randomInt(0, 5)];
-        this._start = Stage.randomPosition(stage.size)
-        this._color = Stage.randomColor();
-        this._cell_width = Stage.randomInt(90, 150);
-        this._cell_height = Stage.randomInt(20, 30);
-        this._font = Stage.randomInt(10, 40) + 'px Arial';
-        this._border_style_index = Stage.randomInt(0, 3);
-        this._color = Stage.randomColor();
-    }, {
-    draw: function(context) {
-        context.save();
-        rectPath = new Path2D();
-        rectPath.rect(this._start.x, this._start.y, this._cell_width, this._cell_height);
-        context.clip(rectPath);
-        context.beginPath();
-        context.globalAlpha = 0.5;
-        context.fillStyle = this._fill_color;
-        context.fill(rectPath);
-        context.globalAlpha = 1;
-        context.font = this._font;
-        context.fillStyle = this._text_color;
-        context.textAlign = this._text_align;
-        context.fillText("hello world", (this._start.x + this._start.x + this._cell_width)/2, this._start.y + this._cell_height);
-        if (this._border_style_index === 0) {
-            context.setLineDash([5, 5]);
-        } else if (this._border_style_index === 1) {
-            context.globalAlpha = 0.5;
-        } else {
-            context.strokeStyle = "#000000";
-        }
-        context.stroke(rectPath);
-        context.restore();
-    }
-});
-
 CanvasStroke = Utilities.createClass(
     function (stage) {
         this._object = new (Stage.randomElementInArray(this.objectTypes))(stage);
@@ -505,9 +460,6 @@
         case "ellipseFill":
             stage = new SimpleCanvasStage(CanvasEllipseFill);
             break;
-        case "spreadSheets":
-            stage = new SimpleCanvasStage(CanvasSpreadSheets);
-            break;
         case "strokes":
             stage = new SimpleCanvasStage(CanvasStroke);
             break;
diff --git a/third_party/blink/perf_tests/MotionMark/tests/simple/simple-canvas-paths.html b/third_party/blink/perf_tests/MotionMark/tests/simple/simple-canvas-paths.html
index 910ef3cc..a9145486 100644
--- a/third_party/blink/perf_tests/MotionMark/tests/simple/simple-canvas-paths.html
+++ b/third_party/blink/perf_tests/MotionMark/tests/simple/simple-canvas-paths.html
@@ -35,7 +35,7 @@
     <script src="../../resources/statistics.js"></script>
     <script src="../resources/math.js"></script>
     <script src="../resources/main.js"></script>
-    <script src="../master/resources/canvas-stage.js"></script> <!-- nocheck -->
+    <script src="../core/resources/canvas-stage.js"></script>
     <script src="resources/simple-canvas.js"></script>
     <script src="resources/simple-canvas-paths.js"></script>
 </body>
diff --git a/third_party/blink/perf_tests/MotionMark/tests/svg/suits.html b/third_party/blink/perf_tests/MotionMark/tests/svg/suits.html
index 1ea139947..a39d1d8 100644
--- a/third_party/blink/perf_tests/MotionMark/tests/svg/suits.html
+++ b/third_party/blink/perf_tests/MotionMark/tests/svg/suits.html
@@ -61,8 +61,8 @@
     <script src="../../resources/statistics.js"></script>
     <script src="../resources/math.js"></script>
     <script src="../resources/main.js"></script>
-    <script src="../master/resources/particles.js"></script> <!-- nocheck -->
-    <script src="../master/resources/suits.js"></script> <!-- nocheck -->
+    <script src="../core/resources/particles.js"></script>
+    <script src="../core/resources/suits.js"></script>
     <script src="suits.js"></script>
 </body>
 </html>
diff --git a/third_party/blink/perf_tests/MotionMark/tests/text/design-6.html b/third_party/blink/perf_tests/MotionMark/tests/text/design-6.html
index 9ce2b360..decbdd2 100644
--- a/third_party/blink/perf_tests/MotionMark/tests/text/design-6.html
+++ b/third_party/blink/perf_tests/MotionMark/tests/text/design-6.html
@@ -111,7 +111,7 @@
     <script src="../../resources/statistics.js"></script>
     <script src="../resources/math.js"></script>
     <script src="../resources/main.js"></script>
-    <script src="../master/resources/design.js"></script> <!-- nocheck -->
+    <script src="../core/resources/design.js"></script>
     <script src="design-6.js" charset="utf-8"></script>
 </body>
 </html>
diff --git a/third_party/blink/perf_tests/MotionMark/tests/text/design.html b/third_party/blink/perf_tests/MotionMark/tests/text/design.html
index 900c9aa..2de4977 100644
--- a/third_party/blink/perf_tests/MotionMark/tests/text/design.html
+++ b/third_party/blink/perf_tests/MotionMark/tests/text/design.html
@@ -101,7 +101,7 @@
     <script src="../../resources/statistics.js"></script>
     <script src="../resources/math.js"></script>
     <script src="../resources/main.js"></script>
-    <script src="../master/resources/design.js"></script> <!-- nocheck -->
+    <script src="../core/resources/design.js"></script>
     <script src="design.js" charset="utf-8"></script>
 </body>
 </html>
diff --git a/third_party/blink/perf_tests/MotionMark/version b/third_party/blink/perf_tests/MotionMark/version
new file mode 100644
index 0000000..6261a05b
--- /dev/null
+++ b/third_party/blink/perf_tests/MotionMark/version
@@ -0,0 +1 @@
+1.3.1
\ No newline at end of file
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 35a3e20..333491c 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
@@ -34,12 +34,16 @@
 //
 // The names of features in this enum should map to the identifiers (i.e. .yml
 // file names) used by the WebDX community group at the W3C
-// (https://github.com/web-platform-dx/web-features/tree/main/features).
-// Those identifiers are all lower case alphanumeric identifiers, with dashes
-// (-) as word separators. To translate those identifier names to enum names
-// here, start with a lower case 'k', then camelcase the name and remove the
-// dashes. I.e. "view-transitions" becomes kViewTransitions. More discussion
-// about WebDX feature identifier naming can be found at
+// (https://github.com/web-platform-dx/web-features/tree/main/features). Those
+// identifiers are all lower case alphanumeric identifiers, with dashes (-) as
+// word separators. To translate those identifier names to enum names,
+// capitalize the first alphabetic character in each sequence of alphabetic
+// characters, then remove dashes. I.e. "view-transitions" becomes
+// kViewTransitions, canvas-2d-color-management becomes
+// kCanvas2DColorManagement, etc. This translation should match the behavior of
+// id.title().replace("-", "") in Python or SUBSTITUTE(PROPER(id), "-", "") in
+// Google Sheets. More discussion about WebDX feature identifier naming can be
+// found at
 // https://github.com/web-platform-dx/web-features/tree/main/docs#identifiers
 //
 // In the event that you're adding a use counter for a feature that is yet to be
diff --git a/third_party/blink/renderer/build/scripts/generate_permission_element_grd.py b/third_party/blink/renderer/build/scripts/generate_permission_element_grd.py
index 655b870ec..4e31690 100644
--- a/third_party/blink/renderer/build/scripts/generate_permission_element_grd.py
+++ b/third_party/blink/renderer/build/scripts/generate_permission_element_grd.py
@@ -149,6 +149,20 @@
         output_file.write(doc.toxml(encoding='UTF-8'))
 
 
+# Generate the shortest string containing both `long` and `short` as
+# substrings. The full n-string problem is NP-complete, but we only use a crude
+# greedy heuristic.
+def superstring(long, short):
+    if long.find(short) >= 0:
+        return long
+    for i in range(len(short), 0, -1):
+        if long[-i:] == short[:i]:
+            return long + short[i:]
+        if short[-i:] == long[:i]:
+            return short + long[i:]
+    return long + short
+
+
 def generate_cpp_mapping(orderings, input_file_path, output_file_path):
     doc = parse(input_file_path)
     messages = doc.getElementsByTagName("message")
@@ -161,8 +175,7 @@
         # this code will use `pt-pt` (Portuguese from Portugal).
         custom_locale_mappings = {"en-gb": "en", "pt-pt": "pt", "zh-cn": "zh"}
 
-        langs = ''
-        lang_map = {}
+        locales = set()
         message_map = []
         for message in messages:
             message_name = message.getAttribute('name')
@@ -171,20 +184,20 @@
                 '_', 1)[1].lower().replace("_", "-")
             if locale in custom_locale_mappings:
                 locale = custom_locale_mappings[locale]
-            if locale not in lang_map:
-                # String concatenation is inefficientin Python, since strings
-                # are immutable. However, maintaining a list of chars and
-                # re-implementing find() is also unpleasant.
-                langs_idx = langs.find(locale)
-                if langs_idx < 0:
-                    lang_map[locale] = (len(langs), len(locale))
-                    langs += locale
-                else:
-                    # If locale is already a substring in the existing list,
-                    # the substring can simply be reused to save some space.
-                    lang_map[locale] = (langs_idx, len(locale))
+            # Add all locales first since iteration order is non-deterministic.
+            locales.add(locale)
             message_map.append((locale, base_message, message_name))
 
+        langs = ''
+        locales = sorted(locales, key=lambda x: (-len(x), x))
+        # Sort by length so that we add `ab-cd` before `ab` and `cd`
+        for locale in locales:
+            langs = superstring(langs, locale)
+
+        lang_map = {}
+        for locale in locales:
+            lang_map[locale] = langs.find(locale), len(locale)
+
         output_file.write(kStringMapCcPrefix)
         output_file.write(f'    "{langs}";\n')
         output_file.write(kStringMapCcMidfix)
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver.cc b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
index 5316aaa..10c84088 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
@@ -1641,9 +1641,9 @@
   }
 
   // ComputedStyles produced by OOF-interleaving (StyleEngine::
-  // UpdateStyleForOutOfFlow) have this flag set. We can not apply the style
-  // incrementally on top of this, because ComputedStyles produced by normal
-  // style recalcs should not have this flag.
+  // UpdateStyleAndLayoutTreeForOutOfFlow) have this flag set. We can not apply
+  // the style incrementally on top of this, because ComputedStyles produced by
+  // normal style recalcs should not have this flag.
   if (element->GetComputedStyle()->HasAnchorEvaluator()) {
     return false;
   }
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver_test.cc b/third_party/blink/renderer/core/css/resolver/style_resolver_test.cc
index db9024be..1b10e08 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver_test.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver_test.cc
@@ -130,16 +130,17 @@
     return style.MaxHeight();
   }
 
-  void UpdateStyleForOutOfFlow(Element& element, AtomicString try_name) {
+  void UpdateStyleAndLayoutTreeForOutOfFlow(Element& element,
+                                            AtomicString try_name) {
     ScopedCSSName* scoped_name =
         MakeGarbageCollected<ScopedCSSName>(try_name, &GetDocument());
     StyleRulePositionTry* rule =
         GetStyleEngine().GetPositionTryRule(*scoped_name);
     CHECK(rule);
-    GetStyleEngine().UpdateStyleForOutOfFlow(element, /*try_fallback_index*/ 0,
-                                             /*try_set=*/&rule->Properties(),
-                                             kNoTryTactics,
-                                             /*anchor_evaluator=*/nullptr);
+    GetStyleEngine().UpdateStyleAndLayoutTreeForOutOfFlow(
+        element, /*try_fallback_index*/ 0,
+        /*try_set=*/&rule->Properties(), kNoTryTactics,
+        /*anchor_evaluator=*/nullptr);
   }
 
   size_t GetCurrentOldStylesCount() {
@@ -3093,20 +3094,20 @@
   EXPECT_EQ(Length::Auto(), GetTop(*base_style));
   EXPECT_EQ(Length::Auto(), GetLeft(*base_style));
 
-  UpdateStyleForOutOfFlow(*target, AtomicString("--f1"));
+  UpdateStyleAndLayoutTreeForOutOfFlow(*target, AtomicString("--f1"));
   const ComputedStyle* try1 = target->GetComputedStyle();
   ASSERT_TRUE(try1);
   EXPECT_EQ(Length::Auto(), GetTop(*try1));
   EXPECT_EQ(Length::Fixed(100), GetLeft(*try1));
 
-  UpdateStyleForOutOfFlow(*target, AtomicString("--f2"));
+  UpdateStyleAndLayoutTreeForOutOfFlow(*target, AtomicString("--f2"));
   const ComputedStyle* try2 = target->GetComputedStyle();
   ASSERT_TRUE(try2);
   EXPECT_EQ(Length::Fixed(100), GetTop(*try2));
   EXPECT_EQ(Length::Auto(), GetLeft(*try2));
 
   // Shorthand should also work
-  UpdateStyleForOutOfFlow(*target, AtomicString("--f3"));
+  UpdateStyleAndLayoutTreeForOutOfFlow(*target, AtomicString("--f3"));
   const ComputedStyle* try3 = target->GetComputedStyle();
   ASSERT_TRUE(try3);
   EXPECT_EQ(Length::Fixed(50), GetTop(*try3));
@@ -3142,7 +3143,7 @@
   EXPECT_EQ(Length::Fixed(50), GetRight(*base_style));
 
   // 'inset-inline-start' should resolve to 'bottom'
-  UpdateStyleForOutOfFlow(*target, AtomicString("--f1"));
+  UpdateStyleAndLayoutTreeForOutOfFlow(*target, AtomicString("--f1"));
   const ComputedStyle* try1 = target->GetComputedStyle();
   ASSERT_TRUE(try1);
   EXPECT_EQ(Length::Fixed(50), GetTop(*try1));
@@ -3151,7 +3152,7 @@
   EXPECT_EQ(Length::Fixed(50), GetRight(*try1));
 
   // 'inset-block' with two parameters should set 'right' and then 'left'
-  UpdateStyleForOutOfFlow(*target, AtomicString("--f2"));
+  UpdateStyleAndLayoutTreeForOutOfFlow(*target, AtomicString("--f2"));
   const ComputedStyle* try2 = target->GetComputedStyle();
   ASSERT_TRUE(try2);
   EXPECT_EQ(Length::Fixed(50), GetTop(*try2));
@@ -3181,7 +3182,7 @@
   EXPECT_EQ(Length::Auto(), GetTop(*base_style));
 
   // '2em' should resolve to '40px'
-  UpdateStyleForOutOfFlow(*target, AtomicString("--f1"));
+  UpdateStyleAndLayoutTreeForOutOfFlow(*target, AtomicString("--f1"));
   const ComputedStyle* try1 = target->GetComputedStyle();
   ASSERT_TRUE(try1);
   EXPECT_EQ(Length::Fixed(40), GetTop(*try1));
@@ -3212,7 +3213,7 @@
   EXPECT_EQ(Length::Auto(), GetTop(*base_style));
 
   // 'position-try-fallbacks' applies to ::before pseudo-element.
-  UpdateStyleForOutOfFlow(*before, AtomicString("--f1"));
+  UpdateStyleAndLayoutTreeForOutOfFlow(*before, AtomicString("--f1"));
   const ComputedStyle* try1 = before->GetComputedStyle();
   ASSERT_TRUE(try1);
   EXPECT_EQ(Length::Fixed(50), GetTop(*try1));
@@ -3250,7 +3251,7 @@
   EXPECT_EQ(Length::Fixed(50), GetBottom(*base_style));
   EXPECT_EQ(Length::Fixed(50), GetRight(*base_style));
 
-  UpdateStyleForOutOfFlow(*target, AtomicString("--f1"));
+  UpdateStyleAndLayoutTreeForOutOfFlow(*target, AtomicString("--f1"));
   const ComputedStyle* try1 = target->GetComputedStyle();
   ASSERT_TRUE(try1);
   EXPECT_EQ(Length::Auto(), GetTop(*try1));
@@ -3258,7 +3259,7 @@
   EXPECT_EQ(Length::Fixed(50), GetBottom(*try1));
   EXPECT_EQ(Length::Fixed(50), GetRight(*try1));
 
-  UpdateStyleForOutOfFlow(*target, AtomicString("--f2"));
+  UpdateStyleAndLayoutTreeForOutOfFlow(*target, AtomicString("--f2"));
   const ComputedStyle* try2 = target->GetComputedStyle();
   ASSERT_TRUE(try2);
   EXPECT_EQ(Length::Fixed(50), GetTop(*try2));
@@ -3266,7 +3267,7 @@
   EXPECT_EQ(Length::Fixed(50), GetBottom(*try2));
   EXPECT_EQ(Length::Fixed(50), GetRight(*try2));
 
-  UpdateStyleForOutOfFlow(*target, AtomicString("--f3"));
+  UpdateStyleAndLayoutTreeForOutOfFlow(*target, AtomicString("--f3"));
   const ComputedStyle* try3 = target->GetComputedStyle();
   ASSERT_TRUE(try3);
   EXPECT_EQ(Length::Fixed(50), GetTop(*try3));
@@ -3298,7 +3299,7 @@
     EXPECT_EQ(Length::Auto(), GetTop(*base_style));
     EXPECT_EQ(Length::Auto(), GetLeft(*base_style));
 
-    UpdateStyleForOutOfFlow(*target, AtomicString("--foo"));
+    UpdateStyleAndLayoutTreeForOutOfFlow(*target, AtomicString("--foo"));
     const ComputedStyle* fallback = target->GetComputedStyle();
     ASSERT_TRUE(fallback);
     EXPECT_EQ(Length::Fixed(100), GetTop(*fallback));
@@ -3314,7 +3315,7 @@
     EXPECT_EQ(Length::Auto(), GetTop(*base_style));
     EXPECT_EQ(Length::Auto(), GetLeft(*base_style));
 
-    UpdateStyleForOutOfFlow(*target, AtomicString("--bar"));
+    UpdateStyleAndLayoutTreeForOutOfFlow(*target, AtomicString("--bar"));
     const ComputedStyle* fallback = target->GetComputedStyle();
     ASSERT_TRUE(fallback);
     ASSERT_TRUE(fallback);
diff --git a/third_party/blink/renderer/core/css/style_engine.cc b/third_party/blink/renderer/core/css/style_engine.cc
index cf4f497..18cfa5a 100644
--- a/third_party/blink/renderer/core/css/style_engine.cc
+++ b/third_party/blink/renderer/core/css/style_engine.cc
@@ -3691,6 +3691,19 @@
   RecalcStyleForContainer(container, change);
 }
 
+void StyleEngine::PostInterleavedRecalcUpdate(
+    const Element& interleaving_root) {
+  // Update quotes only if there are any scopes marked dirty.
+  if (StyleContainmentScopeTree* tree = GetStyleContainmentScopeTree()) {
+    tree->UpdateQuotes();
+  }
+  GetDocument().GetLayoutView()->UpdateCountersAfterStyleChange(
+      interleaving_root.GetLayoutObject());
+  GetDocument().InvalidatePendingSVGResources();
+  GetDocument().UpdateScrollMarkerGroupRelations();
+  GetDocument().UpdateScrollMarkerGroupToScrollableAreasMap();
+}
+
 void StyleEngine::UpdateStyleAndLayoutTreeForSizeContainer(
     Element& container,
     const LogicalSize& logical_size,
@@ -3778,10 +3791,6 @@
     RebuildLayoutTree(&container);
   }
 
-  // Update quotes only if there are any scopes marked dirty.
-  if (StyleContainmentScopeTree* tree = GetStyleContainmentScopeTree()) {
-    tree->UpdateQuotes();
-  }
   if (container == GetDocument().documentElement()) {
     // If the container is the root element, there may be body styles which have
     // changed as a result of the new container query evaluation, and if
@@ -3789,14 +3798,11 @@
     // styles.
     GetStyleResolver().PropagateStyleToViewport();
   }
-  GetDocument().GetLayoutView()->UpdateCountersAfterStyleChange(
-      container.GetLayoutObject());
-  GetDocument().InvalidatePendingSVGResources();
-  GetDocument().UpdateScrollMarkerGroupRelations();
-  GetDocument().UpdateScrollMarkerGroupToScrollableAreasMap();
+
+  PostInterleavedRecalcUpdate(container);
 }
 
-void StyleEngine::UpdateStyleForOutOfFlow(
+void StyleEngine::UpdateStyleAndLayoutTreeForOutOfFlow(
     Element& element,
     std::optional<wtf_size_t> try_fallback_index,
     const CSSPropertyValueSet* try_set,
@@ -3807,6 +3813,7 @@
 
   base::AutoReset<bool> pt_recalc(&in_position_try_style_recalc_, true);
 
+  NthIndexCache nth_index_cache(GetDocument());
   UpdateViewportSize();
 
   StyleRecalcContext style_recalc_context =
@@ -3833,6 +3840,21 @@
     style_recalc_root_.Update(nullptr, &element);
     RecalcStyle(change, style_recalc_context);
   }
+  if (NeedsLayoutTreeRebuild()) {
+    if (layout_tree_rebuild_root_.GetRootNode()->IsDocumentNode()) {
+      // Avoid traversing from outside the OOF root. We know none of the
+      // elements outside the subtree should be marked dirty in this pass, but
+      // we may have fallen back to the document root.
+      layout_tree_rebuild_root_.Clear();
+      layout_tree_rebuild_root_.Update(nullptr, &element);
+    } else {
+      DCHECK(FlatTreeTraversal::ContainsIncludingPseudoElement(
+          element, *layout_tree_rebuild_root_.GetRootNode()));
+    }
+    RebuildLayoutTree(&element);
+  }
+
+  PostInterleavedRecalcUpdate(element);
 }
 
 StyleRulePositionTry* StyleEngine::GetPositionTryRule(
@@ -4234,7 +4256,7 @@
 }
 
 bool StyleEngine::MarkStyleDirtyAllowed() const {
-  if (GetDocument().InStyleRecalc() || InContainerQueryStyleRecalc()) {
+  if (GetDocument().InStyleRecalc() || InInterleavedStyleRecalc()) {
     return allow_mark_style_dirty_from_recalc_;
   }
   return !InRebuildLayoutTree();
diff --git a/third_party/blink/renderer/core/css/style_engine.h b/third_party/blink/renderer/core/css/style_engine.h
index 31a87b5..1178883 100644
--- a/third_party/blink/renderer/core/css/style_engine.h
+++ b/third_party/blink/renderer/core/css/style_engine.h
@@ -683,11 +683,13 @@
   // a @position-try rule. The specified TryTacticList will cause
   // CSSFlipRevertValues to appear in the try-tactics layer (see
   // OutOfFlowData::try_tactics_set_).
-  void UpdateStyleForOutOfFlow(Element& element,
-                               std::optional<wtf_size_t> try_fallback_index,
-                               const CSSPropertyValueSet* try_set,
-                               const TryTacticList&,
-                               AnchorEvaluator*);
+  void UpdateStyleAndLayoutTreeForOutOfFlow(
+      Element& element,
+      std::optional<wtf_size_t> try_fallback_index,
+      const CSSPropertyValueSet* try_set,
+      const TryTacticList&,
+      AnchorEvaluator*);
+  void PostInterleavedRecalcUpdate(const Element& interleaving_root);
   StyleRulePositionTry* GetPositionTryRule(const ScopedCSSName&);
   void RecalcStyle();
 
@@ -702,6 +704,9 @@
   bool InPositionTryStyleRecalc() const {
     return in_position_try_style_recalc_;
   }
+  bool InInterleavedStyleRecalc() const {
+    return InContainerQueryStyleRecalc() || InPositionTryStyleRecalc();
+  }
   void SetInScrollMarkersAttachment(bool in_scroll_markers_attachment) {
     DCHECK(!in_scroll_markers_attachment_ || !in_scroll_markers_attachment);
     in_scroll_markers_attachment_ = in_scroll_markers_attachment;
@@ -714,9 +719,9 @@
   // elements can't be recalc roots.
   //
   // See StyleEngine::UpdateStyleAndLayoutTreeForSizeContainer.
-  // See StyleEngine::UpdateStyleForOutOfFlow.
+  // See StyleEngine::UpdateStyleAndLayoutTreeForOutOfFlow.
   Element* GetInterleavingRecalcRoot() const {
-    if (InContainerQueryStyleRecalc() || InPositionTryStyleRecalc()) {
+    if (InInterleavedStyleRecalc()) {
       // During interleaved style recalc, the recalc root is either set
       // to the interleaving root (always an Element), or nullptr (if it's
       // a PseudoElement).
diff --git a/third_party/blink/renderer/core/dom/container_node.cc b/third_party/blink/renderer/core/dom/container_node.cc
index 2c5cbdc..d7e7721 100644
--- a/third_party/blink/renderer/core/dom/container_node.cc
+++ b/third_party/blink/renderer/core/dom/container_node.cc
@@ -1969,15 +1969,13 @@
   }
   if (SoftNavigationHeuristics* heuristics =
           window->GetSoftNavigationHeuristics()) {
+    // When a child node, which is an HTML-element, is modified within a parent
+    // (added, moved, etc), mark that child as modified by soft navigation.
+    // Otherwise, if the child is not an HTML-element, mark the parent instead.
     // TODO(crbug.com/1521100): This does not filter out updates from isolated
     // worlds. Should it?
-    if (heuristics->ModifiedDOM()) {
-      if (inserted_node.IsHTMLElement()) {
-        inserted_node.SetIsModifiedBySoftNavigation();
-      } else {
-        SetIsModifiedBySoftNavigation();
-      }
-    }
+    Node* updated_node = inserted_node.IsHTMLElement() ? &inserted_node : this;
+    heuristics->ModifiedDOM(updated_node);
   }
 }
 
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 1169cff..07218c2 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -9423,8 +9423,7 @@
 
 bool Document::InStyleRecalc() const {
   return lifecycle_.GetState() == DocumentLifecycle::kInStyleRecalc ||
-         style_engine_->InContainerQueryStyleRecalc() ||
-         style_engine_->InPositionTryStyleRecalc() ||
+         style_engine_->InInterleavedStyleRecalc() ||
          style_engine_->InEnsureComputedStyle();
 }
 
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index 0a42bf6..c55615d 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -4045,8 +4045,7 @@
     // may be that an update detected in the previous pass would no longer be
     // necessary if the animated property flipped back to the old style with no
     // change as the result.
-    DCHECK(GetDocument().GetStyleEngine().InContainerQueryStyleRecalc() ||
-           GetDocument().GetStyleEngine().InPositionTryStyleRecalc() ||
+    DCHECK(GetDocument().GetStyleEngine().InInterleavedStyleRecalc() ||
            PostStyleUpdateScope::InPendingPseudoUpdate() ||
            element_animations->CssAnimations().PendingUpdate().IsEmpty());
     element_animations->CssAnimations().ClearPendingUpdate();
@@ -4286,11 +4285,11 @@
   }
 
   StyleRecalcContext child_recalc_context = local_style_recalc_context;
-  // If we're in StyleEngine::UpdateStyleForOutOfFlow, then anchor_evaluator
-  // may be non-nullptr to allow evaluation of anchor() and anchor-size()
-  // queries, and the try sets may be non-nullptr if we're attempting
-  // some position option [1]. These are only supposed to apply to the
-  // interleaving root itself (i.e. the out-of-flow element being laid out),
+  // If we're in StyleEngine::UpdateStyleAndLayoutTreeForOutOfFlow, then
+  // anchor_evaluator may be non-nullptr to allow evaluation of anchor() and
+  // anchor-size() queries, and the try sets may be non-nullptr if we're
+  // attempting some position option [1]. These are only supposed to apply to
+  // the interleaving root itself (i.e. the out-of-flow element being laid out),
   // and not to descendants.
   //
   // [1] https://drafts.csswg.org/css-anchor-position-1/#fallback
@@ -4793,9 +4792,10 @@
             .SetContainerQueryEvaluator(nullptr);
       } else if (old_style) {
         if (style_recalc_context.anchor_evaluator == nullptr) {
-          // position-try-fallbacks are only applied for UpdateStyleForOutOfFlow
-          // during layout, so we need to reset the anchored(fallback) state to
-          // no fallback for normal style recalc.
+          // position-try-fallbacks are only applied for
+          // UpdateStyleAndLayoutTreeForOutOfFlow during layout, so we need to
+          // reset the anchored(fallback) state to no fallback for normal style
+          // recalc.
           child_change = evaluator->ApplyAnchoredChanges(
               child_change, /*try_fallback_index=*/std::nullopt);
         }
diff --git a/third_party/blink/renderer/core/dom/node.cc b/third_party/blink/renderer/core/dom/node.cc
index 66009173..66867436 100644
--- a/third_party/blink/renderer/core/dom/node.cc
+++ b/third_party/blink/renderer/core/dom/node.cc
@@ -1832,15 +1832,9 @@
 }
 
 void Node::DetachLayoutTree(bool performing_reattach) {
-  // Re-attachment is not generally allowed from PositionTryStyleRecalc, but
-  // computing style for display:none pseudo elements will insert a pseudo
-  // element, compute the style, and remove it again, which includes a
-  // DetachLayoutTree().
   DCHECK(GetDocument().Lifecycle().StateAllowsDetach() ||
-         GetDocument().GetStyleEngine().InContainerQueryStyleRecalc() ||
-         GetDocument().GetStyleEngine().InScrollMarkersAttachment() ||
-         (GetDocument().GetStyleEngine().InPositionTryStyleRecalc() &&
-          IsPseudoElement() && !GetLayoutObject()));
+         GetDocument().GetStyleEngine().InInterleavedStyleRecalc() ||
+         GetDocument().GetStyleEngine().InScrollMarkersAttachment());
   DCHECK(!performing_reattach ||
          GetDocument().GetStyleEngine().InRebuildLayoutTree() ||
          GetDocument().GetStyleEngine().InScrollMarkersAttachment());
diff --git a/third_party/blink/renderer/core/frame/ad_tracker_test.cc b/third_party/blink/renderer/core/frame/ad_tracker_test.cc
index 3fe72d5e..353b568 100644
--- a/third_party/blink/renderer/core/frame/ad_tracker_test.cc
+++ b/third_party/blink/renderer/core/frame/ad_tracker_test.cc
@@ -1826,6 +1826,78 @@
   ad_document.Complete("<body></body>");
 }
 
+// Tests a scenario where a non-subresource-filter-flagged script redirects to a
+// subresource-filter-flagged URL, and this script creates an iframe.
+//
+// This test specifically verifies the *current* behavior affected by
+// crbug.com/417756984. Due to this bug, the ad-tagging decision for the
+// iframe is incorrectly based on the script's *initial*
+// non-subresource-filter-flagged URL. Consequently, this test expects the
+// iframe to NOT be ad-tagged.
+//
+// Once the bug is fixed, the iframe should be ad-tagged, and its ad script
+// ancestry should contain one script.
+TEST_F(
+    AdTrackerSimTest,
+    AdScriptAncestry_ScriptRedirectedFromNonFilterlistedUrlToFilterlistedUrl) {
+  String ad_script_url = "https://example.com/script.js?ad=true";
+
+  SimRequest::Params params;
+  params.redirect_url = ad_script_url;
+
+  String redirect_from_script_url =
+      "https://example.com/redirect-from-script.js";
+  String ad_document_url = "https://example.com/ad_document.html";
+
+  // Scenario:
+  // 1. A script (redirect_from_script_url) is loaded directly in the main
+  //    frame.
+  // 2. This script gets redirected to a subresource-filter-flagged URL
+  //    (ad_script_url).
+  // 3. The script (ad_script_url) then creates an iframe (child_frame).
+  SimSubresourceRequest ad_script(ad_script_url, "text/javascript");
+  SimSubresourceRequest redirect_from_script(redirect_from_script_url,
+                                             "text/javascript", params);
+
+  SimRequest ad_document(ad_document_url, "text/html");
+
+  main_resource_->Complete(R"HTML(
+    <body>
+      <script src="https://example.com/redirect-from-script.js"></script>
+    </body>
+  )HTML");
+
+  ad_script.Complete(R"SCRIPT(
+    const iframe = document.createElement('iframe');
+    iframe.src = 'ad_document.html';
+    document.body.appendChild(iframe);
+  )SCRIPT");
+  base::RunLoop().RunUntilIdle();
+
+  // Current behavior (due to crbug.com/417756984):
+  // The frame is NOT ad-tagged, as the ad-tagging decision is incorrectly based
+  // on the creation script's initial URL.
+  //
+  // Expected behavior (post-fix):
+  // The frame is ad-tagged, as its creation script's final URL matches the
+  // filterlist.
+  //
+  // TODO: Update the expectations once the bug is fixed.
+  // EXPECT_TRUE(child_frame->IsFrameCreatedByAdScript());
+  // EXPECT_EQ(ad_tracker_->last_ad_script_ancestry().size(), 1u);
+  auto* child_frame =
+      To<LocalFrame>(GetDocument().GetFrame()->Tree().FirstChild());
+  EXPECT_FALSE(child_frame->IsFrameCreatedByAdScript());
+
+  EXPECT_EQ(ad_tracker_->last_ad_script_ancestry().size(), 0u);
+
+  EXPECT_FALSE(ad_tracker_->RequestWithUrlTaggedAsAd(redirect_from_script_url));
+  EXPECT_TRUE(ad_tracker_->RequestWithUrlTaggedAsAd(ad_script_url));
+
+  // Clean up for SimTest expectations.
+  ad_document.Complete("<body></body>");
+}
+
 // Tests that `IsAdScriptInStack` returns the correct ad script ancestry when
 // the final ad frame is created through script execution originating from an
 // initial subresource-filter-flagged ad script. The stack ad script for this
@@ -2238,7 +2310,7 @@
 
   String redirect_from_script_url =
       "https://example.com/redirect-from-script.js?ad=true";
-  String ad_script_url = "https://example.com/script.js?ad=true";
+  String ad_script_url = "https://example.com/ad_script.js";
   String ad_document_url = "https://example.com/ad_document.html";
 
   // Scenario:
@@ -2256,7 +2328,7 @@
 
   main_resource_->Complete(R"HTML(
     <body>
-      <script src="script.js?ad=true"></script>
+      <script src="ad_script.js"></script>
     </body>
   )HTML");
 
@@ -2277,6 +2349,12 @@
   // There is one ad script in the ancestry. The resource request's initial
   // ad-tagged status propagates to the final URL after the redirect, so the
   // script does not require further ancestor attribution.
+  //
+  // Note: due to crbug.com/417756984, the real reason for this 'one script'
+  // outcome is because the ancestor attribution decision is erroneously
+  // dictated only by the script's initial, filterlisted URL. Coincidentally,
+  // this bug's outcome aligns with the intended behavior for this specific
+  // scenario.
   auto* child_frame =
       To<LocalFrame>(GetDocument().GetFrame()->Tree().FirstChild());
   EXPECT_TRUE(child_frame->IsFrameCreatedByAdScript());
@@ -2291,6 +2369,91 @@
   ad_document.Complete("<body></body>");
 }
 
+// Tests a scenario where a transitively added, non-subresource-filter-flagged
+// script redirects to a subresource-filter-flagged URL, and this script creates
+// an iframe.
+//
+// This test specifically verifies the *current* behavior affected by
+// crbug.com/417756984. Due to this bug, the ad script ancestor decision is
+// incorrectly based on the script's *initial* non-subresource-filter-flagged
+// URL. This results in ancestor attribution, and the frame's creation ad script
+// ancestry includes two scripts.
+//
+// Once the bug is fixed, the frame's creation ad script ancestry should contain
+// one script. Ancestor attribution should not occur for the redirecting script
+// because its final URL matches the filterlist.
+TEST_F(
+    AdTrackerSimTest,
+    AdScriptAncestry_TransitiveScriptRedirectedFromNonFilterlistedUrlToFilterlistedUrl) {
+  String final_ad_script_url = "https://example.com/script.js?ad=true";
+
+  SimRequest::Params params;
+  params.redirect_url = final_ad_script_url;
+
+  String redirect_from_script_url =
+      "https://example.com/redirect-from-script.js";
+  String ad_script_url = "https://example.com/ad_script.js";
+  String ad_document_url = "https://example.com/ad_document.html";
+
+  // Scenario:
+  // 1. An ad script (ad_script_url) is loaded directly in the main frame.
+  // 2. This ad script loads another script (redirect_from_script_url), which
+  //    gets redirected to a subresource-filter-flagged URL
+  //    (final_ad_script_url).
+  // 3. The final script (final_ad_script_url) creates an iframe (child_frame).
+  SimSubresourceRequest ad_script(ad_script_url, "text/javascript");
+  SimSubresourceRequest final_ad_script(final_ad_script_url, "text/javascript");
+  SimSubresourceRequest redirect_from_script(redirect_from_script_url,
+                                             "text/javascript", params);
+
+  SimRequest ad_document(ad_document_url, "text/html");
+
+  main_resource_->Complete(R"HTML(
+    <body>
+      <script src="ad_script.js"></script>
+    </body>
+  )HTML");
+
+  ad_script.Complete(R"SCRIPT(
+    const script = document.createElement('script');
+    script.src = 'redirect-from-script.js';
+    document.body.appendChild(script);
+  )SCRIPT");
+  base::RunLoop().RunUntilIdle();
+
+  final_ad_script.Complete(R"SCRIPT(
+    const iframe = document.createElement('iframe');
+    iframe.src = 'ad_document.html';
+    document.body.appendChild(iframe);
+  )SCRIPT");
+  base::RunLoop().RunUntilIdle();
+
+  // Current behavior (due to crbug.com/417756984):
+  // There are two ad scripts in the ancestry. Ancestor attribution occurs
+  // because the decision is erroneously dictated by the script's initial,
+  // non-filterlisted URL.
+  //
+  // Expected behavior (post-fix):
+  // The frame's creation ad script ancestry contains one script. Ancestor
+  // attribution does not occur because the transitive script's final URL
+  // matches the filterlist.
+  //
+  // TODO: Update expectations once the bug is fixed.
+  // EXPECT_EQ(ad_tracker_->last_ad_script_ancestry().size(), 1u);
+  auto* child_frame =
+      To<LocalFrame>(GetDocument().GetFrame()->Tree().FirstChild());
+  EXPECT_TRUE(child_frame->IsFrameCreatedByAdScript());
+
+  EXPECT_EQ(ad_tracker_->last_ad_script_ancestry().size(), 2u);
+
+  EXPECT_TRUE(ad_tracker_->RequestWithUrlTaggedAsAd(ad_script_url));
+  EXPECT_TRUE(ad_tracker_->RequestWithUrlTaggedAsAd(redirect_from_script_url));
+  EXPECT_TRUE(ad_tracker_->RequestWithUrlTaggedAsAd(final_ad_script_url));
+
+  // Clean up for SimTest expectations.
+  ad_document.Complete("<body></body>");
+}
+
 // Tests a scenario where the same vanilla script is loaded from two different
 // ancestor ad scripts. The vanilla script is designed to create an ad iframe
 // only on its second execution.
diff --git a/third_party/blink/renderer/core/layout/depth_ordered_layout_object_list.cc b/third_party/blink/renderer/core/layout/depth_ordered_layout_object_list.cc
index 448ac8f3..0bbfca9 100644
--- a/third_party/blink/renderer/core/layout/depth_ordered_layout_object_list.cc
+++ b/third_party/blink/renderer/core/layout/depth_ordered_layout_object_list.cc
@@ -57,7 +57,7 @@
   // We are allowed to insert/remove orthogonal writing mode roots during
   // layout for interleaved style recalcs, but only when these roots are fully
   // managed by LayoutNG.
-  return object.GetDocument().GetStyleEngine().InContainerQueryStyleRecalc();
+  return object.GetDocument().GetStyleEngine().InInterleavedStyleRecalc();
 }
 
 }  // namespace
diff --git a/third_party/blink/renderer/core/layout/flex/flex_layout_algorithm.cc b/third_party/blink/renderer/core/layout/flex/flex_layout_algorithm.cc
index a5f9cff..fcf819e 100644
--- a/third_party/blink/renderer/core/layout/flex/flex_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/flex/flex_layout_algorithm.cc
@@ -1336,6 +1336,24 @@
   LogicalSize available_size = ChildAvailableSize();
   LogicalSize percentage_size = child_percentage_size_;
 
+  // If we are balancing with a minimum line-count, divide the cross-axis
+  // available-space if definite.
+  if (balance_min_line_count_) {
+    const LayoutUnit gap_size =
+        (*balance_min_line_count_ - 1) * gap_between_lines_;
+    if (is_column_) {
+      if (available_size.inline_size != kIndefiniteSize) {
+        available_size.inline_size =
+            (available_size.inline_size - gap_size) / *balance_min_line_count_;
+      }
+    } else {
+      if (available_size.block_size != kIndefiniteSize) {
+        available_size.block_size =
+            (available_size.block_size - gap_size) / *balance_min_line_count_;
+      }
+    }
+  }
+
   if (is_column_) {
     if (override_inline_size) {
       DCHECK(!line_cross_size)
diff --git a/third_party/blink/renderer/core/layout/flex/flex_line_breaker.cc b/third_party/blink/renderer/core/layout/flex/flex_line_breaker.cc
index 18e8c06..2e05fd7 100644
--- a/third_party/blink/renderer/core/layout/flex/flex_line_breaker.cc
+++ b/third_party/blink/renderer/core/layout/flex/flex_line_breaker.cc
@@ -62,8 +62,8 @@
 };
 
 struct ScoreContext {
-  const ScoreUnit line_break_size;
   const ScoreUnit gap_between_items;
+  ScoreUnit line_break_size;
   Vector<ScoreUnit> sums;
   Vector<ScoreData> scores;
 };
@@ -138,6 +138,126 @@
   return data.best_score;
 }
 
+// Using the prefix-sums array returns how many flex-lines would result with
+// greedy packing given `line_break_size`.
+wtf_size_t GreedyLineCount(const ScoreContext& ctx, ScoreUnit line_break_size) {
+  const auto begin = ctx.sums.begin();
+  const auto end = ctx.sums.end();
+  auto it = begin;
+  ScoreUnit previous_sum = kZero;
+  wtf_size_t line_count = 1u;
+  for (;;) {
+    // Get the first value that has a size *greater* than the breakpoint.
+    auto next = std::upper_bound(
+        it, end, previous_sum + line_break_size + ctx.gap_between_items);
+
+    // Check if we are at the end, there is no additional line.
+    if (next == end) {
+      break;
+    }
+
+    // `next` is past our breakpoint. There are two scenarios:
+    //  - We are a single item which exceeds the line-break size, don't
+    //    backtrack for this case.
+    //  - There is more than one item, backtrack one item so the content fits
+    //    on the line.
+    const bool is_single_item =
+        ((line_count == 1 ? 1 : 0) + std::distance(it, next)) <= 1;
+    it = is_single_item ? next : std::prev(next);
+    previous_sum = *it;
+
+    // Only increment our line-count if we have content following `it`.
+    if (std::next(it) != end) {
+      ++line_count;
+    }
+  }
+  return line_count;
+}
+
+// Applies the `min_line_count` by:
+//  - Bisecting the `ctx.line_break_size` to try and achieve the minimum.
+//  - Then (if above fails) infinitesimally increasing the size of certain
+//    items so they create additional lines.
+// Returns the new line-break size.
+wtf_size_t ApplyMinLineCount(const wtf_size_t min_line_count,
+                             ScoreContext& ctx) {
+  // Bisect our `ctx.line_break_size` to try and achieve `min_line_count`.
+  {
+    // We can clamp `line_break_size` to the maximum size of the items. This is
+    // important when we pass `LayoutUnit::Max()` into the line-break for
+    // column flexboxes.
+    ScoreUnit low = kZero;
+    ScoreUnit high =
+        std::min(ctx.line_break_size, ctx.sums.back() - ctx.gap_between_items);
+
+    while (low < high) {
+      const ScoreUnit midpoint = low + (high - low) / 2;
+      if (GreedyLineCount(ctx, midpoint) > min_line_count) {
+        low = midpoint + 1u;
+      } else {
+        high = midpoint;
+      }
+    }
+    ctx.line_break_size = high;
+  }
+
+  // Update our line-count based on our new line-break size, and return if
+  // we've reached the minimum.
+  wtf_size_t line_count = GreedyLineCount(ctx, ctx.line_break_size);
+  if (line_count >= min_line_count) {
+    return line_count;
+  }
+
+  // After running the bisection, we can still be under the minimum. For
+  // example: if we have 4 identical items in a 2x2 grid, there is no
+  // line-break size which will achieve exactly 3 lines.
+  //
+  // In order to solve this we "fudge" the sizes of "perfectly fitting" items,
+  // e.g. items which are exactly are on the end of a line, so that they shift
+  // down one line.
+  Vector<wtf_size_t> perfect_fit_indices;
+  {
+    ScoreUnit previous = kZero;
+    bool line_has_content = false;
+    for (wtf_size_t i = 0; i < ctx.sums.size(); ++i) {
+      const ScoreUnit line_size =
+          ctx.sums[i] - previous - ctx.gap_between_items;
+
+      // Check if the current item fits exactly.
+      if (line_size == ctx.line_break_size) {
+        perfect_fit_indices.push_back(i);
+      }
+
+      if (line_size > ctx.line_break_size) {
+        previous = line_has_content ? ctx.sums[i - 1] : ctx.sums[i];
+        line_has_content = false;
+        continue;
+      }
+
+      line_has_content = true;
+    }
+  }
+
+  // Next increase the size of each of our "perfect-fit" items so they get
+  // pushed to the next line.
+  //
+  // Each time we do this we need to check how many lines we have and
+  // potentially break (so as we don't create *too many* lines).
+  for (wtf_size_t index : base::Reversed(perfect_fit_indices)) {
+    for (wtf_size_t i = index; i < ctx.sums.size(); ++i) {
+      ++ctx.sums[i];
+    }
+
+    // Break if we've reached our desired line-count.
+    line_count = GreedyLineCount(ctx, ctx.line_break_size);
+    if (line_count >= min_line_count) {
+      break;
+    }
+  }
+
+  return line_count;
+}
+
 template <typename ShouldBreakFunc>
 FlexLineBreakerResult BreakIntoLines(base::span<FlexItem> all_items,
                                      const LayoutUnit gap_between_items,
@@ -185,9 +305,14 @@
     const LayoutUnit line_break_size,
     const LayoutUnit gap_between_items,
     wtf_size_t min_line_count) {
+  // We can't have more lines than items.
+  min_line_count =
+      std::min(min_line_count, ClampTo<wtf_size_t>(all_items.size()));
+  DCHECK_GE(min_line_count, 1u);
+
   ScoreContext ctx = {
-      .line_break_size = static_cast<uint64_t>(line_break_size.RawValue()),
       .gap_between_items = static_cast<uint64_t>(gap_between_items.RawValue()),
+      .line_break_size = static_cast<uint64_t>(line_break_size.RawValue()),
       .sums = Vector<ScoreUnit>(all_items.size(), 0u),
       .scores = Vector(all_items.size(), ScoreData())};
 
@@ -220,47 +345,12 @@
     }
   }
 
-  // Using the prefix-sums array returns how many flex-lines would result with
-  // greedy packing given `line_break_size`.
-  auto greedy_line_count = [&ctx](ScoreUnit line_break_size) -> wtf_size_t {
-    const auto begin = ctx.sums.begin();
-    const auto end = ctx.sums.end();
-    auto it = begin;
-    ScoreUnit previous_sum = kZero;
-    wtf_size_t line_count = 1u;
-    for (;;) {
-      // Get the first value that has a size *greater* than the breakpoint.
-      auto next = std::upper_bound(
-          it, end, previous_sum + line_break_size + ctx.gap_between_items);
-
-      // Check if we are at the end, there is no additional line.
-      if (next == end) {
-        break;
-      }
-
-      // `next` is past our breakpoint. There are two scenarios:
-      //  - We are a single item which exceeds the line-break size, don't
-      //    backtrack for this case.
-      //  - There is more than one item, backtrack one item so the content fits
-      //    on the line.
-      const bool is_single_item =
-          ((line_count == 1 ? 1 : 0) + std::distance(it, next)) <= 1;
-      it = is_single_item ? next : std::prev(next);
-      previous_sum = *it;
-
-      // Only increment our line-count if we have content following `it`.
-      if (std::next(it) != end) {
-        ++line_count;
-      }
-    }
-    return line_count;
-  };
-
-  const wtf_size_t line_count = greedy_line_count(ctx.line_break_size);
-
-  // TODO(ikilpatrick): Bisect `line_break_size` to a smaller value with the
-  // same number of lines. We also need this for the "min-lines" feature.
-  // NOTE: This is close to being balanced, but not quite.
+  // Check if We are below the minimum line-count, bisect our line-break size
+  // to achieve the minimum number of lines.
+  wtf_size_t line_count = GreedyLineCount(ctx, ctx.line_break_size);
+  if (line_count < min_line_count) {
+    line_count = ApplyMinLineCount(min_line_count, ctx);
+  }
 
   // Next we can calculate for a given end index, what is the earliest start
   // index which will fit on the line.
diff --git a/third_party/blink/renderer/core/layout/layout_object.cc b/third_party/blink/renderer/core/layout/layout_object.cc
index 21efdfb..7f662f5 100644
--- a/third_party/blink/renderer/core/layout/layout_object.cc
+++ b/third_party/blink/renderer/core/layout/layout_object.cc
@@ -4903,7 +4903,7 @@
 
 bool LayoutObject::IsAllowedToModifyLayoutTreeStructure(Document& document) {
   return document.Lifecycle().StateAllowsLayoutTreeMutations() ||
-         document.GetStyleEngine().InContainerQueryStyleRecalc() ||
+         document.GetStyleEngine().InInterleavedStyleRecalc() ||
          document.GetStyleEngine().InScrollMarkersAttachment();
 }
 
diff --git a/third_party/blink/renderer/core/layout/layout_view.cc b/third_party/blink/renderer/core/layout/layout_view.cc
index f670a33..acc105a 100644
--- a/third_party/blink/renderer/core/layout/layout_view.cc
+++ b/third_party/blink/renderer/core/layout/layout_view.cc
@@ -948,32 +948,35 @@
   return add_result.is_new_entry;
 }
 
-void LayoutView::UpdateCountersAfterStyleChange(LayoutObject* container) {
+void LayoutView::UpdateCountersAfterStyleChange(
+    LayoutObject* interleaving_root) {
   NOT_DESTROYED();
-  if (!needs_marker_counter_update_)
+  if (!needs_marker_counter_update_) {
     return;
-
-  DCHECK(!container ||
-         (container->View() == this && container->IsDescendantOf(this) &&
-          GetDocument().GetStyleEngine().InContainerQueryStyleRecalc()))
-      << "The container parameter is currently only for scoping updates for "
-         "container query style recalcs";
+  }
+  DCHECK(!interleaving_root ||
+         (interleaving_root->View() == this &&
+          interleaving_root->IsDescendantOf(this) &&
+          GetDocument().GetStyleEngine().InInterleavedStyleRecalc()))
+      << "The interleaving_root parameter is currently only for scoped updates "
+         "for "
+         "interleaved style recalcs";
 
   needs_marker_counter_update_ = false;
   if (!HasLayoutCounters() && !HasLayoutListItems()) {
     return;
   }
 
-  // For container queries style recalc, we know the counter styles didn't
-  // change outside the container. Hence, we can start the update traversal from
-  // the container.
-  LayoutObject* start = container ? container : this;
-  // Additionally, if the container contains style, we know list-item counters
-  // inside the container cannot affect list-item counters outside the
-  // container, which means we can limit the traversal to the container subtree.
-  LayoutObject* stay_within =
-      container && container->ShouldApplyStyleContainment() ? container
-                                                            : nullptr;
+  // For interleaved style recalcs, we know the counter styles didn't change
+  // outside the interleaving root. Hence, we can start the update traversal
+  // from the interleaving_root.
+  LayoutObject* start = interleaving_root ? interleaving_root : this;
+  // Additionally, since the interleaving_root contains style, we know list-item
+  // counters inside the interleaving_root cannot affect list-item counters
+  // outside the interleaving_root, which means we can limit the traversal to
+  // the interleaving_root subtree.
+  CHECK(!interleaving_root || interleaving_root->ShouldApplyStyleContainment());
+  LayoutObject* stay_within = interleaving_root;
 
   for (LayoutObject* layout_object = start; layout_object;
        layout_object = layout_object->NextInPreOrder(stay_within)) {
diff --git a/third_party/blink/renderer/core/layout/layout_view.h b/third_party/blink/renderer/core/layout/layout_view.h
index 6f9cb53..26425b29 100644
--- a/third_party/blink/renderer/core/layout/layout_view.h
+++ b/third_party/blink/renderer/core/layout/layout_view.h
@@ -258,8 +258,11 @@
   bool AffectedByResizedInitialContainingBlock(const LayoutResult&);
 
   // Update generated counters after style and layout tree update.
-  // container - The container for container queries, otherwise nullptr.
-  void UpdateCountersAfterStyleChange(LayoutObject* container = nullptr);
+  // interleaving_root - For interleaved style recalcs, this is the container
+  // for size container queries and the anchored element for fallback queries,
+  // otherwise nullptr.
+  void UpdateCountersAfterStyleChange(
+      LayoutObject* interleaving_root = nullptr);
 
   bool BackgroundIsKnownToBeOpaqueInRect(
       const PhysicalRect& local_rect) const override;
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 15e3715..7a4563f9 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
@@ -337,9 +337,11 @@
 
     CHECK(element_);
 
-    element_->GetDocument().GetStyleEngine().UpdateStyleForOutOfFlow(
-        *element_, try_fallback_index, try_set, try_tactics,
-        &anchor_evaluator_);
+    element_->GetDocument()
+        .GetStyleEngine()
+        .UpdateStyleAndLayoutTreeForOutOfFlow(*element_, try_fallback_index,
+                                              try_set, try_tactics,
+                                              &anchor_evaluator_);
     CHECK(element_->GetLayoutObject());
     // Returns LayoutObject ComputedStyle instead of element style for layout
     // purposes. The style may be different, in particular for body -> html
@@ -353,8 +355,9 @@
   // Otherwise, the base style for generating auto anchor fallbacks.
   const ComputedStyle* style_ = nullptr;
 
-  // This evaluator is passed to StyleEngine::UpdateStyleForOutOfFlow to
-  // evaluate anchor queries on the computed style.
+  // This evaluator is passed to
+  // StyleEngine::UpdateStyleAndLayoutTreeForOutOfFlow to evaluate anchor
+  // queries on the computed style.
   AnchorEvaluatorImpl& anchor_evaluator_;
 
   // If the current style is applying a `position-try-fallbacks` fallback, this
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 40aca11..059b225 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
@@ -552,6 +552,14 @@
     return base::FeatureList::IsEnabled(features::kAISummarizationAPI);
   }
 
+  if (trial_name == "AIRewriterAPI") {
+    return base::FeatureList::IsEnabled(features::kAIRewriterAPI);
+  }
+
+  if (trial_name == "AIWriterAPI") {
+    return base::FeatureList::IsEnabled(features::kAIRewriterAPI);
+  }
+
   if (trial_name == "LanguageDetectionAPI") {
     return base::FeatureList::IsEnabled(features::kLanguageDetectionAPI);
   }
diff --git a/third_party/blink/renderer/core/paint/box_border_painter.cc b/third_party/blink/renderer/core/paint/box_border_painter.cc
index 91692dd..5663ed1 100644
--- a/third_party/blink/renderer/core/paint/box_border_painter.cc
+++ b/third_party/blink/renderer/core/paint/box_border_painter.cc
@@ -1109,6 +1109,48 @@
   }
 }
 
+bool BoxBorderPainter::ClipOutlineAsStrokeIfNeeded(
+    const ContouredRect& contoured_rect,
+    SkClipOp clip_op) const {
+  // We only use stroke for positive outlines with concave corners.
+  // Negative or convex outlines don't require joins and can use the ordinary
+  // ContouredRect path.
+  if (!is_uniform_width_ || !outer_.IsRounded() || outer_.IsConvex() ||
+      outer_outsets_.top <= 0) {
+    return false;
+  }
+
+  CHECK(style_.HasOutline());
+
+  // When rendering a uniform outset (e.g. an outline), we use stroking instead
+  // of the normal PathBuilder ContouredRect path, so that appropriate round
+  // joins/caps are rendered.
+  const FloatRoundedRect& origin_rect = contoured_rect.GetOriginRect();
+  const Path origin_path = Path::MakeContouredRect(
+      ContouredRect(origin_rect, contoured_rect.GetCornerCurvature()));
+  StrokeData stroke_data;
+  stroke_data.SetThickness(
+      contoured_rect.Rect().InsetsFrom(origin_rect.Rect()).width());
+  stroke_data.SetLineJoin(LineJoin::kRoundJoin);
+  stroke_data.SetLineCap(LineCap::kRoundCap);
+  SkPath clip_path = origin_path.StrokePath(stroke_data, AffineTransform());
+  clip_path.addPath(origin_path.GetSkPath());
+  context_.ClipPath(clip_path, kAntiAliased, clip_op);
+  return true;
+}
+
+void BoxBorderPainter::ClipContouredRect(const ContouredRect& rect) const {
+  if (!ClipOutlineAsStrokeIfNeeded(rect, SkClipOp::kIntersect)) {
+    context_.ClipContouredRect(rect);
+  }
+}
+
+void BoxBorderPainter::ClipOutContouredRect(const ContouredRect& rect) const {
+  if (!ClipOutlineAsStrokeIfNeeded(rect, SkClipOp::kDifference)) {
+    context_.ClipOutContouredRect(rect);
+  }
+}
+
 void BoxBorderPainter::Paint() const {
   if (!visible_edge_count_ || outer_.Rect().IsEmpty())
     return;
@@ -1118,14 +1160,17 @@
 
   bool clip_to_outer_border = outer_.IsRounded();
   GraphicsContextStateSaver state_saver(context_, clip_to_outer_border);
+
   if (clip_to_outer_border) {
     // For BackgroundBleedClip{Only,Layer}, the outer rrect clip is already
     // applied.
-    if (!BleedAvoidanceIsClipping(bleed_avoidance_))
-      context_.ClipContouredRect(outer_);
+    if (!BleedAvoidanceIsClipping(bleed_avoidance_)) {
+      ClipContouredRect(outer_);
+    }
 
-    if (inner_.IsRenderable() && !inner_.IsEmpty())
-      context_.ClipOutContouredRect(inner_);
+    if (inner_.IsRenderable() && !inner_.IsEmpty()) {
+      ClipOutContouredRect(inner_);
+    }
   }
 
   const ComplexBorderInfo border_info(*this);
@@ -1511,7 +1556,7 @@
     ContouredRect inner_clip =
         ContouredBorderGeometry::PixelSnappedContouredBorderWithOutsets(
             style_, border_rect_, inner_outsets, sides_to_include_);
-    context_.ClipContouredRect(inner_clip);
+    ClipContouredRect(inner_clip);
     context_.SetFillColor(color);
     context_.FillRect(rect, auto_dark_mode);
   }
@@ -1531,7 +1576,7 @@
     ContouredRect outer_clip =
         ContouredBorderGeometry::PixelSnappedContouredBorderWithOutsets(
             style_, used_border_rect, outer_outsets, sides_to_include_);
-    context_.ClipOutContouredRect(outer_clip);
+    ClipOutContouredRect(outer_clip);
     context_.SetFillColor(color);
     context_.FillRect(rect, auto_dark_mode);
   }
@@ -1562,7 +1607,7 @@
       ContouredBorderGeometry::PixelSnappedContouredBorderWithOutsets(
           style_, border_rect_, CenterOutsets(), sides_to_include_);
 
-  context_.ClipContouredRect(clip_rect);
+  ClipContouredRect(clip_rect);
   context_.SetFillColor(CalculateInsetOutsetColor(!darken_s1, color));
   context_.FillRect(rect, auto_dark_mode);
 }
diff --git a/third_party/blink/renderer/core/paint/box_border_painter.h b/third_party/blink/renderer/core/paint/box_border_painter.h
index 25064df..7a09c71 100644
--- a/third_party/blink/renderer/core/paint/box_border_painter.h
+++ b/third_party/blink/renderer/core/paint/box_border_painter.h
@@ -110,6 +110,10 @@
   void ClipBorderSidePolygon(BoxSide, MiterType miter1, MiterType miter2) const;
   gfx::Rect CalculateSideRectIncludingInner(BoxSide) const;
 
+  void ClipContouredRect(const ContouredRect&) const;
+  void ClipOutContouredRect(const ContouredRect&) const;
+  bool ClipOutlineAsStrokeIfNeeded(const ContouredRect&, SkClipOp) const;
+
   MiterType ComputeMiter(BoxSide, BoxSide adjacent_side, BorderEdgeFlags) const;
   static bool MitersRequireClipping(MiterType miter1,
                                     MiterType miter2,
diff --git a/third_party/blink/renderer/core/paint/timing/image_paint_timing_detector.cc b/third_party/blink/renderer/core/paint/timing/image_paint_timing_detector.cc
index 701cb2f3..2b6a446 100644
--- a/third_party/blink/renderer/core/paint/timing/image_paint_timing_detector.cc
+++ b/third_party/blink/renderer/core/paint/timing/image_paint_timing_detector.cc
@@ -70,8 +70,7 @@
   if (LocalDOMWindow* window = frame.DomWindow()) {
     if (SoftNavigationHeuristics* heuristics =
             window->GetSoftNavigationHeuristics()) {
-      heuristics->RecordPaint(&frame, rect.size().GetArea(),
-                              node->IsModifiedBySoftNavigation());
+      heuristics->RecordPaint(&frame, rect, node);
     }
   }
 }
diff --git a/third_party/blink/renderer/core/paint/timing/paint_timing_detector.cc b/third_party/blink/renderer/core/paint/timing/paint_timing_detector.cc
index 1a2f9ce..0a78870 100644
--- a/third_party/blink/renderer/core/paint/timing/paint_timing_detector.cc
+++ b/third_party/blink/renderer/core/paint/timing/paint_timing_detector.cc
@@ -29,6 +29,7 @@
 #include "third_party/blink/renderer/core/style/style_fetched_image.h"
 #include "third_party/blink/renderer/core/svg/graphics/svg_image.h"
 #include "third_party/blink/renderer/core/timing/dom_window_performance.h"
+#include "third_party/blink/renderer/core/timing/soft_navigation_heuristics.h"
 #include "third_party/blink/renderer/platform/graphics/bitmap_image.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_context.h"
 #include "third_party/blink/renderer/platform/graphics/image.h"
@@ -153,6 +154,10 @@
   LocalDOMWindow* window = frame_view_->GetFrame().DomWindow();
   if (window) {
     DOMWindowPerformance::performance(*window)->OnPaintFinished();
+
+    if (auto* heuristics = window->GetSoftNavigationHeuristics()) {
+      heuristics->OnPaintFinished();
+    }
   }
 
   PaintTiming::From(*frame_view_->GetFrame().GetDocument())
diff --git a/third_party/blink/renderer/core/paint/timing/text_paint_timing_detector.cc b/third_party/blink/renderer/core/paint/timing/text_paint_timing_detector.cc
index 3bd5783..eac3037a 100644
--- a/third_party/blink/renderer/core/paint/timing/text_paint_timing_detector.cc
+++ b/third_party/blink/renderer/core/paint/timing/text_paint_timing_detector.cc
@@ -182,9 +182,7 @@
   if (LocalDOMWindow* window = frame.DomWindow()) {
     if (SoftNavigationHeuristics* heuristics =
             window->GetSoftNavigationHeuristics()) {
-      heuristics->RecordPaint(
-          &frame, mapped_visual_rect.size().GetArea(),
-          aggregator.GetNode()->IsModifiedBySoftNavigation());
+      heuristics->RecordPaint(&frame, mapped_visual_rect, aggregator.GetNode());
     }
   }
   recorded_set_.insert(&aggregator);
diff --git a/third_party/blink/renderer/core/style/computed_style.cc b/third_party/blink/renderer/core/style/computed_style.cc
index 58e8beeb..c429dd60 100644
--- a/third_party/blink/renderer/core/style/computed_style.cc
+++ b/third_party/blink/renderer/core/style/computed_style.cc
@@ -2906,6 +2906,30 @@
   return false;
 }
 
+bool ComputedStyle::GapRuleColorIsTransparent(
+    const GapDataList<StyleColor>& gap_rule_color) const {
+  const blink::Color& current_color = GetCurrentColor();
+  const mojom::blink::ColorScheme& color_scheme = UsedColorScheme();
+  return std::ranges::all_of(
+      gap_rule_color.GetGapDataList(),
+      [&](const GapData<StyleColor>& gap_data) {
+        // If it’s a simple value, just test it directly.
+        if (!gap_data.IsRepeaterData()) {
+          const StyleColor& v = gap_data.GetValue();
+          return v.Resolve(current_color, color_scheme).IsFullyTransparent();
+        }
+
+        // Otherwise it’s a repeater: walk through its RepeatedValues(), and
+        // only return true if all values are transparent.
+        const auto* rep = gap_data.GetValueRepeater();
+        return std::ranges::all_of(
+            rep->RepeatedValues(), [&](const StyleColor& v) {
+              return v.Resolve(current_color, color_scheme)
+                  .IsFullyTransparent();
+            });
+      });
+}
+
 bool ComputedStyle::IsRenderedInTopLayer(const Element& element) const {
   return (element.IsInTopLayer() && Overlay() == EOverlay::kAuto) ||
          StyleType() == kPseudoIdBackdrop;
diff --git a/third_party/blink/renderer/core/style/computed_style.h b/third_party/blink/renderer/core/style/computed_style.h
index 75916fe..5316965 100644
--- a/third_party/blink/renderer/core/style/computed_style.h
+++ b/third_party/blink/renderer/core/style/computed_style.h
@@ -1003,10 +1003,7 @@
            !HasAutoColumnHeight();
   }
   bool ColumnRuleIsTransparent() const {
-    return ColumnRuleColor()
-        .GetLegacyValue()
-        .Resolve(GetCurrentColor(), UsedColorScheme())
-        .IsFullyTransparent();
+    return GapRuleColorIsTransparent(ColumnRuleColor());
   }
   bool ColumnRuleEquivalent(const ComputedStyle& other_style) const;
   bool HasColumnRule() const {
@@ -1019,10 +1016,7 @@
   }
 
   bool RowRuleIsTransparent() const {
-    return RowRuleColor()
-        .GetLegacyValue()
-        .Resolve(GetCurrentColor(), UsedColorScheme())
-        .IsFullyTransparent();
+    return GapRuleColorIsTransparent(RowRuleColor());
   }
   bool HasRowRule() const {
     // `SpecifiesColumns()` signifies we are in a multicol context. Return false
@@ -1583,6 +1577,9 @@
       }
       effective |= kContainsBlockSize;
     }
+    if (container_type & kContainerTypeAnchored) {
+      effective |= kContainsStyle;
+    }
     if (!IsContentVisibilityVisible(content_visibility)) {
       effective |= kContainsStyle;
       effective |= kContainsLayout;
@@ -2772,6 +2769,9 @@
   // Derived flags:
   bool CalculateIsStackingContextWithoutContainment() const;
 
+  CORE_EXPORT bool GapRuleColorIsTransparent(
+      const GapDataList<StyleColor>& gap_rule_color) const;
+
   FRIEND_TEST_ALL_PREFIXES(ComputedStyleTest, CustomPropertiesEqual_Values);
   FRIEND_TEST_ALL_PREFIXES(ComputedStyleTest, CustomPropertiesEqual_Data);
   FRIEND_TEST_ALL_PREFIXES(StyleCascadeTest, ForcedVisitedBackgroundColor);
diff --git a/third_party/blink/renderer/core/timing/soft_navigation_context.cc b/third_party/blink/renderer/core/timing/soft_navigation_context.cc
index 0b2cc1f..57efee9 100644
--- a/third_party/blink/renderer/core/timing/soft_navigation_context.cc
+++ b/third_party/blink/renderer/core/timing/soft_navigation_context.cc
@@ -4,12 +4,128 @@
 
 #include "third_party/blink/renderer/core/timing/soft_navigation_context.h"
 
+#include "base/trace_event/trace_event.h"
+#include "third_party/blink/renderer/core/dom/node.h"
+
 namespace blink {
 
+uint64_t SoftNavigationContext::last_context_id_ = 0;
+
 SoftNavigationContext::SoftNavigationContext() = default;
 
-void SoftNavigationContext::SetUrl(const String& url) {
-  url_ = url;
+void SoftNavigationContext::AddModifiedNode(Node* node) {
+  auto add_result = modified_nodes_.insert(node);
+
+  if (add_result.is_new_entry) {
+    // If this is the first mod this animation frame, trace it.
+    if (num_modified_dom_nodes_ ==
+        num_modified_dom_nodes_last_animation_frame_) {
+      // TODO(crbug.com/353218760): Add support for reporting every single
+      // modification. Perhaps changing this to FirstModifiedNodeInFrame, and
+      // then having all modifications in an even noisier trace category. Or
+      // based on a chrome feature flag, for testing?
+      TRACE_EVENT_INSTANT(
+          TRACE_DISABLED_BY_DEFAULT("loading"),
+          "SoftNavigationContext::FirstAddedModifiedNodeInAnimationFrame",
+          "context", this);
+    }
+    ++num_modified_dom_nodes_;
+  }
+}
+
+bool SoftNavigationContext::AddPaintedArea(Node* node,
+                                           const gfx::RectF& rect,
+                                           bool is_newest_context) {
+  uint64_t painted_area = rect.size().GetArea();
+
+  if (already_painted_modified_nodes_.Contains(node)) {
+    // We are sometimes observing paints for the same node.
+    // Until we fix first-contentful-paint-only observation, let's ignore these.
+    repainted_area_ += painted_area;
+    return false;
+  }
+
+  if (modified_nodes_.Contains(node)) {
+    already_painted_modified_nodes_.insert(node);
+    // If this is the first paint this animation frame, trace it.
+    if (painted_area_ == painted_area_last_animation_frame_) {
+      // TODO(crbug.com/353218760): Add support for reporting every single
+      // paint.
+      TRACE_EVENT_INSTANT(
+          TRACE_DISABLED_BY_DEFAULT("loading"),
+          "SoftNavigationContext::FirstAttributablePaintInAnimationFrame",
+          "context", this);
+    }
+    painted_area_ += painted_area;
+    return true;
+  }
+
+  if (is_newest_context) {
+    // We want to know how much paint the page is doing that isn't attributed.
+    // We only want to do this for a single (most recent) context, in order to
+    // never double count painted areas.
+    unattributed_area_ += painted_area;
+  }
+
+  return false;
+}
+
+bool SoftNavigationContext::SatisfiesSoftNavNonPaintCriteria() const {
+  return HasDomModification() && !url_.empty() &&
+         !user_interaction_timestamp_.is_null();
+}
+
+bool SoftNavigationContext::SatisfiesSoftNavPaintCriteria(
+    uint64_t required_paint_area) const {
+  return painted_area_ >= required_paint_area;
+}
+
+bool SoftNavigationContext::OnPaintFinished() {
+  auto num_modded_new_nodes =
+      num_modified_dom_nodes_ - num_modified_dom_nodes_last_animation_frame_;
+  auto num_gced_old_nodes = num_live_nodes_last_animation_frame_ +
+                            num_modded_new_nodes - modified_nodes_.size();
+  auto new_painted_area = painted_area_ - painted_area_last_animation_frame_;
+  auto new_repainted_area =
+      repainted_area_ - repainted_area_last_animation_frame_;
+
+  // TODO(crbug.com/353218760): Consider reporting if any of the values change
+  // if we have an extra loud tracing debug mode.
+  if (num_modded_new_nodes || new_painted_area) {
+    TRACE_EVENT_INSTANT(TRACE_DISABLED_BY_DEFAULT("loading"),
+                        "SoftNavigationContext::OnPaintFinished", "context",
+                        this, "numModdenNewNodes", num_modded_new_nodes,
+                        "numGcedOldNodes", num_gced_old_nodes, "newPaintedArea",
+                        new_painted_area, "newRepaintedArea",
+                        new_repainted_area);
+  }
+
+  num_modified_dom_nodes_last_animation_frame_ = num_modified_dom_nodes_;
+  num_live_nodes_last_animation_frame_ = modified_nodes_.size();
+  painted_area_last_animation_frame_ = painted_area_;
+  repainted_area_last_animation_frame_ = repainted_area_;
+
+  return new_painted_area > 0;
+}
+
+void SoftNavigationContext::WriteIntoTrace(
+    perfetto::TracedValue context) const {
+  perfetto::TracedDictionary dict = std::move(context).WriteDictionary();
+
+  dict.Add("softNavContextId", context_id_);
+  dict.Add("interactionTimestamp", UserInteractionTimestamp());
+  dict.Add("url", Url());
+  dict.Add("wasEmitted", WasEmitted());
+
+  dict.Add("domModifications", num_modified_dom_nodes_);
+  dict.Add("paintedArea", painted_area_);
+  dict.Add("repaintedArea", repainted_area_);
+  dict.Add("unattributedPaintedArea", unattributed_area_);
+}
+
+void SoftNavigationContext::Trace(Visitor* visitor) const {
+  visitor->Trace(modified_nodes_);
+  visitor->Trace(already_painted_modified_nodes_);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/timing/soft_navigation_context.h b/third_party/blink/renderer/core/timing/soft_navigation_context.h
index 8e8e33b0b..43f486d 100644
--- a/third_party/blink/renderer/core/timing/soft_navigation_context.h
+++ b/third_party/blink/renderer/core/timing/soft_navigation_context.h
@@ -5,18 +5,29 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_TIMING_SOFT_NAVIGATION_CONTEXT_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_TIMING_SOFT_NAVIGATION_CONTEXT_H_
 
+#include <cstdint>
+
 #include "base/time/time.h"
 #include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_set.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "ui/gfx/geometry/rect_f.h"
 
 namespace blink {
 
+class Node;
 class CORE_EXPORT SoftNavigationContext
     : public GarbageCollected<SoftNavigationContext> {
+  static uint64_t last_context_id_;
+
  public:
   SoftNavigationContext();
 
+  bool IsMostRecentlyCreatedContext() const {
+    return context_id_ == last_context_id_;
+  }
+
   base::TimeTicks UserInteractionTimestamp() const {
     return user_interaction_timestamp_;
   }
@@ -25,21 +36,56 @@
   }
 
   const String& Url() const { return url_; }
-  void SetUrl(const String& url);
+  void SetUrl(const String& url) { url_ = url; }
 
-  void MarkMainModification() { has_main_modification_ = true; }
-  bool HasMainModification() const { return has_main_modification_; }
+  bool WasEmitted() const { return was_emitted_; }
+  void SetWasEmitted() { was_emitted_ = true; }
 
-  void Trace(Visitor*) const {}
+  void AddModifiedNode(Node* node);
+  // Returns true if this paint updated the attributed area, and so we should
+  // check for sufficient paints to emit a soft-nav entry.
+  bool HasDomModification() const { return num_modified_dom_nodes_ > 0; }
 
-  bool IsSoftNavigation() const {
-    return has_main_modification_ && !url_.empty();
-  }
+  // Reports a new contentful paint area to this context, and the Node painted.
+  // Returns true if we update the total attributed area (meaning this context
+  // was involved in modifying this dom node, and we grew the painted region).
+  // Return value is used to check if it is worthwhile to check for "sufficient
+  // paints" (to emit a new soft-nav entry).
+  bool AddPaintedArea(Node* node,
+                      const gfx::RectF& rect,
+                      bool is_newest_context);
+  bool OnPaintFinished();
+
+  bool SatisfiesSoftNavNonPaintCriteria() const;
+  bool SatisfiesSoftNavPaintCriteria(uint64_t required_paint_area) const;
+
+  void WriteIntoTrace(perfetto::TracedValue context) const;
+
+  void Trace(Visitor* visitor) const;
 
  private:
+  // Pre-Increment `last_context_id_` such that the newest context uses the
+  // largest value and can be used to identify the most recent context.
+  const uint64_t context_id_ = ++last_context_id_;
+
   base::TimeTicks user_interaction_timestamp_;
   String url_;
-  bool has_main_modification_ = false;
+  bool was_emitted_ = false;
+
+  blink::HeapHashSet<WeakMember<Node>> modified_nodes_;
+  blink::HeapHashSet<WeakMember<Node>> already_painted_modified_nodes_;
+
+  // Elements of `modified_nodes_` can get GC-ed, so we need to keep a count of
+  // the total nodes modified.
+  size_t num_modified_dom_nodes_ = 0;
+  uint64_t painted_area_ = 0;
+  uint64_t repainted_area_ = 0;
+  uint64_t unattributed_area_ = 0;
+
+  size_t num_modified_dom_nodes_last_animation_frame_ = 0;
+  size_t num_live_nodes_last_animation_frame_ = 0;
+  uint64_t painted_area_last_animation_frame_ = 0;
+  uint64_t repainted_area_last_animation_frame_ = 0;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/timing/soft_navigation_heuristics.cc b/third_party/blink/renderer/core/timing/soft_navigation_heuristics.cc
index 91932c2e..ffcdc6c 100644
--- a/third_party/blink/renderer/core/timing/soft_navigation_heuristics.cc
+++ b/third_party/blink/renderer/core/timing/soft_navigation_heuristics.cc
@@ -4,8 +4,11 @@
 
 #include "third_party/blink/renderer/core/timing/soft_navigation_heuristics.h"
 
+#include <cstdint>
+#include <iterator>
 #include <utility>
 
+#include "base/containers/adapters.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_functions.h"
 #include "third_party/blink/public/common/features.h"
@@ -40,34 +43,47 @@
   kSoftNavigationDetected = 0,
 
   kNoSoftNavContextDuringUrlChange = 1,
-  kNoPaint = 2,
+  kInsufficientPaints = 2,
   kNoDomModification = 4,
 
-  kNoPaintOrDomModification = kNoPaint | kNoDomModification,
+  // For now, this next value is equivalent to kNoDomModification, because we
+  // cannot have paints without a dom mod.
+  // However, kNoDomModification might evolve into something more "semantic",
+  // such that you could have paints without a dom mod.
+  kNoPaintOrDomModification = kInsufficientPaints | kNoDomModification,
 
   kMaxValue = kNoPaintOrDomModification,
 };
 // LINT.ThenChange(/tools/metrics/histograms/enums.xml:SoftNavigationOutcome)
 
-void LogAndTraceDetectedSoftNavigation(LocalFrame* frame,
-                                       LocalDOMWindow* window,
-                                       const SoftNavigationContext& context) {
-  CHECK(frame && frame->IsMainFrame());
-  CHECK(window);
-  if (!RuntimeEnabledFeatures::SoftNavigationHeuristicsEnabled(window)) {
+void OnSoftNavigationContextWasExhausted(const SoftNavigationContext& context,
+                                         uint64_t required_paint_area) {
+  TRACE_EVENT_INSTANT(
+      TRACE_DISABLED_BY_DEFAULT("loading"),
+      "SoftNavigationHeuristics::SoftNavigationContextWasExhausted", "context",
+      context);
+
+  // Don't bother to log if the URL was never set.  That means it was just a
+  // normal interaction.
+  if (context.Url().empty()) {
     return;
   }
-  auto* console_message = MakeGarbageCollected<ConsoleMessage>(
-      mojom::blink::ConsoleMessageSource::kJavaScript,
-      mojom::blink::ConsoleMessageLevel::kInfo,
-      String("A soft navigation has been detected: ") + context.Url());
-  window->AddConsoleMessage(console_message);
 
-  TRACE_EVENT_INSTANT("scheduler,devtools.timeline,loading",
-                      "SoftNavigationHeuristics_SoftNavigationDetected",
-                      context.UserInteractionTimestamp(), "frame",
-                      GetFrameIdForTracing(frame), "url", context.Url(),
-                      "navigationId", window->GetNavigationId());
+  // TODO(crbug.com/351826232): Consider differentiating contexts that were
+  // cleaned up before page was unloaded vs cleaned up because of page unload.
+
+  if (context.WasEmitted()) {
+    // We already report this outcome eagerly, as part of
+    // `ReportSoftNavigationToMetrics`, so don't report again here.
+    // However, if we ever report total painted area, or largest paint size, we
+    // should do it here, lazily, in order to wait for the final sizes.
+  } else if (!context.HasDomModification()) {
+    base::UmaHistogramEnumeration(kPageLoadInternalSoftNavigationOutcome,
+                                  SoftNavigationOutcome::kNoDomModification);
+  } else if (!context.SatisfiesSoftNavPaintCriteria(required_paint_area)) {
+    base::UmaHistogramEnumeration(kPageLoadInternalSoftNavigationOutcome,
+                                  SoftNavigationOutcome::kInsufficientPaints);
+  }
 }
 
 constexpr bool IsInteractionStart(
@@ -141,34 +157,14 @@
 
 void SoftNavigationHeuristics::Shutdown() {
   task_attribution_tracker_ = nullptr;
+
+  const auto required_paint_area = CalculateRequiredPaintArea();
   for (const auto& context : potential_soft_navigations_) {
-    RecordUmaForNonSoftNavigationInteraction(*context.Get());
+    OnSoftNavigationContextWasExhausted(*context.Get(), required_paint_area);
   }
   potential_soft_navigations_.clear();
 }
 
-void SoftNavigationHeuristics::RecordUmaForNonSoftNavigationInteraction(
-    const SoftNavigationContext& context) const {
-  // For all interactions which included a (soft nav context attributable) URL
-  // modification, yet were not declared soft navs, log the criteria which were
-  // not met.
-  if (context.Url().empty()) {
-    return;
-  }
-
-  if (!paint_conditions_met_ && !context.HasMainModification()) {
-    base::UmaHistogramEnumeration(
-        kPageLoadInternalSoftNavigationOutcome,
-        SoftNavigationOutcome::kNoPaintOrDomModification);
-  } else if (!paint_conditions_met_) {
-    base::UmaHistogramEnumeration(kPageLoadInternalSoftNavigationOutcome,
-                                  SoftNavigationOutcome::kNoPaint);
-  } else if (!context.HasMainModification()) {
-    base::UmaHistogramEnumeration(kPageLoadInternalSoftNavigationOutcome,
-                                  SoftNavigationOutcome::kNoDomModification);
-  }
-}
-
 void SoftNavigationHeuristics::SetIsTrackingSoftNavigationHeuristicsOnDocument(
     bool value) const {
   if (Document* document = window_->document()) {
@@ -176,17 +172,6 @@
   }
 }
 
-void SoftNavigationHeuristics::ResetHeuristic() {
-  // Reset previously seen indicators and task IDs.
-  potential_soft_navigations_.clear();
-  last_detected_soft_navigation_ = nullptr;
-  active_interaction_context_ = nullptr;
-  SetIsTrackingSoftNavigationHeuristicsOnDocument(false);
-  did_commit_previous_paints_ = false;
-  paint_conditions_met_ = false;
-  softnav_painted_area_ = 0;
-}
-
 SoftNavigationContext*
 SoftNavigationHeuristics::GetSoftNavigationContextForCurrentTask() {
   if (potential_soft_navigations_.empty()) {
@@ -200,6 +185,13 @@
   CHECK(task_attribution_tracker_);
   if (auto* task_state = task_attribution_tracker_->RunningTask()) {
     if (auto* context = task_state->GetSoftNavigationContext()) {
+      // Even when we have a context, we need to confirm if this SNH instance
+      // knows about it. If a context created in one window is scheduled in
+      // another, we might have a different SNH instance. This seems to fail
+      // with datetime/calendar modals, for example.
+      // TODO(crbug.com/40871933): We don't care to support datetime modals, but
+      // this behaviour might be similar for iframes, and might be worth
+      // supporting.
       if (context && potential_soft_navigations_.Contains(context)) {
         return context;
       }
@@ -217,65 +209,100 @@
   }
   scheduler::TaskAttributionInfo* task_state =
       task_attribution_tracker_->RunningTask();
-  SoftNavigationContext* context =
-      task_state ? task_state->GetSoftNavigationContext() : nullptr;
-  TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("loading"),
-               "SoftNavigationHeuristics::AsyncSameDocumentNavigationStarted",
-               "has_context", !!context);
-  if (context) {
-    task_attribution_tracker_->AddSameDocumentNavigationTask(task_state);
+  if (!task_state) {
+    return std::nullopt;
   }
-  return context ? std::optional<scheduler::TaskAttributionId>(task_state->Id())
-                 : std::nullopt;
+  SoftNavigationContext* context = task_state->GetSoftNavigationContext();
+  if (!context) {
+    return std::nullopt;
+  }
+  task_attribution_tracker_->AddSameDocumentNavigationTask(task_state);
+  return task_state->Id();
 }
 
 void SoftNavigationHeuristics::SameDocumentNavigationCommitted(
     const String& url,
     SoftNavigationContext* context) {
-  TRACE_EVENT2(TRACE_DISABLED_BY_DEFAULT("loading"),
-               "SoftNavigationHeuristics::SameDocumentNavigationCommitted",
-               "url", url, "has_context", !!context);
-  if (context) {
-    if (potential_soft_navigations_.Contains(context)) {
-      context->SetUrl(url);
-      EmitSoftNavigationEntryIfAllConditionsMet(context);
-    }
-  } else {
+  if (!context) {
+    TRACE_EVENT_INSTANT(TRACE_DISABLED_BY_DEFAULT("loading"),
+                        "SoftNavigationHeuristics::"
+                        "SameDocumentNavigationCommittedWithoutContext",
+                        "url", url);
     base::UmaHistogramEnumeration(
         kPageLoadInternalSoftNavigationOutcome,
         SoftNavigationOutcome::kNoSoftNavContextDuringUrlChange);
+    return;
   }
+  context->SetUrl(url);
+
+  TRACE_EVENT_INSTANT(
+      TRACE_DISABLED_BY_DEFAULT("loading"),
+      "SoftNavigationHeuristics::SameDocumentNavigationCommitted", "context",
+      *context);
+
+  EmitSoftNavigationEntryIfAllConditionsMet(context);
 }
 
-bool SoftNavigationHeuristics::ModifiedDOM() {
+bool SoftNavigationHeuristics::ModifiedDOM(Node* node) {
+  // Don't bother marking dom nodes unless they are in the right frame.
+  if (!GetLocalFrameIfOutermostAndNotDetached()) {
+    return false;
+  }
   SoftNavigationContext* context = GetSoftNavigationContextForCurrentTask();
-  if (context) {
-    context->MarkMainModification();
-    EmitSoftNavigationEntryIfAllConditionsMet(context);
+  if (!context) {
+    return false;
   }
-  TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("loading"),
-               "SoftNavigationHeuristics::ModifiedDOM", "has_context",
-               !!context);
-  return !!context;
+  node->SetIsModifiedBySoftNavigation();
+  context->AddModifiedNode(node);
+
+  EmitSoftNavigationEntryIfAllConditionsMet(context);
+  return true;
 }
 
-void SoftNavigationHeuristics::EmitSoftNavigationEntryIfAllConditionsMet(
+bool SoftNavigationHeuristics::EmitSoftNavigationEntryIfAllConditionsMet(
     SoftNavigationContext* context) {
-  // If there's an `EventScope` on the stack, hold off checking to avoid
-  // clearing state while it's in use.
-  if (has_active_event_scope_) {
-    return;
+  DCHECK(context);
+
+  // If we've already emitted this entry, we might still be tracking paints.
+  // Skip the rest since we only want to emit new soft-navs.
+  if (context->WasEmitted()) {
+    return false;
   }
 
-  LocalFrame* frame = GetLocalFrameIfNotDetached();
-  // TODO(crbug.com/1510706): See if we need to add `paint_conditions_met_` back
-  // into this condition.
-  if (!context || !context->IsSoftNavigation() ||
-      context->UserInteractionTimestamp().is_null() || !frame ||
-      !frame->IsOutermostMainFrame()) {
-    return;
+  // Are the basic criteria met (interaction, url, dom modification)?
+  if (!context->SatisfiesSoftNavNonPaintCriteria()) {
+    return false;
   }
-  last_detected_soft_navigation_ = context;
+
+  // Once we've met all criteria with a new context, we replace the active
+  // soft_navigation. This helps us collect paints even after Task graph is
+  // exhausted, and allows the previous context to get cleaned up.
+  // TODO(crbug.com/416705860): Consider always making this the context which
+  // was last to update the URL, immediately upon changing, rather than waiting
+  // for other criteria (like dom modification) to be met.
+  most_recent_context_to_meet_non_paint_criteria_ = context;
+
+  // Are we done?
+  uint64_t required_paint_area = CalculateRequiredPaintArea();
+  if (!context->SatisfiesSoftNavPaintCriteria(required_paint_area)) {
+    return false;
+  }
+
+  // We have met all criteria!  SetWasEmitted here, even though we might still
+  // constrain reporting (below).  That is because we do not want to test
+  // for meeting criteria ever again, once we meet it for the first time.
+  context->SetWasEmitted();
+
+  // TODO(crbug.com/40871933): We are already only marking dom nodes when we
+  // have a frame, and we are already limiting paints attribution to contexts
+  // that come from the same SNH/window instance.  So, this might be safe to
+  // CHECK().  However, potentially it is possible to meet paint criteria, then
+  // meet some other final criteria in a different frame?  Until we test that,
+  // let's just guard carefully.
+  LocalFrame* frame = GetLocalFrameIfOutermostAndNotDetached();
+  if (!frame) {
+    return false;
+  }
 
   ++soft_navigation_count_;
   window_->GenerateNewNavigationId();
@@ -283,55 +310,51 @@
   performance->AddSoftNavigationEntry(AtomicString(context->Url()),
                                       context->UserInteractionTimestamp());
 
-  CommitPreviousPaints(frame);
-
-  LogAndTraceDetectedSoftNavigation(frame, window_.Get(), *context);
+  CommitPreviousPaintTimings(frame);
   ReportSoftNavigationToMetrics(frame, context);
-  ResetHeuristic();
+
+  TRACE_EVENT_INSTANT("scheduler,devtools.timeline,loading",
+                      "SoftNavigationHeuristics_SoftNavigationDetected",
+                      "context", *context, "frame", GetFrameIdForTracing(frame),
+                      "navigationId", window_->GetNavigationId());
+
+  return true;
 }
 
-// This is called from Text/ImagePaintTimingDetector when a paint is recorded
-// there.
-void SoftNavigationHeuristics::RecordPaint(
-    LocalFrame* frame,
-    uint64_t painted_area,
-    bool is_modified_by_soft_navigation) {
-  if (potential_soft_navigations_.empty()) {
-    // We aren't measuring a soft-nav so we can just exit.
+void SoftNavigationHeuristics::RecordPaint(LocalFrame* frame,
+                                           const gfx::RectF& rect,
+                                           Node* node) {
+  // A node which was created in a different window and transferred to this
+  // window might have modified-bit set, but this SNH class might not actually
+  // be set to detecting softnavs.
+  if (!node->IsModifiedBySoftNavigation()) {
     return;
   }
 
-  if (!is_modified_by_soft_navigation) {
+  // First, start with the most likely context.
+  if (most_recent_context_to_meet_non_paint_criteria_ &&
+      most_recent_context_to_meet_non_paint_criteria_->AddPaintedArea(
+          node, rect, true)) {
     return;
   }
 
-  softnav_painted_area_ += painted_area;
-
-  uint64_t required_paint_area = CalculateRequiredPaintArea();
-
-  if (required_paint_area == 0) {
-    return;
+  // Then, reverse-iterate the rest of the list.
+  for (auto& context : base::Reversed(potential_soft_navigations_)) {
+    if (context == most_recent_context_to_meet_non_paint_criteria_) {
+      continue;
+    }
+    if (context->AddPaintedArea(node, rect,
+                                context->IsMostRecentlyCreatedContext())) {
+      break;
+    }
   }
+}
 
-  bool is_above_threshold = (softnav_painted_area_ > required_paint_area);
-
-  TRACE_EVENT_INSTANT(
-      TRACE_DISABLED_BY_DEFAULT("loading"),
-      "SoftNavigationHeuristics_RecordPaint", "softnav_painted_area",
-      softnav_painted_area_, "required_paint_area", required_paint_area, "url",
-      (last_detected_soft_navigation_ ? last_detected_soft_navigation_->Url()
-                                      : ""),
-      "is_above_threshold", is_above_threshold);
-
-  // TODO(crbug.com/1510706): GC between DOM modification and paint could cause
-  // `last_detected_soft_navigation_` to be cleared, preventing the entry from
-  // being emitted if `paint_conditions_met_` wasn't set but will be in the
-  // subsequent paint. This problem existed in task attribution v1 as well since
-  // the heuristic is reset when `potential_soft_navigations_` becomes empty.
-  if (is_above_threshold) {
-    paint_conditions_met_ = true;
-    EmitSoftNavigationEntryIfAllConditionsMet(
-        last_detected_soft_navigation_.Get());
+void SoftNavigationHeuristics::OnPaintFinished() {
+  for (const auto& context : potential_soft_navigations_) {
+    if (context->OnPaintFinished()) {
+      EmitSoftNavigationEntryIfAllConditionsMet(context);
+    }
   }
 }
 
@@ -363,9 +386,9 @@
                                 SoftNavigationOutcome::kSoftNavigationDetected);
 }
 
-void SoftNavigationHeuristics::ResetPaintsIfNeeded() {
-  LocalFrame* frame = GetLocalFrameIfNotDetached();
-  if (!frame || !frame->IsOutermostMainFrame()) {
+void SoftNavigationHeuristics::ResetPaintTimingsIfNeeded() {
+  LocalFrame* frame = GetLocalFrameIfOutermostAndNotDetached();
+  if (!frame) {
     return;
   }
   LocalFrameView* local_frame_view = frame->View();
@@ -387,27 +410,23 @@
 // `EmitSoftNavigationEntryIfAllConditionsMet()`), the previous paints are
 // committed, to make sure accumulated FP, FCP and LCP entries are properly
 // fired.
-void SoftNavigationHeuristics::CommitPreviousPaints(LocalFrame* frame) {
+void SoftNavigationHeuristics::CommitPreviousPaintTimings(LocalFrame* frame) {
   CHECK(frame && frame->IsOutermostMainFrame());
-  if (!did_commit_previous_paints_) {
-    LocalFrameView* local_frame_view = frame->View();
+  LocalFrameView* local_frame_view = frame->View();
 
-    CHECK(local_frame_view);
+  CHECK(local_frame_view);
 
-    local_frame_view->GetPaintTimingDetector().SoftNavigationDetected(
-        window_.Get());
-    if (RuntimeEnabledFeatures::SoftNavigationHeuristicsExposeFPAndFCPEnabled(
-            window_.Get())) {
-      PaintTiming::From(*window_->document()).SoftNavigationDetected();
-    }
-
-    did_commit_previous_paints_ = true;
+  local_frame_view->GetPaintTimingDetector().SoftNavigationDetected(
+      window_.Get());
+  if (RuntimeEnabledFeatures::SoftNavigationHeuristicsExposeFPAndFCPEnabled(
+          window_.Get())) {
+    PaintTiming::From(*window_->document()).SoftNavigationDetected();
   }
 }
 
 void SoftNavigationHeuristics::Trace(Visitor* visitor) const {
-  visitor->Trace(last_detected_soft_navigation_);
   visitor->Trace(active_interaction_context_);
+  visitor->Trace(most_recent_context_to_meet_non_paint_criteria_);
   // Register a custom weak callback, which runs after processing weakness for
   // the container. This allows us to observe the collection becoming empty
   // without needing to observe individual element disposal.
@@ -417,6 +436,11 @@
   visitor->Trace(window_);
 }
 
+// This is invoked when executing a callback with an active `EventScope`,
+// which happens for click and keyboard input events, as well as
+// user-initiated navigation and popstate events. Running such an event
+// listener "activates" the `SoftNavigationContext` as a candidate soft
+// navigation.
 void SoftNavigationHeuristics::OnCreateTaskScope(
     scheduler::TaskAttributionInfo& task_state) {
   CHECK(active_interaction_context_);
@@ -431,15 +455,11 @@
 
   // TODO(crbug.com/40942324): Replace task_id with either an id for the
   // `SoftNavigationContext` or a serialized version of the object.
-  TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("loading"),
-               "SoftNavigationHeuristics::OnCreateTaskScope", "task_id",
-               task_state.Id().value());
-  // This is invoked when executing a callback with an active `EventScope`,
-  // which happens for click and keyboard input events, as well as
-  // user-initiated navigation and popstate events. Running such an event
-  // listener "activates" the `SoftNavigationContext` as a candidate soft
-  // navigation.
-  initial_interaction_encountered_ = true;
+  TRACE_EVENT_INSTANT(TRACE_DISABLED_BY_DEFAULT("loading"),
+                      "SoftNavigationHeuristics::OnCreateTaskScope", "context",
+                      active_interaction_context_.Get(), "task_id",
+                      task_state.Id().value());
+
   SetIsTrackingSoftNavigationHeuristicsOnDocument(true);
 }
 
@@ -455,27 +475,50 @@
   //
   // Note: This is not allowed to do Oilpan allocations. If that's needed, this
   // can schedule a task or microtask to reset the heuristic.
-  Vector<UntracedMember<SoftNavigationContext>> dead_contexts;
-  for (const auto& context : potential_soft_navigations_) {
+  const auto required_paint_area = CalculateRequiredPaintArea();
+  WTF::EraseIf(potential_soft_navigations_, [&](const auto& context) {
     if (!info.IsHeapObjectAlive(context)) {
-      RecordUmaForNonSoftNavigationInteraction(*context.Get());
-      dead_contexts.push_back(context);
+      OnSoftNavigationContextWasExhausted(*context.Get(), required_paint_area);
+      return true;
     }
-  }
-  potential_soft_navigations_.RemoveAll(dead_contexts);
+    return false;
+  });
+
+  // This should never happen if we have a last_interaction_context_.
+  // numbecrbug.com/416706750aints?  Perhaps once all dom nodes modified by the
+  // context and/or number of paints?  Perhaps once all dom nodes modified by
+  // the context have been painted at least once, we don't care about more data?
+  // Perhaps after user scrolls/interacts after history change?
   if (potential_soft_navigations_.empty()) {
     CHECK(!active_interaction_context_);
-    ResetHeuristic();
+    CHECK(!most_recent_context_to_meet_non_paint_criteria_);
+    SetIsTrackingSoftNavigationHeuristicsOnDocument(false);
   }
 }
 
-LocalFrame* SoftNavigationHeuristics::GetLocalFrameIfNotDetached() const {
-  return window_->IsCurrentlyDisplayedInFrame() ? window_->GetFrame() : nullptr;
+LocalFrame* SoftNavigationHeuristics::GetLocalFrameIfOutermostAndNotDetached()
+    const {
+  if (!window_->IsCurrentlyDisplayedInFrame()) {
+    return nullptr;
+  }
+
+  LocalFrame* frame = window_->GetFrame();
+  if (!frame->IsOutermostMainFrame()) {
+    return nullptr;
+  }
+
+  return frame;
 }
 
 SoftNavigationHeuristics::EventScope SoftNavigationHeuristics::CreateEventScope(
     EventScope::Type type,
     ScriptState* script_state) {
+  // TODO(crbug.com/417164510): It appears that we can create many contexts for
+  // a single interaction, because we can get many ::CreateEventScope (non
+  // nested) even for a single interaction.
+  // We might want to move the EventScope wrapper higher up in the event
+  // dispatch code, so we don't re-create it so often.
+
   if (!has_active_event_scope_) {
     // Create a new `SoftNavigationContext`, which represents a candidate soft
     // navigation interaction. This context is propagated to all descendant
@@ -487,12 +530,15 @@
     if (IsInteractionStart(type) || !active_interaction_context_) {
       active_interaction_context_ =
           MakeGarbageCollected<SoftNavigationContext>();
-      potential_soft_navigations_.insert(active_interaction_context_.Get());
+      potential_soft_navigations_.push_back(active_interaction_context_);
+      TRACE_EVENT_INSTANT(TRACE_DISABLED_BY_DEFAULT("loading"),
+                          "SoftNavigationHeuristics::CreateNewContext",
+                          "context", *active_interaction_context_);
     }
 
     // Ensure that paints would be reset, so that paint recording would continue
     // despite the user interaction.
-    ResetPaintsIfNeeded();
+    ResetPaintTimingsIfNeeded();
   }
   CHECK(active_interaction_context_.Get());
 
@@ -554,17 +600,24 @@
 }
 
 uint64_t SoftNavigationHeuristics::CalculateRequiredPaintArea() const {
+  static constexpr uint64_t kMinRequiredArea = 1;
+
   LocalFrame* frame = window_->GetFrame();
   CHECK(frame);
   LocalFrameView* local_frame_view = frame->View();
-  CHECK(local_frame_view);
+  if (!local_frame_view) {
+    return kMinRequiredArea;
+  }
 
-  constexpr int kSoftNavigationPaintAreaPercentage = 2;
+  constexpr int kSoftNavigationPaintAreaPercentageInPoints = 1;  // 0.01%
   uint64_t viewport_area = local_frame_view->GetLayoutSize().Area64();
   uint64_t required_paint_area =
-      (viewport_area * kSoftNavigationPaintAreaPercentage) / 100;
-  CHECK_GE(required_paint_area, 0u);
-  return required_paint_area;
+      (viewport_area * kSoftNavigationPaintAreaPercentageInPoints) / 10000;
+
+  if (required_paint_area > kMinRequiredArea) {
+    return required_paint_area;
+  }
+  return kMinRequiredArea;
 }
 
 // SoftNavigationHeuristics::EventScope implementation
diff --git a/third_party/blink/renderer/core/timing/soft_navigation_heuristics.h b/third_party/blink/renderer/core/timing/soft_navigation_heuristics.h
index 3245e4ca..4f8ecf56c 100644
--- a/third_party/blink/renderer/core/timing/soft_navigation_heuristics.h
+++ b/third_party/blink/renderer/core/timing/soft_navigation_heuristics.h
@@ -93,15 +93,15 @@
 
   void SameDocumentNavigationCommitted(const String& url,
                                        SoftNavigationContext*);
-  bool ModifiedDOM();
+  bool ModifiedDOM(Node* node);
   uint32_t SoftNavigationCount() { return soft_navigation_count_; }
 
   // TaskAttributionTracker::Observer's implementation.
   void OnCreateTaskScope(scheduler::TaskAttributionInfo&) override;
 
-  void RecordPaint(LocalFrame*,
-                   uint64_t painted_area,
-                   bool is_modified_by_soft_navigation);
+  void RecordPaint(LocalFrame*, const gfx::RectF& rect, Node* node);
+
+  void OnPaintFinished();
 
   // Returns an `EventScope` suitable for navigation. Used for navigations not
   // yet associated with an event.
@@ -114,26 +114,18 @@
   std::optional<EventScope> MaybeCreateEventScopeForEvent(const Event&);
 
   // This method is called during the weakness processing stage of garbage
-  // collection to remove items from `potential_soft_navigations_` and to detect
-  // it becoming empty, in which case the heuristic is reset.
+  // collection to remove items from `potential_soft_navigations_`.
   void ProcessCustomWeakness(const LivenessBroker& info);
 
-  bool GetInitialInteractionEncounteredForTest() {
-    return initial_interaction_encountered_;
-  }
-
  private:
-  void RecordUmaForNonSoftNavigationInteraction(
-      const SoftNavigationContext&) const;
   void ReportSoftNavigationToMetrics(LocalFrame*, SoftNavigationContext*) const;
   void SetIsTrackingSoftNavigationHeuristicsOnDocument(bool value) const;
 
   SoftNavigationContext* GetSoftNavigationContextForCurrentTask();
-  void ResetHeuristic();
-  void ResetPaintsIfNeeded();
-  void CommitPreviousPaints(LocalFrame*);
-  void EmitSoftNavigationEntryIfAllConditionsMet(SoftNavigationContext*);
-  LocalFrame* GetLocalFrameIfNotDetached() const;
+  void ResetPaintTimingsIfNeeded();
+  void CommitPreviousPaintTimings(LocalFrame*);
+  bool EmitSoftNavigationEntryIfAllConditionsMet(SoftNavigationContext*);
+  LocalFrame* GetLocalFrameIfOutermostAndNotDetached() const;
   void OnSoftNavigationEventScopeDestroyed(const EventScope&);
   EventScope CreateEventScope(EventScope::Type type, ScriptState*);
   uint64_t CalculateRequiredPaintArea() const;
@@ -144,8 +136,7 @@
   // objects are added when they are the active context during an event handler
   // running in an `EventScope`. Entries are stored as untraced members to do
   // custom weak processing (see `ProcessCustomWeakness()`).
-  HashSet<UntracedMember<const SoftNavigationContext>>
-      potential_soft_navigations_;
+  Vector<UntracedMember<SoftNavigationContext>> potential_soft_navigations_;
 
   // The `SoftNavigationContext` of the "active interaction", if any.
   //
@@ -168,18 +159,13 @@
   // events, this remains alive until the next interaction.
   Member<SoftNavigationContext> active_interaction_context_;
 
-  // The last soft navigation detected, which could be pending (not emitted)
-  // until `paint_conditions_met_` is true.
-  //
-  // TODO(crbug.com/1510706): Remove this is if `paint_conditions_met_` isn't
-  // reinstated since it is cleared immediately after emitting the entry.
-  WeakMember<SoftNavigationContext> last_detected_soft_navigation_;
+  // Save a strong reference to the most recent soft navigation detected.  This
+  // context could still be pending (not emitted) as we wait to observe more
+  // paints, or it might have already been emitted, but we still want to
+  // continue measuring paints for a while.
+  Member<SoftNavigationContext> most_recent_context_to_meet_non_paint_criteria_;
 
   uint32_t soft_navigation_count_ = 0;
-  uint64_t softnav_painted_area_ = 0;
-  bool did_commit_previous_paints_ = false;
-  bool paint_conditions_met_ = false;
-  bool initial_interaction_encountered_ = false;
   bool has_active_event_scope_ = false;
   // `task_attribution_tracker_` is cleared during `Shutdown()` (frame detach),
   // which should happen before the tracker is destroyed, since its lifetime is
diff --git a/third_party/blink/renderer/core/timing/soft_navigation_heuristics_test.cc b/third_party/blink/renderer/core/timing/soft_navigation_heuristics_test.cc
index cdd11de..1d2d73e 100644
--- a/third_party/blink/renderer/core/timing/soft_navigation_heuristics_test.cc
+++ b/third_party/blink/renderer/core/timing/soft_navigation_heuristics_test.cc
@@ -49,6 +49,23 @@
     return heuristics;
   }
 
+  Node* CreateNodeForTest() {
+    ScriptState* script_state = GetScriptStateForTest();
+    LocalDOMWindow* window = LocalDOMWindow::From(script_state);
+    Document* document = window->document();
+    return document->CreateRawElement(html_names::kDivTag);
+  }
+
+  void ReportPaintRectForTest(SoftNavigationHeuristics* heuristics,
+                              Node* node) {
+    ScriptState* script_state = GetScriptStateForTest();
+    LocalDOMWindow* window = LocalDOMWindow::From(script_state);
+    LocalFrame* frame = window->GetFrame();
+    gfx::RectF rect{1000, 1000};
+    heuristics->RecordPaint(frame, rect, node);
+    return heuristics->OnPaintFinished();
+  }
+
   ScriptState* GetScriptStateForTest() {
     return ToScriptStateForMainWorld(page_holder_->GetDocument().GetFrame());
   }
@@ -125,7 +142,6 @@
         tracker->MaybeCreateTaskScopeForCallback(GetScriptStateForTest(),
                                                  nullptr);
   }
-  ASSERT_TRUE(test_heuristics->GetInitialInteractionEncounteredForTest());
 }
 
 TEST_F(SoftNavigationHeuristicsTest, ResetHeuristicOnSetBecameEmpty) {
@@ -236,7 +252,7 @@
   {
     std::optional<TaskScope> task_scope =
         tracker->MaybeCreateTaskScopeForCallback(script_state, nullptr);
-    heuristics->ModifiedDOM();
+    heuristics->ModifiedDOM(CreateNodeForTest());
   }
 
   // Simulate default action link navigation after the click event.
@@ -325,7 +341,9 @@
       ASSERT_TRUE(context);
 
       heuristics->SameDocumentNavigationCommitted("foo.html", context);
-      heuristics->ModifiedDOM();
+      Node* node = CreateNodeForTest();
+      heuristics->ModifiedDOM(node);
+      ReportPaintRectForTest(heuristics, node);
     }
   }
   EXPECT_EQ(heuristics->SoftNavigationCount(), 1u);
@@ -334,7 +352,9 @@
     std::optional<TaskScope> task_scope =
         tracker->MaybeCreateTaskScopeForCallback(script_state, task_state);
     heuristics->SameDocumentNavigationCommitted("bar.html", context);
-    heuristics->ModifiedDOM();
+    Node* node = CreateNodeForTest();
+    heuristics->ModifiedDOM(node);
+    ReportPaintRectForTest(heuristics, node);
   }
   EXPECT_EQ(heuristics->SoftNavigationCount(), 1u);
 }
diff --git a/third_party/blink/renderer/core/view_transition/dom_view_transition.cc b/third_party/blink/renderer/core/view_transition/dom_view_transition.cc
index 73b64ff..8b03aeb 100644
--- a/third_party/blink/renderer/core/view_transition/dom_view_transition.cc
+++ b/third_party/blink/renderer/core/view_transition/dom_view_transition.cc
@@ -342,4 +342,8 @@
   return view_transition_->Types();
 }
 
+Element* DOMViewTransition::transitionRoot() const {
+  return view_transition_->Scope();
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/view_transition/dom_view_transition.h b/third_party/blink/renderer/core/view_transition/dom_view_transition.h
index f0ab250..8cb3bc3 100644
--- a/third_party/blink/renderer/core/view_transition/dom_view_transition.h
+++ b/third_party/blink/renderer/core/view_transition/dom_view_transition.h
@@ -51,6 +51,8 @@
 
   ViewTransitionTypeSet* types() const;
 
+  Element* transitionRoot() const;
+
   // Called from ViewTransition when the transition is skipped/aborted for any
   // reason.
   void DidSkipTransition(ViewTransition::PromiseResponse);
diff --git a/third_party/blink/renderer/core/view_transition/view_transition.idl b/third_party/blink/renderer/core/view_transition/view_transition.idl
index 6d157baa..18faf5a 100644
--- a/third_party/blink/renderer/core/view_transition/view_transition.idl
+++ b/third_party/blink/renderer/core/view_transition/view_transition.idl
@@ -33,4 +33,11 @@
   // for this transition. These types are selectable using the
   // :active-view-transition-type pseudo-class.
   readonly attribute ViewTransitionTypeSet types;
+
+  // This will return an Element that generated this transition.
+  // TODO(vmpstr): Note that for document transitions, this is the document
+  // element. We need to figure out if we need to distinguish between
+  // document.startViewTransition() and
+  // document.documentElement.startViewTransition().
+  [RuntimeEnabled=ScopedViewTransitions] readonly attribute Element transitionRoot;
 };
diff --git a/third_party/blink/renderer/modules/ai/on_device_translation/language_detector.cc b/third_party/blink/renderer/modules/ai/on_device_translation/language_detector.cc
index 97db104..154cc79 100644
--- a/third_party/blink/renderer/modules/ai/on_device_translation/language_detector.cc
+++ b/third_party/blink/renderer/modules/ai/on_device_translation/language_detector.cc
@@ -456,6 +456,10 @@
 
   HeapVector<Member<LanguageDetectionResult>> results;
   for (const auto& prediction : predictions) {
+    if (prediction.language == "unknown") {
+      continue;
+    }
+
     CHECK_GE(prediction.score, 0);
     CHECK_LE(prediction.score, 1 - cumulative_confidence);
     CHECK_LE(prediction.score, last_score);
diff --git a/third_party/blink/renderer/platform/OWNERS b/third_party/blink/renderer/platform/OWNERS
index 24417a06b..e929d31 100644
--- a/third_party/blink/renderer/platform/OWNERS
+++ b/third_party/blink/renderer/platform/OWNERS
@@ -3,7 +3,6 @@
 drott@chromium.org
 dtapuska@chromium.org
 fmalita@chromium.org
-fserb@chromium.org
 haraken@chromium.org
 ikilpatrick@chromium.org
 jbroman@chromium.org
diff --git a/third_party/blink/renderer/platform/geometry/contoured_rect.h b/third_party/blink/renderer/platform/geometry/contoured_rect.h
index 54d3ae85..22764f0 100644
--- a/third_party/blink/renderer/platform/geometry/contoured_rect.h
+++ b/third_party/blink/renderer/platform/geometry/contoured_rect.h
@@ -62,6 +62,11 @@
       return (top_left_ == kRound) && IsUniform();
     }
 
+    constexpr bool IsConvex() const {
+      return top_left_ >= kBevel && top_right_ >= kBevel &&
+             bottom_right_ >= kBevel && bottom_left_ >= kBevel;
+    }
+
     constexpr bool IsUniform() const {
       return top_left_ == top_right_ && top_left_ == bottom_right_ &&
              top_left_ == bottom_left_;
@@ -172,6 +177,10 @@
     return corner_curvature_.IsRound() || !IsRounded();
   }
 
+  constexpr bool IsConvex() const {
+    return !IsRounded() || corner_curvature_.IsConvex();
+  }
+
   const FloatRoundedRect::Radii& GetRadii() const { return rect_.GetRadii(); }
 
   void SetRadii(const FloatRoundedRect::Radii& radii) { rect_.SetRadii(radii); }
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 62ba6fe..6bd99a4 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
@@ -134149,6 +134149,19 @@
         ],
         {}
        ]
+      ],
+      "gap-decorations-004.html": [
+       "c5d1e3f99cc2b0df01bfc364befb9288c619fc12",
+       [
+        null,
+        [
+         [
+          "/css/css-gaps/agnostic/gap-decorations-003-ref.html",
+          "=="
+         ]
+        ],
+        {}
+       ]
       ]
      },
      "flex": {
@@ -153845,7 +153858,7 @@
       ]
      ],
      "tiled-gradients.html": [
-      "46cc842648138b30274b5ee3f18ce7c636e37462",
+      "b41dd70c9a9136123792caf02a0ea00dca03773d",
       [
        null,
        [
@@ -153861,11 +153874,11 @@
           [
            [
             0,
-            1
+            255
            ],
            [
             0,
-            40000
+            564
            ]
           ]
          ]
@@ -350732,6 +350745,10 @@
        "52e45c4f65f01e5eb98a98590788a0af7bcf7230",
        []
       ],
+      "gap-decorations-004-ref.html": [
+       "52e45c4f65f01e5eb98a98590788a0af7bcf7230",
+       []
+      ],
       "gap-decorations-006-ref.html": [
        "09bef415d402fb2cf9025bdfc6bd887902a439a9",
        []
@@ -408483,12 +408500,6 @@
        []
       ]
      },
-     "serializing-html-fragments": {
-      "serializing-expected.txt": [
-       "90f4612b1ad7f6ad4dd22f7ce319e12e56a1bc29",
-       []
-      ]
-     },
      "speculative-charset": {
       "support": {
        "script.py": [
@@ -467721,7 +467732,16 @@
       ]
      ],
      "comp_name_from_content.html": [
-      "6626eecb0ddd8f6e5709350a93cfd384994efff4",
+      "cd108a190e3609cd3eb0644eb12251f3390982da",
+      [
+       null,
+       {
+        "testdriver": true
+       }
+      ]
+     ],
+     "comp_name_from_content_alt_counter_invalidation.html": [
+      "d659a6e7b3c713c618d002ee08b4c330f2b23494",
       [
        null,
        {
@@ -500020,7 +500040,7 @@
        ]
       ],
       "at-container-style-parsing.html": [
-       "2e6722335c2c307343b0cc36660e22e87ff620bf",
+       "2890727ad4ce3a3e8fb397e0707cc0a5b7bc3e69",
        [
         null,
         {}
@@ -531905,7 +531925,7 @@
        ]
       ],
       "auto-name.html": [
-       "2d11985b3986b479304d5de0195eea1840148868",
+       "3eb5ea1808a1b679be3f85213a054cfc0e4b49dc",
        [
         null,
         {}
@@ -531926,7 +531946,7 @@
        ]
       ],
       "document-element-start-view-transition.html": [
-       "42f87f93a07fac6715d422dea4d1e5835437858e",
+       "380b67213a6a684bcd5ea2a065933cac5a7dd42d",
        [
         null,
         {}
@@ -634679,7 +634699,7 @@
     },
     "cross-origin-opener-policy": {
      "blob-popup.https.html": [
-      "eda150eb34880a332269666cf388c40152bc17ff",
+      "164bf2429bffd72d858791344e177871804138aa",
       [
        null,
        {}
@@ -634693,7 +634713,7 @@
       ]
      ],
      "coep-navigate-popup.https.html": [
-      "714a4b6c4270900282328af1aacadc8592a0b44e",
+      "f9b575fccc255db70afe38a2872a64c1af16f115",
       [
        "html/cross-origin-opener-policy/coep-navigate-popup.https.html?0-1",
        {
@@ -634891,7 +634911,7 @@
      ],
      "historical": {
       "coep-navigate-popup-unsafe-inherit.https.html": [
-       "8368dc4c81129d2d15e700198e4d4fff659cd0f6",
+       "ce8c363f09190eca93ce60bdb44959eb4d819afa",
        [
         null,
         {
@@ -659667,7 +659687,7 @@
         {}
        ]
       ],
-      "serializing-lt-gt.tentative.html": [
+      "serializing-lt-gt.html": [
        "6c74b443a838267be7f0d2ad8a8a80964c57209f",
        [
         null,
@@ -659675,7 +659695,7 @@
        ]
       ],
       "serializing.html": [
-       "1bccbf560880d98287c57046e84a449b20c39d90",
+       "e0473f968017eb536f3e2bb62ccd34dd0cdaeeb8",
        [
         null,
         {}
diff --git a/third_party/blink/web_tests/external/wpt/css/css-gaps/agnostic/gap-decorations-004-ref.html b/third_party/blink/web_tests/external/wpt/css/css-gaps/agnostic/gap-decorations-004-ref.html
new file mode 100644
index 0000000..52e45c4f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-gaps/agnostic/gap-decorations-004-ref.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<style>
+    body {
+        margin: 0px;
+    }
+    #current {
+        columns: 6;
+        column-gap: 2px;
+        column-rule-style: solid;
+        column-rule-width: 2px;
+        column-fill: auto;
+        height: 20px;
+        column-rule-color: hotpink;
+        width: 72px;
+        height: 20px;
+    }
+    .items {
+        background-color: lightgreen;
+        height: 20px
+    }
+</style>
+<body>
+    <div id="current">
+        <div class="items"></div>
+        <div class="items"></div>
+        <div class="items"></div>
+        <div class="items"></div>
+        <div class="items"></div>
+        <div class="items"></div>
+    </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-gaps/agnostic/gap-decorations-004.html b/third_party/blink/web_tests/external/wpt/css/css-gaps/agnostic/gap-decorations-004.html
new file mode 100644
index 0000000..c5d1e3f9
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-gaps/agnostic/gap-decorations-004.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+    <title>column-rule-color declared with repeaters doesn't crash when recomputing</title>
+    <link rel="match" href="gap-decorations-003-ref.html">
+    <link rel="help" href="https://drafts.csswg.org/css-color-4/#resolving-other-colors">
+    <link rel="author" href="mailto:javiercon@microsoft.com">
+</head>
+<style>
+    body {
+        margin: 0px;
+    }
+    #current {
+        color: firebrick;
+        columns: 6;
+        column-gap: 2px;
+        column-rule-style: solid;
+        column-rule-width: 2px;
+        column-fill: auto;
+        height: 20px;
+        column-rule-color: gold;
+        width: 72px;
+        height: 20px;
+    }
+    .items {
+        background-color: lightgreen;
+        height: 20px
+    }
+</style>
+<body>
+    <div id="current">
+        <div class="items"></div>
+        <div class="items"></div>
+        <div class="items"></div>
+        <div class="items"></div>
+        <div class="items"></div>
+        <div class="items"></div>
+    </div>
+</body>
+<script>
+    // Use double requestAnimationFrame to remove need of setTimeout.
+    // Wait for the first frame to ensure that the style is computed.
+    requestAnimationFrame(() => {
+        // Wait for the second frame to ensure that the style is painted.
+        requestAnimationFrame(() => {
+            document.getElementById("current").style.columnRuleColor = "hotpink";
+            document.documentElement.classList.remove("reftest-wait");
+        });
+    });
+</script>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-view-transitions/scoped/auto-name.html b/third_party/blink/web_tests/external/wpt/css/css-view-transitions/scoped/auto-name.html
index 2d11985b..3eb5ea1 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-view-transitions/scoped/auto-name.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-view-transitions/scoped/auto-name.html
@@ -59,6 +59,7 @@
     const vt = element.startViewTransition(() => {
       element.style.flexDirection = 'column';
     });
+    assert_equals(vt.transitionRoot, element);
     await vt.ready;
     const results =
         document.getAnimations().map((a) => {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-view-transitions/scoped/document-element-start-view-transition.html b/third_party/blink/web_tests/external/wpt/css/css-view-transitions/scoped/document-element-start-view-transition.html
index 42f87f9..380b672 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-view-transitions/scoped/document-element-start-view-transition.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-view-transitions/scoped/document-element-start-view-transition.html
@@ -60,6 +60,9 @@
         target.className = 'update-2';
       });
 
+      assert_equals(vt1.transitionRoot, document.documentElement);
+      assert_equals(vt2.transitionRoot, document.documentElement);
+
       await verifyAbortedTransition(vt1.ready);
       await vt2.ready;
 
diff --git a/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/blob-popup.https.html b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/blob-popup.https.html
index eda150e..164bf242 100644
--- a/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/blob-popup.https.html
+++ b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/blob-popup.https.html
@@ -3,38 +3,44 @@
 <script src=/resources/testharness.js></script>
 <script src=/resources/testharnessreport.js></script>
 <script src=/common/get-host-info.sub.js></script>
-<script src="/common/utils.js"></script>
+<script src=/common/utils.js></script>
+<script src=/common/dispatcher/dispatcher.js></script>
 <script>
-async_test(t => {
-  window.test = t; // Make the test available globally so the blob URL can use it
+promise_test(async t => {
   window.furtherPopup = null;
 
-  const bc = new BroadcastChannel(token());
-  bc.onmessage = t.step_func_done(({ data }) => {
-    assert_equals(data.name.length, 0);
-    assert_false(data.opener);
-    assert_true(furtherPopup.closed);
-  });
+  const responseToken = token();
+  const iframeToken = token();
 
   const blobContents = `<script>
-const w = window.open("${get_host_info().HTTPS_REMOTE_ORIGIN}/html/cross-origin-opener-policy/resources/coop-coep.py?coop=x&coep=x&channel=${bc.name}", "${bc.name}");
+const w = window.open("${get_host_info().HTTPS_REMOTE_ORIGIN}/html/cross-origin-opener-policy/resources/coop-coep.py?coop=x&coep=x&responseToken=${responseToken}&iframeToken=${iframeToken}", "${responseToken}");
 window.opener.furtherPopup = w;
 <\/script>`;
   const blob = new Blob([blobContents], { type: "text/html" });
   const blobURL = URL.createObjectURL(blob);
   const popup = window.open(blobURL);
-  t.add_cleanup(() => {
+  t.add_cleanup(async () => {
     // Close the popups once the test is complete.
     // The browsing context of the second popup is closed hence use the
     //  broadcast channel to trigger the closure.
-    bc.postMessage("close");
+    await send(iframeToken, "close");
     popup.close();
   });
+
+  let popupOnloadHappened = false;
   popup.onload = t.step_func(() => {
     assert_equals(popup.opener, window);
     assert_equals(popup.location.href, blobURL);
     assert_equals(popup.document.URL, blobURL);
     assert_equals(popup.origin, window.origin);
+    popupOnloadHappened = true;
   });
+
+  const data = JSON.parse(await receive(responseToken));
+
+  assert_true(popupOnloadHappened);
+  assert_equals(data.name.length, 0);
+  assert_false(data.opener);
+  assert_true(furtherPopup.closed);
 });
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/coep-navigate-popup.https.html b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/coep-navigate-popup.https.html
index 714a4b6..f9b575f 100644
--- a/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/coep-navigate-popup.https.html
+++ b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/coep-navigate-popup.https.html
@@ -6,9 +6,11 @@
 <meta name=variant content=?4-last>
 <script src=/resources/testharness.js></script>
 <script src=/resources/testharnessreport.js></script>
-<script src="/common/subset-tests.js"></script>
-<script src="/common/get-host-info.sub.js"></script>
-<script src="resources/common.js"></script>
+<script src=/common/subset-tests.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src=/common/utils.js></script>
+<script src=resources/common.js></script>
+<script src=/common/dispatcher/dispatcher.js></script>
 <script>
 [
   {
@@ -55,17 +57,18 @@
 
   ["same-origin", "same-site"].forEach(site => {
     const title = `Popup navigating to ${site} with ${variant.title}`;
-    const channel = title.replace(/ /g,"-");
+    const responseToken = token();
+    const iframeToken = token();
     const navigateHost = site === "same-origin" ? SAME_ORIGIN : SAME_SITE;
-    const navigateURL = `${navigateHost.origin}/html/cross-origin-opener-policy/resources/coop-coep.py?coop=${variant.coop}&coep=${variant.coep}&channel=${channel}`;
+    const navigateURL = `${navigateHost.origin}/html/cross-origin-opener-policy/resources/coop-coep.py?coop=${variant.coop}&coep=${variant.coep}&responseToken=${responseToken}&iframeToken=${iframeToken}`;
     const opener = site === "same-origin" ? variant.opener : false;
 
-    async_test(t => {
+    promise_test(t => {
       // For each test we open a COOP: same-origin/COEP: require-corp document in a popup and then
       // navigate that to either a same-origin (site=="same-origin") or same-site (site=="same-site")
       // document whose COOP and COEP are set as per the top-most array. We then verify that this
       // document has the correct opener for its specific setup.
-      url_test(t, `${SAME_ORIGIN.origin}/html/cross-origin-opener-policy/resources/coop-coep.py?coop=same-origin&coep=require-corp&navigate=${encodeURIComponent(navigateURL)}`, channel, opener);
+      return dispatcher_url_test(t, `${SAME_ORIGIN.origin}/html/cross-origin-opener-policy/resources/coop-coep.py?coop=same-origin&coep=require-corp&navigate=${encodeURIComponent(navigateURL)}`, responseToken, iframeToken, opener, undefined, () => t.done());
     }, title);
   });
 });
diff --git a/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/historical/coep-navigate-popup-unsafe-inherit.https.html b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/historical/coep-navigate-popup-unsafe-inherit.https.html
index 8368dc4c..ce8c363 100644
--- a/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/historical/coep-navigate-popup-unsafe-inherit.https.html
+++ b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/historical/coep-navigate-popup-unsafe-inherit.https.html
@@ -3,9 +3,11 @@
 <meta name=timeout content=long>
 <script src=/resources/testharness.js></script>
 <script src=/resources/testharnessreport.js></script>
-<script src="/common/get-host-info.sub.js"></script>
-<script src="../resources/common.js"></script>
-<script src="/common/subset-tests.js"></script>
+<script src=/common/subset-tests.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src=/common/utils.js></script>
+<script src=../resources/common.js></script>
+<script src=/common/dispatcher/dispatcher.js></script>
 <script>
 [
   {
@@ -23,17 +25,18 @@
 ].forEach((variant) => {
   ["same-origin", "same-site"].forEach((site) => {
     const title = `Popup navigating to ${site} with ${variant.title}`;
-    const channel = title.replace(/ /g,"-");
+    const responseToken = token();
+    const iframeToken = token();
     const navigateHost = site === "same-origin" ? SAME_ORIGIN : SAME_SITE;
-    const navigateURL = `${navigateHost.origin}/html/cross-origin-opener-policy/resources/coop-coep.py?coop=${variant.coop}&coep=${variant.coep}&channel=${channel}`;
+    const navigateURL = `${navigateHost.origin}/html/cross-origin-opener-policy/resources/coop-coep.py?coop=${variant.coop}&coep=${variant.coep}&responseToken=${responseToken}&iframeToken=${iframeToken}`;
     const opener = site === "same-origin" ? variant.opener : false;
 
-    async_test(t => {
+    promise_test(t => {
       // For each test we open a COOP: same-origin/COEP: require-corp document in a popup and then
       // navigate that to either a document with same origin (site=="same-origin") or
       // not-same-origin (site=="same-site") whose COOP and COEP are set as per the top-most array.
       // We then verify that this document has the correct opener for its specific setup.
-      url_test(t, `${SAME_ORIGIN.origin}/html/cross-origin-opener-policy/resources/coop-coep.py?coop=same-origin&coep=require-corp&navigate=${encodeURIComponent(navigateURL)}`, channel, opener);
+      return dispatcher_url_test(t, `${SAME_ORIGIN.origin}/html/cross-origin-opener-policy/resources/coop-coep.py?coop=same-origin&coep=require-corp&navigate=${encodeURIComponent(navigateURL)}`, responseToken, iframeToken, opener, undefined, () => t.done());
     }, title);
   });
 });
diff --git a/third_party/blink/web_tests/external/wpt/html/syntax/serializing-html-fragments/serializing-expected.txt b/third_party/blink/web_tests/external/wpt/html/syntax/serializing-html-fragments/serializing-expected.txt
deleted file mode 100644
index 90f4612..0000000
--- a/third_party/blink/web_tests/external/wpt/html/syntax/serializing-html-fragments/serializing-expected.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-This is a testharness.js-based test.
-Found 6 FAIL, 0 TIMEOUT, 0 NOTRUN.
-[FAIL] innerHTML 7 <a b="<"></a>
-  assert_equals: expected "<a b=\\"<\\"></a>" but got "<a b=\\"&lt;\\"></a>"
-[FAIL] innerHTML 8 <a b=">"></a>
-  assert_equals: expected "<a b=\\">\\"></a>" but got "<a b=\\"&gt;\\"></a>"
-[FAIL] innerHTML 9 <a href="javascript:&quot;<>&quot;"></a>
-  assert_equals: expected "<a href=\\"javascript:&quot;<>&quot;\\"></a>" but got "<a href=\\"javascript:&quot;&lt;&gt;&quot;\\"></a>"
-[FAIL] outerHTML 7 <span><a b="<"></a></span>
-  assert_equals: expected "<span><a b=\\"<\\"></a></span>" but got "<span><a b=\\"&lt;\\"></a></span>"
-[FAIL] outerHTML 8 <span><a b=">"></a></span>
-  assert_equals: expected "<span><a b=\\">\\"></a></span>" but got "<span><a b=\\"&gt;\\"></a></span>"
-[FAIL] outerHTML 9 <span><a href="javascript:&quot;<>&quot;"></a></span>
-  assert_equals: expected "<span><a href=\\"javascript:&quot;<>&quot;\\"></a></span>" but got "<span><a href=\\"javascript:&quot;&lt;&gt;&quot;\\"></a></span>"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/html/syntax/serializing-html-fragments/serializing-lt-gt.tentative.html b/third_party/blink/web_tests/external/wpt/html/syntax/serializing-html-fragments/serializing-lt-gt.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/html/syntax/serializing-html-fragments/serializing-lt-gt.tentative.html
rename to third_party/blink/web_tests/external/wpt/html/syntax/serializing-html-fragments/serializing-lt-gt.html
diff --git a/third_party/blink/web_tests/external/wpt/html/syntax/serializing-html-fragments/serializing.html b/third_party/blink/web_tests/external/wpt/html/syntax/serializing-html-fragments/serializing.html
index 1bccbf5..e0473f9 100644
--- a/third_party/blink/web_tests/external/wpt/html/syntax/serializing-html-fragments/serializing.html
+++ b/third_party/blink/web_tests/external/wpt/html/syntax/serializing-html-fragments/serializing.html
@@ -48,9 +48,9 @@
 ["<a b=\"&amp;\"></a>", "<span><a b=\"&amp;\"></a></span>"],
 ["<a b=\"&nbsp;\"></a>", "<span><a b=\"&nbsp;\"></a></span>"],
 ["<a b=\"&quot;\"></a>", "<span><a b=\"&quot;\"></a></span>"],
-["<a b=\"<\"></a>", "<span><a b=\"<\"></a></span>"],
-["<a b=\">\"></a>", "<span><a b=\">\"></a></span>"],
-["<a href=\"javascript:&quot;<>&quot;\"></a>", "<span><a href=\"javascript:&quot;<>&quot;\"></a></span>"],
+["<a b=\"&lt;\"></a>", "<span><a b=\"&lt;\"></a></span>"],
+["<a b=\"&gt;\"></a>", "<span><a b=\"&gt;\"></a></span>"],
+["<a href=\"javascript:&quot;&lt;&gt;&quot;\"></a>", "<span><a href=\"javascript:&quot;&lt;&gt;&quot;\"></a></span>"],
 ["<svg xlink:href=\"a\"></svg>", "<span><svg xlink:href=\"a\"></svg></span>"],
 ["<svg xmlns:svg=\"test\"></svg>", "<span><svg xmlns:svg=\"test\"></svg></span>"],
 ["a", "<span>a</span>"],
diff --git a/third_party/blink/web_tests/external/wpt/soft-navigation-heuristics/smoke/tentative/almost-soft-navigation-expected.txt b/third_party/blink/web_tests/external/wpt/soft-navigation-heuristics/smoke/tentative/almost-soft-navigation-expected.txt
deleted file mode 100644
index 36456e163..0000000
--- a/third_party/blink/web_tests/external/wpt/soft-navigation-heuristics/smoke/tentative/almost-soft-navigation-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Doesn't paint because the element is hidden.
-  assert_equals: Expected only one soft navigation (test_id=no-paint). expected 1 but got 2
-Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/fast/block/float/float-in-float-hit-testing-expected.txt b/third_party/blink/web_tests/fast/block/float/float-in-float-hit-testing-expected.txt
deleted file mode 100644
index dd064d8..0000000
--- a/third_party/blink/web_tests/fast/block/float/float-in-float-hit-testing-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-CONSOLE MESSAGE: A soft navigation has been detected: float-in-float-hit-testing.html#
diff --git a/third_party/blink/web_tests/fast/borders/cornerShapeOutlineDouble-expected.png b/third_party/blink/web_tests/fast/borders/cornerShapeOutlineDouble-expected.png
new file mode 100644
index 0000000..e82be9d7
--- /dev/null
+++ b/third_party/blink/web_tests/fast/borders/cornerShapeOutlineDouble-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/fast/borders/cornerShapeOutlineDouble.html b/third_party/blink/web_tests/fast/borders/cornerShapeOutlineDouble.html
new file mode 100644
index 0000000..6b3f3c74
--- /dev/null
+++ b/third_party/blink/web_tests/fast/borders/cornerShapeOutlineDouble.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<head>
+	<style type="text/css">
+		div {
+			width: 200px;
+			height: 200px;
+			outline: 15px double purple;
+			margin: 20px;
+			border-radius: 140px;
+			corner-shape: scoop bevel notch squircle;
+			background-color: green;
+		}
+	</style>
+</head>
+<body>
+	<div>
+		&nbsp;
+	</div>
+</body>
+</html>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/fast/borders/cornerShapeOutlineGroove-expected.png b/third_party/blink/web_tests/fast/borders/cornerShapeOutlineGroove-expected.png
new file mode 100644
index 0000000..1613efa
--- /dev/null
+++ b/third_party/blink/web_tests/fast/borders/cornerShapeOutlineGroove-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/fast/borders/cornerShapeOutlineGroove.html b/third_party/blink/web_tests/fast/borders/cornerShapeOutlineGroove.html
new file mode 100644
index 0000000..c7353ef
--- /dev/null
+++ b/third_party/blink/web_tests/fast/borders/cornerShapeOutlineGroove.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<head>
+	<style type="text/css">
+		div {
+			width: 200px;
+			height: 200px;
+			outline: 15px groove purple;
+			margin: 20px;
+			border-radius: 140px;
+			corner-shape: scoop bevel notch squircle;
+			background-color: green;
+		}
+	</style>
+</head>
+<body>
+	<div>
+		&nbsp;
+	</div>
+</body>
+</html>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/fast/borders/cornerShapeOutlineNegative-expected.png b/third_party/blink/web_tests/fast/borders/cornerShapeOutlineNegative-expected.png
new file mode 100644
index 0000000..3e581e2
--- /dev/null
+++ b/third_party/blink/web_tests/fast/borders/cornerShapeOutlineNegative-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/fast/borders/cornerShapeOutlineNegative.html b/third_party/blink/web_tests/fast/borders/cornerShapeOutlineNegative.html
new file mode 100644
index 0000000..3afbebc
--- /dev/null
+++ b/third_party/blink/web_tests/fast/borders/cornerShapeOutlineNegative.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<head>
+	<style type="text/css">
+		div {
+			width: 200px;
+			height: 200px;
+			outline: 5px solid purple;
+			outline-offset: -15px;
+			margin: 20px;
+			border-radius: 140px;
+			corner-shape: scoop bevel notch squircle;
+			background-color: green;
+		}
+	</style>
+</head>
+<body>
+	<div>
+		&nbsp;
+	</div>
+</body>
+</html>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/fast/borders/cornerShapeOutlineOffset-expected.png b/third_party/blink/web_tests/fast/borders/cornerShapeOutlineOffset-expected.png
new file mode 100644
index 0000000..41860dd9
--- /dev/null
+++ b/third_party/blink/web_tests/fast/borders/cornerShapeOutlineOffset-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/fast/borders/cornerShapeOutlineOffset.html b/third_party/blink/web_tests/fast/borders/cornerShapeOutlineOffset.html
new file mode 100644
index 0000000..0506815
--- /dev/null
+++ b/third_party/blink/web_tests/fast/borders/cornerShapeOutlineOffset.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<head>
+	<style type="text/css">
+		div {
+			width: 200px;
+			height: 200px;
+			outline: 15px solid purple;
+			outline-offset: 5px;
+			margin: 20px;
+			border-radius: 140px;
+			corner-shape: scoop bevel notch squircle;
+			background-color: green;
+		}
+	</style>
+</head>
+<body>
+	<div>
+		&nbsp;
+	</div>
+</body>
+</html>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/fast/borders/cornerShapeOutlineRidge-expected.png b/third_party/blink/web_tests/fast/borders/cornerShapeOutlineRidge-expected.png
new file mode 100644
index 0000000..81437f0
--- /dev/null
+++ b/third_party/blink/web_tests/fast/borders/cornerShapeOutlineRidge-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/fast/borders/cornerShapeOutlineRidge.html b/third_party/blink/web_tests/fast/borders/cornerShapeOutlineRidge.html
new file mode 100644
index 0000000..fcc2976
--- /dev/null
+++ b/third_party/blink/web_tests/fast/borders/cornerShapeOutlineRidge.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<head>
+	<style type="text/css">
+		div {
+			width: 200px;
+			height: 200px;
+			outline: 15px ridge purple;
+			margin: 20px;
+			border-radius: 140px;
+			corner-shape: scoop bevel notch squircle;
+			background-color: green;
+		}
+	</style>
+</head>
+<body>
+	<div>
+		&nbsp;
+	</div>
+</body>
+</html>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/fast/borders/cornerShapeOutlineSolid-expected.png b/third_party/blink/web_tests/fast/borders/cornerShapeOutlineSolid-expected.png
new file mode 100644
index 0000000..08c2488
--- /dev/null
+++ b/third_party/blink/web_tests/fast/borders/cornerShapeOutlineSolid-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/fast/borders/cornerShapeOutlineSolid.html b/third_party/blink/web_tests/fast/borders/cornerShapeOutlineSolid.html
new file mode 100644
index 0000000..96c9b603
--- /dev/null
+++ b/third_party/blink/web_tests/fast/borders/cornerShapeOutlineSolid.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<head>
+	<style type="text/css">
+		div {
+			width: 200px;
+			height: 200px;
+			outline: 15px solid purple;
+			margin: 20px;
+			border-radius: 140px;
+			corner-shape: scoop bevel notch squircle;
+			background-color: green;
+		}
+	</style>
+</head>
+<body>
+	<div>
+		&nbsp;
+	</div>
+</body>
+</html>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/fast/events/touch/gesture/gesture-click-on-inline-continations-expected.txt b/third_party/blink/web_tests/fast/events/touch/gesture/gesture-click-on-inline-continations-expected.txt
index b4c2667..c5a48cc 100644
--- a/third_party/blink/web_tests/fast/events/touch/gesture/gesture-click-on-inline-continations-expected.txt
+++ b/third_party/blink/web_tests/fast/events/touch/gesture/gesture-click-on-inline-continations-expected.txt
@@ -1,4 +1,3 @@
-CONSOLE MESSAGE: A soft navigation has been detected: gesture-click-on-inline-continations.html#
 The test succeeds if this does not hang or crash when getting the y conner's position from inline renderer. This test passes if it does not timeout.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
diff --git a/third_party/blink/web_tests/fast/forms/access-key-expected.txt b/third_party/blink/web_tests/fast/forms/access-key-expected.txt
index e6c2555..e1685bb6 100644
--- a/third_party/blink/web_tests/fast/forms/access-key-expected.txt
+++ b/third_party/blink/web_tests/fast/forms/access-key-expected.txt
@@ -1,4 +1,3 @@
-CONSOLE MESSAGE: A soft navigation has been detected: access-key.html#
 This test checks to see if accesskey attributes works on the specified elements.
 
 If this test passes you should see 1 - 9 and a, b and c clicked or focussed.
diff --git a/third_party/blink/web_tests/hittesting/text-overflow-inline-image-expected.txt b/third_party/blink/web_tests/hittesting/text-overflow-inline-image-expected.txt
index 13ee25ba..b1f318bb 100644
--- a/third_party/blink/web_tests/hittesting/text-overflow-inline-image-expected.txt
+++ b/third_party/blink/web_tests/hittesting/text-overflow-inline-image-expected.txt
@@ -1,4 +1,3 @@
-CONSOLE MESSAGE: A soft navigation has been detected: text-overflow-inline-image.html#
 this is a link
 clicked
 
diff --git a/third_party/blink/web_tests/platform/fuchsia/fast/block/float/float-in-float-hit-testing-expected.txt b/third_party/blink/web_tests/platform/fuchsia/fast/block/float/float-in-float-hit-testing-expected.txt
deleted file mode 100644
index a096403..0000000
--- a/third_party/blink/web_tests/platform/fuchsia/fast/block/float/float-in-float-hit-testing-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-CONSOLE MESSAGE: A soft navigation has been detected: http://127.0.0.1:8000/third_party/blink/web_tests/fast/block/float/float-in-float-hit-testing.html#
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index a739a31..6eb7602 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -10604,6 +10604,7 @@
     attribute @@toStringTag
     getter finished
     getter ready
+    getter transitionRoot
     getter types
     getter updateCallbackDone
     method constructor
diff --git a/third_party/blink/web_tests/wpt_internal/ai/language-model-api-availability-available.https.window.js b/third_party/blink/web_tests/wpt_internal/ai/language-model-api-availability-available.https.window.js
new file mode 100644
index 0000000..0e79d98
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/ai/language-model-api-availability-available.https.window.js
@@ -0,0 +1,50 @@
+// META: title=Language Model Availability Available
+// META: script=resources/utils.js
+// META: timeout=long
+
+'use strict';
+
+promise_test(async () => {
+  await ensureLanguageModel();
+}, 'LanguageModel.availability() is available with no options');
+
+promise_test(async () => {
+  await ensureLanguageModel();
+  // An array of supported test option values.
+  const kCreateOptionsSpec = [
+    {topK: [1, 1.5, 2, 3, 99]},  // Nominally int 1-10+.
+    {temperature: [0, 0.5, 1, 2]},  // Nominally float 0-1.
+    {expectedInputs: [undefined, [], [{type: 'text'}], [{type: 'text', languages: ['en']}], ]},
+  ];
+  for (const options of generateOptionCombinations(kCreateOptionsSpec)) {
+    const availability = await LanguageModel.availability(options);
+    assert_in_array(availability, kValidAvailabilities, options);
+  }
+}, 'LanguageModel.availability() returns available with supported options');
+
+promise_test(async () => {
+  await ensureLanguageModel();
+  // An array of unsupported test options.
+  const kUnsupportedCreateOptions = [
+    { expectedInputs: [{type: 'text', languages: ['unk']}] },  // Language not supported.
+    { topK: 0, temperature: 0.5 },  // zero topK not supported.
+    { topK: -3, temperature: 0.5 },  // negative topK not supported.
+    { topK: 3, temperature: -0.5 },  // negative temperature not supported.
+    { topK: 3 },  // topK without temperature not supported.
+    { temperature: 0.5 },  // temperature without topK not supported.
+  ];
+  for (const options of kUnsupportedCreateOptions) {
+    assert_equals(await LanguageModel.availability(options), 'unavailable', options);
+  }
+}, 'LanguageModel.availability() returns unavailable with unsupported options');
+
+promise_test(async t => {
+  await ensureLanguageModel();
+  // An array of invalid test options.
+  const kInvalidCreateOptions = [
+    { expectedInputs: [{type: 'soup'}]},  // Type not supported.
+  ];
+  for (const options of kInvalidCreateOptions) {
+    await promise_rejects_js(t, TypeError, LanguageModel.availability(options));
+  }
+}, 'LanguageModel.availability() rejects with invalid options');
diff --git a/third_party/blink/web_tests/wpt_internal/ai/language-model-api-availability.https.window.js b/third_party/blink/web_tests/wpt_internal/ai/language-model-api-availability.https.window.js
index 3c76d5b5..4a1c79e 100644
--- a/third_party/blink/web_tests/wpt_internal/ai/language-model-api-availability.https.window.js
+++ b/third_party/blink/web_tests/wpt_internal/ai/language-model-api-availability.https.window.js
@@ -4,68 +4,34 @@
 
 'use strict';
 
-promise_test(async t => {
+promise_test(async () => {
   assert_true(!!LanguageModel);
-  assert_not_equals(
-    await LanguageModel.availability(),
-    'unavailable'
-  );
-  assert_not_equals(
-    await LanguageModel.availability({ expectedInputs: [{ type: "text", languages: ["en"] }]}),
-    'unavailable',
-    'availability() with supported language should not return unavailable.'
-  );
-  assert_equals(
-    await LanguageModel.availability({ expectedInputs: [{ type: "text", languages: ["ja"] }]}),
-    'unavailable',
-    'availability() with unsupported language should return unavailable.'
-  );
-  assert_not_equals(
-    await LanguageModel.availability({ topK: 3, temperature: 0.5 }),
-    'unavailable',
-    'availability() with valid topK and temperature should not return unavailable.'
-  );
-  assert_equals(
-    await LanguageModel.availability({ topK: 0, temperature: 0.5 }),
-    'unavailable',
-    'availability() with zero topK should return unavailable.'
-  );
-  assert_equals(
-    await LanguageModel.availability({ topK: -3, temperature: 0.5 }),
-    'unavailable',
-    'availability() with negative topK should return unavailable.'
-  );
-  assert_equals(
-    await LanguageModel.availability({ topK: 3, temperature: -0.1 }),
-    'unavailable',
-    'availability() with negative temperature should return unavailable.'
-  );
-  assert_equals(
-    await LanguageModel.availability({ topK: 3 }),
-    'unavailable',
-    'availability() with only topK should return unavailable.'
-  );
-  assert_equals(
-    await LanguageModel.availability({ temperature: 0.5 }),
-    'unavailable',
-    'availability() with only temperature should return unavailable.'
-  );
-  assert_not_equals(
-    await LanguageModel.availability({
-      topK: 3,
-      temperature: 1.5,
-      expectedInputs: [{ type: "text", languages: ["en"] }]
-    }),
-    'unavailable',
-    'availability() with valid sampling params and supported language should not return unavailable.'
-  );
-  assert_equals(
-    await LanguageModel.availability({
-      topK: 3,
-      temperature: -1,
-      expectedInputs: [{ type: "text", languages: ["en"] }]
-    }),
-    'unavailable',
-    'availability() with invalid sampling params and supported language should return unavailable.'
-  );
-});
+  assert_equals(typeof LanguageModel.availability, 'function');
+}, 'LanguageModel.availability() is defined');
+
+promise_test(async () => {
+  const availability = await LanguageModel.availability();
+  assert_in_array(availability, kValidAvailabilities);
+}, 'LanguageModel.availability() returns a valid value with no options');
+
+promise_test(async () => {
+  // An array of plausible test option values.
+  const kCreateOptionsSpec = [
+    {topK: [undefined, -2, 0, 1, 1.5, 3, 99]},  // Nominally int 1-10+.
+    {temperature: [undefined, -0.5, 0, 0.6, 1, 7]},  // Nominally float 0-1.
+    {expectedInputs: [undefined, [], [{type: 'text'}],
+       [{type: 'text'}, {type: 'audio'}, {type: 'image'}],
+       [{type: 'text', languages: ['en', 'ja', 'ko']}],
+       [{type: 'audio', languages: ['es']}, {type: 'image', languages: ['fr']}],
+    ]},
+    {initialPrompts: [undefined, [], [{role: 'system', content: 'have fun'}],
+      [{role: 'system', content: 'have fun'}, {role: 'user', content: 'be good'}],
+      [{role: 'system', content: 'be good'}, {role: 'system', content: 'be bad'}],
+      [{role: 'system', content: 'have fun'}, {role: 'system', content: 'be bad'}],
+    ]},
+  ];
+  for (const options of generateOptionCombinations(kCreateOptionsSpec)) {
+    const availability = await LanguageModel.availability(options);
+    assert_in_array(availability, kValidAvailabilities, options);
+  }
+}, 'LanguageModel.availability() returns a valid value with plausible options');
diff --git a/third_party/blink/web_tests/wpt_internal/ai/resources/utils.js b/third_party/blink/web_tests/wpt_internal/ai/resources/utils.js
index 66c7d19..1cdefbc 100644
--- a/third_party/blink/web_tests/wpt_internal/ai/resources/utils.js
+++ b/third_party/blink/web_tests/wpt_internal/ai/resources/utils.js
@@ -1,5 +1,47 @@
+const kValidAvailabilities =
+    ['unavailable', 'downloadable', 'downloading', 'available'];
+const kAvailableAvailabilities = ['downloadable', 'downloading', 'available'];
+
 const kTestPrompt = 'Please write a sentence in English.';
 
+// Takes an array of dictionaries mapping keys to value arrays, e.g.:
+//   [ {Shape: ["Square", "Circle", undefined]}, {Count: [1, 2]} ]
+// Returns an array of dictionaries with all value combinations, i.e.:
+//  [ {Shape: "Square", Count: 1}, {Shape: "Square", Count: 2},
+//    {Shape: "Circle", Count: 1}, {Shape: "Circle", Count: 2},
+//    {Shape: undefined, Count: 1}, {Shape: undefined, Count: 2} ]
+// Omits dictionary members when the value is undefined; supports array values.
+function generateOptionCombinations(optionsSpec) {
+  // 1. Extract keys from the input specification.
+  const keys = optionsSpec.map(o => Object.keys(o)[0]);
+  // 2. Extract the arrays of possible values for each key.
+  const valueArrays = optionsSpec.map(o => Object.values(o)[0]);
+  // 3. Compute the Cartesian product of the value arrays using reduce.
+  const valueCombinations = valueArrays.reduce((accumulator, currentValues) => {
+    // Init the empty accumulator (first iteration), with single-element
+    // arrays.
+    if (accumulator.length === 0) {
+      return currentValues.map(value => [value]);
+    }
+    // Otherwise, expand existing combinations with current values.
+    return accumulator.flatMap(
+        existingCombo => currentValues.map(
+            currentValue => [...existingCombo, currentValue]));
+  }, []);
+
+  // 4. Map each value combination to a result dictionary, skipping
+  // undefined.
+  return valueCombinations.map(combination => {
+    const result = {};
+    keys.forEach((key, index) => {
+      if (combination[index] !== undefined) {
+        result[key] = combination[index];
+      }
+    });
+    return result;
+  });
+}
+
 const testSession = async (session) => {
   if (typeof session.topK !== 'number') {
     return {success: false, error: 'session topK property is not properly set'};
@@ -158,14 +200,14 @@
   return getPrompt(left);
 };
 
-const ensureLanguageModel = async () => {
-  // Make sure the prompt api is enabled.
+async function ensureLanguageModel(options = {}) {
   assert_true(!!LanguageModel);
-  // Make sure the session could be created.
-  const availability = await LanguageModel.availability();
-  // TODO(crbug.com/376789810): make it a PRECONDITION_FAILED if the model is
-  // not ready.
-  assert_not_equals(availability, 'unavailable');
+  const availability = await LanguageModel.availability(options);
+  assert_in_array(availability, kValidAvailabilities);
+  // Yield PRECONDITION_FAILED if the API is unavailable on this device.
+  if (availability == 'unavailable') {
+    throw new OptionalFeatureUnsupportedError("API unavailable on this device");
+  }
 };
 
 async function testMonitor(createFunc, options = {}) {
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-conditional/container-queries/anchored/anchored-fallback-display-change.html b/third_party/blink/web_tests/wpt_internal/css/css-conditional/container-queries/anchored/anchored-fallback-display-change.html
new file mode 100644
index 0000000..0dbeea3
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/css/css-conditional/container-queries/anchored/anchored-fallback-display-change.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<title>CSS Conditional Test: @container anchored(fallback) changes display type</title>
+<link rel="help" href="https://drafts.csswg.org/css-conditional-5/#container-rule">
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/8171">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/css-conditional/container-queries/support/cq-testcommon.js"></script>
+<style>
+  body { margin: 0; }
+  #anchor {
+    anchor-name: --a;
+    margin-top: 100px;
+    width: 100px;
+    height: 100px;
+  }
+  #anchored {
+    position: absolute;
+    position-anchor: --a;
+    position-area: top;
+    position-try-fallbacks: flip-block;
+    width: 100px;
+    height: 100px;
+    container-type: anchored;
+  }
+  #t1 { height: 100%; }
+  @container anchored(fallback: 0) {
+    #t1 { display: none; }
+  }
+  @container anchored(fallback: 1) {
+    #t1 { display: block; }
+  }
+</style>
+<div id="anchor"></div>
+<div id="anchored">
+  <span id="t1"></span>
+</div>
+<script>
+  test(() => {
+    assert_equals(anchored.offsetTop, 0, "Non-fallback rendering");
+    assert_equals(t1.offsetHeight, 0, "display:none - zero offsetHeight");
+  }, "@container anchored() without applied fallback");
+
+  test(() => {
+    anchor.style.marginTop = "50px"; // Trigger fallback
+    assert_equals(anchored.offsetTop, 150, "Anchored element rendered below anchor");
+      assert_equals(t1.offsetHeight, 100, "Fallback applies display:block");
+  }, "@container anchored() with fallback applied");
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-flexbox/balance/balance-available-size-001-ref.html b/third_party/blink/web_tests/wpt_internal/css/css-flexbox/balance/balance-available-size-001-ref.html
new file mode 100644
index 0000000..ff4810fb
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/css/css-flexbox/balance/balance-available-size-001-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<style>
+#flex {
+  display: flex;
+  flex-direction: column;
+  flex-wrap: wrap;
+  gap: 20px;
+  width: 220px;
+  height: 220px;
+}
+
+#flex > div {
+  background: green;
+  width: 100px;
+  height: 100px;
+}
+</style>
+<p>There should be three (3) green squares below.</p>
+<div id="flex">
+  <div></div>
+  <div></div>
+  <div></div>
+</div>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-flexbox/balance/balance-available-size-001.html b/third_party/blink/web_tests/wpt_internal/css/css-flexbox/balance/balance-available-size-001.html
new file mode 100644
index 0000000..ed0fa85
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/css/css-flexbox/balance/balance-available-size-001.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/3070">
+<link rel="match" href="balance-available-size-001-ref.html">
+<meta name="assert" content="Tests that the available-size for measuring is smaller than the container.">
+<style>
+#flex {
+  display: flex;
+  flex-direction: column;
+  flex-wrap: balance 2;
+  gap: 20px;
+  width: 220px;
+}
+
+#flex > div {
+  background: green;
+  line-height: 0;
+}
+
+span {
+  display: inline-block;
+  width: 100px;
+  height: 50px;
+}
+</style>
+<p>There should be three (3) green squares below.</p>
+<div id="flex">
+  <div>
+    <span></span><span></span>
+  </div>
+  <div>
+    <span></span><span></span>
+  </div>
+  <div>
+    <span></span><span></span>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-flexbox/balance/balance-available-size-002-ref.html b/third_party/blink/web_tests/wpt_internal/css/css-flexbox/balance/balance-available-size-002-ref.html
new file mode 100644
index 0000000..98de8e1
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/css/css-flexbox/balance/balance-available-size-002-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<style>
+#flex {
+  display: flex;
+  flex-direction: row;
+  flex-wrap: wrap;
+  gap: 20px;
+  width: 220px;
+  height: 220px;
+}
+
+#flex > div {
+  background: green;
+  width: 100px;
+  height: 100px;
+}
+</style>
+<p>There should be three (3) green squares below.</p>
+<div id="flex">
+  <div></div>
+  <div></div>
+  <div></div>
+</div>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-flexbox/balance/balance-available-size-002.html b/third_party/blink/web_tests/wpt_internal/css/css-flexbox/balance/balance-available-size-002.html
new file mode 100644
index 0000000..465bd13
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/css/css-flexbox/balance/balance-available-size-002.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/3070">
+<link rel="match" href="balance-available-size-002-ref.html">
+<meta name="assert" content="Tests that the available-size for measuring is smaller than the container.">
+<style>
+#flex {
+  display: flex;
+  flex-direction: row;
+  flex-wrap: balance 2;
+  gap: 20px;
+  height: 220px;
+}
+
+#flex > div {
+  writing-mode: vertical-rl;
+  background: green;
+  line-height: 0;
+}
+
+span {
+  display: inline-block;
+  width: 50px;
+  height: 100px;
+}
+</style>
+<p>There should be three (3) green squares below.</p>
+<div id="flex">
+  <div>
+    <span></span><span></span>
+  </div>
+  <div>
+    <span></span><span></span>
+  </div>
+  <div>
+    <span></span><span></span>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-flexbox/balance/balance-min-line-count-001.html b/third_party/blink/web_tests/wpt_internal/css/css-flexbox/balance/balance-min-line-count-001.html
new file mode 100644
index 0000000..7f02c206
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/css/css-flexbox/balance/balance-min-line-count-001.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/3070">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<meta name="assert" content="Tests balancing with a minimum number of lines.">
+<style>
+div > div {
+  background: green;
+  width: 100px;
+  height: 25px;
+}
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div style="display: flex; width: 200px; flex-wrap: balance 4;">
+  <div></div>
+  <div></div>
+  <div></div>
+  <div></div>
+</div>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-flexbox/balance/balance-min-line-count-002.html b/third_party/blink/web_tests/wpt_internal/css/css-flexbox/balance/balance-min-line-count-002.html
new file mode 100644
index 0000000..ba8f975f
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/css/css-flexbox/balance/balance-min-line-count-002.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/3070">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<meta name="assert" content="Tests balancing with a minimum number of lines.">
+<style>
+div > div {
+  background: green;
+  width: 50px;
+  height: 50px;
+}
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div style="display: flex; width: 200px; flex-wrap: balance 2;">
+  <div></div>
+  <div></div>
+  <div></div>
+  <div></div>
+</div>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-flexbox/balance/balance-min-line-count-003-ref.html b/third_party/blink/web_tests/wpt_internal/css/css-flexbox/balance/balance-min-line-count-003-ref.html
new file mode 100644
index 0000000..43cd49b7
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/css/css-flexbox/balance/balance-min-line-count-003-ref.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<style>
+#flex {
+  display: flex;
+  width: 100px;
+  flex-wrap: wrap;
+  gap: 20px;
+}
+div > div {
+  background: green;
+  width: 20px;
+  height: 20px;
+}
+</style>
+<p>There should be a minimum of 4 lines.</p>
+<div id="flex">
+  <div></div>
+  <div></div>
+  <div></div>
+  <div></div>
+  <div></div>
+  <div style="background: none;"></div>
+  <div></div>
+  <div></div>
+  <div style="background: none;"></div>
+  <div></div>
+  <div></div>
+</div>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-flexbox/balance/balance-min-line-count-003.html b/third_party/blink/web_tests/wpt_internal/css/css-flexbox/balance/balance-min-line-count-003.html
new file mode 100644
index 0000000..25edb2332
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/css/css-flexbox/balance/balance-min-line-count-003.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/3070">
+<link rel="match" href="balance-min-line-count-003-ref.html">
+<meta name="assert" content="Tests balancing with a minimum number of lines.">
+<style>
+#flex {
+  display: flex;
+  flex-wrap: balance 4;
+  gap: 20px;
+}
+div > div {
+  background: green;
+  width: 20px;
+  height: 20px;
+}
+</style>
+<p>There should be a minimum of 4 lines.</p>
+<div id="flex">
+  <div></div>
+  <div></div>
+  <div></div>
+  <div></div>
+  <div></div>
+  <div></div>
+  <div></div>
+  <div></div>
+  <div></div>
+</div>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-flexbox/balance/balance-min-line-count-004-ref.html b/third_party/blink/web_tests/wpt_internal/css/css-flexbox/balance/balance-min-line-count-004-ref.html
new file mode 100644
index 0000000..54b7d69
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/css/css-flexbox/balance/balance-min-line-count-004-ref.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<style>
+#flex {
+  display: flex;
+  width: 60px;
+  flex-wrap: wrap;
+  gap: 20px;
+}
+div > div {
+  background: green;
+  width: 20px;
+  height: 20px;
+}
+</style>
+<p>There should be a minimum of 5 lines.</p>
+<div id="flex">
+  <div></div>
+  <div></div>
+  <div></div>
+  <div></div>
+  <div></div>
+  <div></div>
+  <div></div>
+  <div></div>
+  <div></div>
+</div>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-flexbox/balance/balance-min-line-count-004.html b/third_party/blink/web_tests/wpt_internal/css/css-flexbox/balance/balance-min-line-count-004.html
new file mode 100644
index 0000000..876c0d2
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/css/css-flexbox/balance/balance-min-line-count-004.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/3070">
+<link rel="match" href="balance-min-line-count-004-ref.html">
+<meta name="assert" content="Tests balancing with a minimum number of lines.">
+<style>
+#flex {
+  display: flex;
+  flex-wrap: balance 5;
+  gap: 20px;
+}
+div > div {
+  background: green;
+  width: 20px;
+  height: 20px;
+}
+</style>
+<p>There should be a minimum of 5 lines.</p>
+<div id="flex">
+  <div></div>
+  <div></div>
+  <div></div>
+  <div></div>
+  <div></div>
+  <div></div>
+  <div></div>
+  <div></div>
+  <div></div>
+</div>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-flexbox/balance/balance-min-line-count-005-ref.html b/third_party/blink/web_tests/wpt_internal/css/css-flexbox/balance/balance-min-line-count-005-ref.html
new file mode 100644
index 0000000..3c700ae
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/css/css-flexbox/balance/balance-min-line-count-005-ref.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<style>
+#flex {
+  display: flex;
+  width: 60px;
+  flex-wrap: wrap;
+  gap: 20px;
+}
+div > div {
+  background: green;
+  width: 20px;
+  height: 20px;
+}
+</style>
+<p>There should be a minimum of 6 lines.</p>
+<div id="flex">
+  <div></div>
+  <div></div>
+  <div></div>
+  <div></div>
+  <div></div>
+  <div></div>
+  <div></div>
+  <div style="background: none;"></div>
+  <div></div>
+  <div style="background: none;"></div>
+  <div></div>
+</div>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-flexbox/balance/balance-min-line-count-005.html b/third_party/blink/web_tests/wpt_internal/css/css-flexbox/balance/balance-min-line-count-005.html
new file mode 100644
index 0000000..1a34d16
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/css/css-flexbox/balance/balance-min-line-count-005.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/3070">
+<link rel="match" href="balance-min-line-count-005-ref.html">
+<meta name="assert" content="Tests balancing with a minimum number of lines.">
+<style>
+#flex {
+  display: flex;
+  flex-wrap: balance 6;
+  gap: 20px;
+}
+div > div {
+  background: green;
+  width: 20px;
+  height: 20px;
+}
+</style>
+<p>There should be a minimum of 6 lines.</p>
+<div id="flex">
+  <div></div>
+  <div></div>
+  <div></div>
+  <div></div>
+  <div></div>
+  <div></div>
+  <div></div>
+  <div></div>
+  <div></div>
+</div>
diff --git a/third_party/chromite b/third_party/chromite
index 8cd8915..4be9390 160000
--- a/third_party/chromite
+++ b/third_party/chromite
@@ -1 +1 @@
-Subproject commit 8cd89158d9c6823fac6328bccf0c389a058cef94
+Subproject commit 4be93907c73c20225535e7efdc6a59c8eceb3775
diff --git a/third_party/crabbyavif/README.chromium b/third_party/crabbyavif/README.chromium
index ddf6109c..919d447 100644
--- a/third_party/crabbyavif/README.chromium
+++ b/third_party/crabbyavif/README.chromium
@@ -2,7 +2,7 @@
 Short Name: crabbyavif
 URL: https://github.com/webmproject/CrabbyAvif
 Version: N/A
-Revision: 91072f337edd69508dbefaf290f01722e228738a
+Revision: eb883022a5886739f07f0241f918e2be97d65ff0
 License: Apache-2.0
 License File: LICENSE
 Security Critical: yes
diff --git a/third_party/crabbyavif/src b/third_party/crabbyavif/src
index 91072f3..eb88302 160000
--- a/third_party/crabbyavif/src
+++ b/third_party/crabbyavif/src
@@ -1 +1 @@
-Subproject commit 91072f337edd69508dbefaf290f01722e228738a
+Subproject commit eb883022a5886739f07f0241f918e2be97d65ff0
diff --git a/third_party/crossbench b/third_party/crossbench
index 8475a50..6b953c0b 160000
--- a/third_party/crossbench
+++ b/third_party/crossbench
@@ -1 +1 @@
-Subproject commit 8475a50251395107239e490b67ca5b765acbde52
+Subproject commit 6b953c0b98c81c00f8458266d07b570f58b40848
diff --git a/third_party/dawn b/third_party/dawn
index 892bb94..5c36eda 160000
--- a/third_party/dawn
+++ b/third_party/dawn
@@ -1 +1 @@
-Subproject commit 892bb94dfc85a3e60b25eb2c6e707adfd58e1079
+Subproject commit 5c36eda233b37810c8ca6ad00ad0300b0f821890
diff --git a/third_party/depot_tools b/third_party/depot_tools
index 196d941..0888983 160000
--- a/third_party/depot_tools
+++ b/third_party/depot_tools
@@ -1 +1 @@
-Subproject commit 196d9416106a6ace0729b09acda46546c4a12ce1
+Subproject commit 0888983e473613d853c419245b97294e46497798
diff --git a/third_party/devtools-frontend/src b/third_party/devtools-frontend/src
index 21ef102..fa13103 160000
--- a/third_party/devtools-frontend/src
+++ b/third_party/devtools-frontend/src
@@ -1 +1 @@
-Subproject commit 21ef1023f916f40d54fdde5e51e03f1ea7c44b47
+Subproject commit fa131039ce7473c0f2f6f18c6039e2a9a72b35a4
diff --git a/third_party/metrics_proto/README.chromium b/third_party/metrics_proto/README.chromium
index 7f4b7e85..020ff08 100644
--- a/third_party/metrics_proto/README.chromium
+++ b/third_party/metrics_proto/README.chromium
@@ -1,8 +1,8 @@
 Name: Metrics Protos
 Short Name: metrics_proto
 URL: This is the canonical public repository
-Version: 752672819
-Date: 2025-04-29
+Version: 760814420
+Date: 2025-05-19
 License: BSD-3-Clause
 License File: LICENSE
 Shipped: yes
diff --git a/third_party/metrics_proto/omnibox_event.proto b/third_party/metrics_proto/omnibox_event.proto
index 16ed4def..b7466ac 100644
--- a/third_party/metrics_proto/omnibox_event.proto
+++ b/third_party/metrics_proto/omnibox_event.proto
@@ -218,6 +218,10 @@
     // The searchbox in the Android Hub.
     ANDROID_HUB = 29;
 
+    // JumpStart Omnibox feature (Android): start Chrome to Search Activity on
+    // Low End devices.
+    JUMP_START = 30;
+
     // When adding new classifications, please update the `OmniboxPageContext`
     // variants listed in chromium's
     // tools/metrics/histograms/metadata/omnibox/histograms.xml and also
diff --git a/third_party/perfetto b/third_party/perfetto
index 5a75e4f..1882429 160000
--- a/third_party/perfetto
+++ b/third_party/perfetto
@@ -1 +1 @@
-Subproject commit 5a75e4faa948b13d3bb6567ea2dd6347cabac610
+Subproject commit 188242925b83331305ff6f8f146b2a6667f21ac2
diff --git a/third_party/rust/OWNERS-review-checklist.md b/third_party/rust/OWNERS-review-checklist.md
index bda1958..42b92c8 100644
--- a/third_party/rust/OWNERS-review-checklist.md
+++ b/third_party/rust/OWNERS-review-checklist.md
@@ -79,6 +79,11 @@
 
 * There is no need to review tests, benchmarks, nor examples.
 
+* To quickly check if a crate uses `unsafe` Rust, one can look at the
+  value of `allow_unsafe` in the crate's `BUILD.gn` file
+  (see [an example here](https://crrev.com/c/6538666/12/third_party/rust/png/v0_18/BUILD.gn#55);
+  TODO(lukasza): once the CL lands, change the link to a code-search-based one).
+
 * Tools that may be helpful during a review:
     - `tools/crates/grep_for_vet_relevant_keywords.sh`
     - Tools for looking at a diff when updating a crate to a new version
diff --git a/third_party/rust/adler2/v2/BUILD.gn b/third_party/rust/adler2/v2/BUILD.gn
index 271155c..fda94e0 100644
--- a/third_party/rust/adler2/v2/BUILD.gn
+++ b/third_party/rust/adler2/v2/BUILD.gn
@@ -22,11 +22,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "2.0.0"
   cargo_pkg_authors = "Jonas Schievink <jonasschievink@gmail.com>, oyvindln <oyvindln@users.noreply.github.com>"
   cargo_pkg_name = "adler2"
   cargo_pkg_description =
       "A simple clean-room implementation of the Adler-32 checksum"
+  cargo_pkg_version = "2.0.0"
+
+  allow_unsafe = false
 
   # Only for usage from third-party crates. Add the crate to
   # //third_party/rust/chromium_crates_io/Cargo.toml to use
diff --git a/third_party/rust/android_system_properties/v0_1/BUILD.gn b/third_party/rust/android_system_properties/v0_1/BUILD.gn
index 90c4326..d416b5f 100644
--- a/third_party/rust/android_system_properties/v0_1/BUILD.gn
+++ b/third_party/rust/android_system_properties/v0_1/BUILD.gn
@@ -19,10 +19,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2018"
-  cargo_pkg_version = "0.1.5"
   cargo_pkg_authors = "Nicolas Silva <nical@fastmail.com>"
   cargo_pkg_name = "android_system_properties"
   cargo_pkg_description = "Minimal Android system properties wrapper"
+  cargo_pkg_version = "0.1.5"
+
+  allow_unsafe = true
+
   deps = []
   if (is_android) {
     deps += [ "//third_party/rust/libc/v0_2:lib" ]
diff --git a/third_party/rust/anstyle/v1/BUILD.gn b/third_party/rust/anstyle/v1/BUILD.gn
index 0606dbb..d933a9c 100644
--- a/third_party/rust/anstyle/v1/BUILD.gn
+++ b/third_party/rust/anstyle/v1/BUILD.gn
@@ -26,9 +26,12 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "1.0.10"
   cargo_pkg_name = "anstyle"
   cargo_pkg_description = "ANSI text styling"
+  cargo_pkg_version = "1.0.10"
+
+  allow_unsafe = true
+
   features = [
     "default",
     "std",
diff --git a/third_party/rust/anyhow/v1/BUILD.gn b/third_party/rust/anyhow/v1/BUILD.gn
index a43de90..b150469f 100644
--- a/third_party/rust/anyhow/v1/BUILD.gn
+++ b/third_party/rust/anyhow/v1/BUILD.gn
@@ -32,11 +32,14 @@
 
   build_native_rust_unit_tests = false
   edition = "2018"
-  cargo_pkg_version = "1.0.98"
   cargo_pkg_authors = "David Tolnay <dtolnay@gmail.com>"
   cargo_pkg_name = "anyhow"
   cargo_pkg_description =
       "Flexible concrete Error type built on std::error::Error"
+  cargo_pkg_version = "1.0.98"
+
+  allow_unsafe = true
+
   features = [
     "default",
     "std",
diff --git a/third_party/rust/autocfg/v1/BUILD.gn b/third_party/rust/autocfg/v1/BUILD.gn
index a006064..88024b2 100644
--- a/third_party/rust/autocfg/v1/BUILD.gn
+++ b/third_party/rust/autocfg/v1/BUILD.gn
@@ -25,10 +25,12 @@
 
   build_native_rust_unit_tests = false
   edition = "2015"
-  cargo_pkg_version = "1.4.0"
   cargo_pkg_authors = "Josh Stone <cuviper@gmail.com>"
   cargo_pkg_name = "autocfg"
   cargo_pkg_description = "Automatic cfg for Rust compiler features"
+  cargo_pkg_version = "1.4.0"
+
+  allow_unsafe = false
 
   # Only for usage from third-party crates. Add the crate to
   # //third_party/rust/chromium_crates_io/Cargo.toml to use
diff --git a/third_party/rust/base64/v0_22/BUILD.gn b/third_party/rust/base64/v0_22/BUILD.gn
index f309c37..8544dd6 100644
--- a/third_party/rust/base64/v0_22/BUILD.gn
+++ b/third_party/rust/base64/v0_22/BUILD.gn
@@ -41,10 +41,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2018"
-  cargo_pkg_version = "0.22.1"
   cargo_pkg_authors = "Marshall Pierce <marshall@mpierce.org>"
   cargo_pkg_name = "base64"
   cargo_pkg_description = "encodes and decodes base64 as bytes or utf8"
+  cargo_pkg_version = "0.22.1"
+
+  allow_unsafe = false
+
   features = [
     "alloc",
     "default",
diff --git a/third_party/rust/bitflags/v2/BUILD.gn b/third_party/rust/bitflags/v2/BUILD.gn
index 4f435a2..a8774d5 100644
--- a/third_party/rust/bitflags/v2/BUILD.gn
+++ b/third_party/rust/bitflags/v2/BUILD.gn
@@ -60,11 +60,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "2.9.0"
   cargo_pkg_authors = "The Rust Project Developers"
   cargo_pkg_name = "bitflags"
   cargo_pkg_description =
       "A macro to generate structures which behave like bitflags."
+  cargo_pkg_version = "2.9.0"
+
+  allow_unsafe = false
 
   #####################################################################
   # Tweaking which GN `config`s apply to this target.
diff --git a/third_party/rust/bytemuck/v1/BUILD.gn b/third_party/rust/bytemuck/v1/BUILD.gn
index 795e568..c2929f62 100644
--- a/third_party/rust/bytemuck/v1/BUILD.gn
+++ b/third_party/rust/bytemuck/v1/BUILD.gn
@@ -34,10 +34,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2018"
-  cargo_pkg_version = "1.23.0"
   cargo_pkg_authors = "Lokathor <zefria@gmail.com>"
   cargo_pkg_name = "bytemuck"
   cargo_pkg_description = "A crate for mucking around with piles of bytes."
+  cargo_pkg_version = "1.23.0"
+
+  allow_unsafe = true
+
   deps = [ "//third_party/rust/bytemuck_derive/v1:lib" ]
   features = [
     "bytemuck_derive",
diff --git a/third_party/rust/bytemuck_derive/v1/BUILD.gn b/third_party/rust/bytemuck_derive/v1/BUILD.gn
index d4d3a681..2ebecad 100644
--- a/third_party/rust/bytemuck_derive/v1/BUILD.gn
+++ b/third_party/rust/bytemuck_derive/v1/BUILD.gn
@@ -21,10 +21,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2018"
-  cargo_pkg_version = "1.9.3"
   cargo_pkg_authors = "Lokathor <zefria@gmail.com>"
   cargo_pkg_name = "bytemuck_derive"
   cargo_pkg_description = "derive proc-macros for `bytemuck`"
+  cargo_pkg_version = "1.9.3"
+
+  allow_unsafe = false
+
   deps = [
     "//third_party/rust/proc_macro2/v1:lib",
     "//third_party/rust/quote/v1:lib",
diff --git a/third_party/rust/bytes/v1/BUILD.gn b/third_party/rust/bytes/v1/BUILD.gn
index 68c2645..21f0d8b 100644
--- a/third_party/rust/bytes/v1/BUILD.gn
+++ b/third_party/rust/bytes/v1/BUILD.gn
@@ -39,11 +39,14 @@
 
   build_native_rust_unit_tests = false
   edition = "2018"
-  cargo_pkg_version = "1.10.1"
   cargo_pkg_authors =
       "Carl Lerche <me@carllerche.com>, Sean McArthur <sean@seanmonstar.com>"
   cargo_pkg_name = "bytes"
   cargo_pkg_description = "Types and traits for working with bytes"
+  cargo_pkg_version = "1.10.1"
+
+  allow_unsafe = true
+
   features = [
     "default",
     "std",
diff --git a/third_party/rust/calendrical_calculations/v0_1/BUILD.gn b/third_party/rust/calendrical_calculations/v0_1/BUILD.gn
index 4bf692f..f4eaa2b 100644
--- a/third_party/rust/calendrical_calculations/v0_1/BUILD.gn
+++ b/third_party/rust/calendrical_calculations/v0_1/BUILD.gn
@@ -33,10 +33,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.1.3"
   cargo_pkg_authors = "The ICU4X Project Developers"
   cargo_pkg_name = "calendrical_calculations"
   cargo_pkg_description = "Calendrical calculations in Rust"
+  cargo_pkg_version = "0.1.3"
+
+  allow_unsafe = false
+
   deps = [
     "//third_party/rust/core_maths/v0_1:lib",
     "//third_party/rust/displaydoc/v0_2:lib",
diff --git a/third_party/rust/cfg_if/v1/BUILD.gn b/third_party/rust/cfg_if/v1/BUILD.gn
index dce0420..4b3ff510 100644
--- a/third_party/rust/cfg_if/v1/BUILD.gn
+++ b/third_party/rust/cfg_if/v1/BUILD.gn
@@ -20,10 +20,12 @@
 
   build_native_rust_unit_tests = false
   edition = "2018"
-  cargo_pkg_version = "1.0.0"
   cargo_pkg_authors = "Alex Crichton <alex@alexcrichton.com>"
   cargo_pkg_name = "cfg-if"
   cargo_pkg_description = "A macro to ergonomically define an item depending on a large number of #[cfg] parameters. Structured like an if-else chain, the first matching branch is the item that gets emitted."
+  cargo_pkg_version = "1.0.0"
+
+  allow_unsafe = false
 
   # Only for usage from third-party crates. Add the crate to
   # //third_party/rust/chromium_crates_io/Cargo.toml to use
diff --git a/third_party/rust/chromium_crates_io/BUILD.gn.hbs b/third_party/rust/chromium_crates_io/BUILD.gn.hbs
index 76e7da3..157f9ca8 100644
--- a/third_party/rust/chromium_crates_io/BUILD.gn.hbs
+++ b/third_party/rust/chromium_crates_io/BUILD.gn.hbs
@@ -35,7 +35,6 @@
 
   build_native_rust_unit_tests = false
   edition = "{{edition}}"
-  cargo_pkg_version = "{{cargo_pkg_version}}"
   {{#if cargo_pkg_authors}}
     {{#with cargo_pkg_authors}}
       cargo_pkg_authors = "{{gn_escape this}}"
@@ -47,6 +46,16 @@
       cargo_pkg_description = "{{gn_escape this}}"
     {{/with}}
   {{/if}}
+  cargo_pkg_version = "{{cargo_pkg_version}}"
+
+  {{! `cargo_pkg_version` changes during minor version updates, so (to help
+      reviewers see important crate properties at a glance) we try to include
+      review-relevant information (e.g. `allow_unsafe`) right below
+      `cargo_pkg_version`.  }}
+  {{#if_key_present "allow_unsafe" extra_kv}}
+    allow_unsafe = {{#if extra_kv.allow_unsafe}} true {{else}} false {{/if}}
+  {{/if_key_present}}
+
   {{#each deps}}
   {{#if @first}}
   deps = [
diff --git a/third_party/rust/chromium_crates_io/Cargo.lock b/third_party/rust/chromium_crates_io/Cargo.lock
index 7e82abc8..239810d 100644
--- a/third_party/rust/chromium_crates_io/Cargo.lock
+++ b/third_party/rust/chromium_crates_io/Cargo.lock
@@ -195,11 +195,6 @@
 ]
 
 [[package]]
-name = "crossbeam-utils"
-version = "0.8.21"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
 name = "cxx"
 version = "1.0.158"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1553,12 +1548,11 @@
 
 [[package]]
 name = "zip"
-version = "2.6.1"
+version = "3.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "arbitrary",
  "crc32fast",
- "crossbeam-utils",
  "flate2",
  "indexmap",
  "memchr",
diff --git a/third_party/rust/chromium_crates_io/Cargo.toml b/third_party/rust/chromium_crates_io/Cargo.toml
index da449638..3e0ca945 100644
--- a/third_party/rust/chromium_crates_io/Cargo.toml
+++ b/third_party/rust/chromium_crates_io/Cargo.toml
@@ -103,6 +103,6 @@
 features = ["nightly_portable_simd"]  # For std::simd coverage.
 
 [dependencies.zip]
-version = "2.6.1"
+version = "3.0.0"
 default-features = false
 features = ["deflate-flate2"]
diff --git a/third_party/rust/chromium_crates_io/check_gnrt_config.py b/third_party/rust/chromium_crates_io/check_gnrt_config.py
index 5f4cf51..a411641 100755
--- a/third_party/rust/chromium_crates_io/check_gnrt_config.py
+++ b/third_party/rust/chromium_crates_io/check_gnrt_config.py
@@ -46,6 +46,41 @@
     return dict()
 
 
+def CheckExplicitAllowUnsafeForAllCrates(crate_ids, gnrt_config):
+    """Checks that `gnrt_config.toml` has `allow_unsafe = ...` for each crate.
+
+       Returns an error message if a problem is detected.
+       Returns an empty string if there are no problems.
+    """
+    result = []
+    for crate_id in sorted(crate_ids):
+        crate_name = crate_utils.ConvertCrateIdToCrateName(crate_id)
+
+        # Ignore the root package and placeholder crates.
+        if crate_name == "chromium": continue
+        if crate_utils.IsPlaceholderCrate(crate_id): continue
+
+        # Ignore crates that specify `allow_unsafe`.
+        extra_kv = _GetExtraKvForCrateName(crate_name, gnrt_config)
+        if "allow_unsafe" in extra_kv:
+            continue
+
+        # Report a problem for all other crates.
+        if not result:  # Is is the **first** problematic `crate_name`?
+            result.append("ERROR: Please ensure that `gnrt_config.toml` "
+                          "explicitly specifies `allow_unsafe = ...` for all "
+                          "crates that `chromium_crates_io` depends on.  "
+                          "This helps `//third_party/rust/OWNERS` to check at "
+                          "a glance if a given crate contains `unsafe` Rust "
+                          "code.")
+            result.append("")
+        result += [
+            f"    [crate.{crate_name}.extra_kv]",
+            f"    allow_unsafe = false (or true if needed)",
+        ]
+
+    return "\n".join(result)
+
 def CheckMultiversionCrates(crate_ids, gnrt_config):
     """Checks that a bug tracks each crate with multiple versions.
 
@@ -81,10 +116,10 @@
 
         # Report a problem for other multiversion crates.
         if not result:  # Is is the **first** problematic `crate_name`?
-            result.append("ERROR: Transitive dependency graph includes " + \
-                          "multiple versions of the same crate.  Please " + \
-                          "open a bug to track removing one of the " + \
-                          "versions and put a link to the bug into " + \
+            result.append("ERROR: Transitive dependency graph includes "
+                          "multiple versions of the same crate.  Please "
+                          "open a bug to track removing one of the "
+                          "versions and put a link to the bug into "
                           "`gnrt_config.toml` like this:")
             result.append("")
 
@@ -105,6 +140,9 @@
     success = True
 
     def RunChecks(check_impl):
+        nonlocal success
+        nonlocal crate_ids
+        nonlocal gnrt_config
         result = check_impl(crate_ids, gnrt_config)
         if result:
             if not success:
@@ -116,8 +154,7 @@
             print(result)
 
     RunChecks(CheckMultiversionCrates)
-
-    # TODO(https://crbug.com/399931417): RunChecks(CheckExplicitAllowUnsafeForAllCrates).
+    RunChecks(CheckExplicitAllowUnsafeForAllCrates)
 
     # TODO(https://crbug.com/399935219): RunChecks(CheckNonapplicableGnrtConfigEntries).
     # Move the useless-entry-in-gnrt_config check from `gnrt` into this script.
diff --git a/third_party/rust/chromium_crates_io/check_gnrt_config_unittests.py b/third_party/rust/chromium_crates_io/check_gnrt_config_unittests.py
index c01ab16..d72d284 100755
--- a/third_party/rust/chromium_crates_io/check_gnrt_config_unittests.py
+++ b/third_party/rust/chromium_crates_io/check_gnrt_config_unittests.py
@@ -4,15 +4,50 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import os
 import unittest
 
+import crate_utils
+
 from check_gnrt_config import (
+    # Functions under test:
     _GetExtraKvForCrateName,
+    CheckExplicitAllowUnsafeForAllCrates,
     CheckMultiversionCrates,
 )
 
 
+class CheckExplicitAllowUnsafeForAllCratesTests(unittest.TestCase):
+
+    def testAllowUnsafeMissing(self):
+        crate_ids = set(["foo@1.2.3"])
+        gnrt_config = {}
+        msg = CheckExplicitAllowUnsafeForAllCrates(crate_ids, gnrt_config)
+        self.assertTrue("explicitly specifies `allow_unsafe = ...`" in msg)
+        self.assertTrue(
+            "all crates that `chromium_crates_io` depends on" in msg)
+        self.assertTrue("gnrt_config.toml" in msg)
+        self.assertTrue("[crate.foo.extra_kv]" in msg)
+        self.assertTrue("allow_unsafe = " in msg)
+
+    def testAllowUnsafePresent(self):
+        crate_ids = set(["foo@1.2.3"])
+        gnrt_config = {"crate": {"foo": {"extra_kv": {"allow_unsafe": True}}}}
+        self.assertEqual(
+            "", CheckExplicitAllowUnsafeForAllCrates(crate_ids, gnrt_config))
+
+    def testFakeRootCrateIsIgnored(self):
+        crate_ids = set(["chromium@1.2.3"])
+        gnrt_config = {}
+        self.assertEqual(
+            "", CheckExplicitAllowUnsafeForAllCrates(crate_ids, gnrt_config))
+
+    def testPlaceholderCratesAreIgnored(self):
+        crate_ids = set([crate_utils.GetPlaceholderCrateIdForTesting()])
+        gnrt_config = {}
+        self.assertEqual(
+            "", CheckExplicitAllowUnsafeForAllCrates(crate_ids, gnrt_config))
+
+
 class CheckMultiversionCratesTests(unittest.TestCase):
 
     def testNoMultiversionCrates(self):
@@ -38,6 +73,7 @@
         gnrt_config = {}
         msg = CheckMultiversionCrates(crate_ids, gnrt_config)
         self.assertTrue("multiple versions of the same crate" in msg)
+        self.assertTrue("gnrt_config.toml" in msg)
         self.assertTrue("foo@1.2.3, foo@4.5.6" in msg)
         self.assertTrue("[crate.foo.extra_kv]" in msg)
         self.assertTrue("multiversion_cleanup_bug = " in msg)
diff --git a/third_party/rust/chromium_crates_io/crate_utils.py b/third_party/rust/chromium_crates_io/crate_utils.py
index f8841fe9..35dca2b 100755
--- a/third_party/rust/chromium_crates_io/crate_utils.py
+++ b/third_party/rust/chromium_crates_io/crate_utils.py
@@ -51,6 +51,7 @@
 
 
 def ConvertCrateIdToCrateEpoch(crate_id: str) -> str:
+    _AssertIsCrateId(crate_id)
     crate_version = ConvertCrateIdToCrateVersion(crate_id)
     v = crate_version.split('.')
     if v[0] == '0':
@@ -60,11 +61,13 @@
 
 def ConvertCrateIdToCrateName(crate_id: str) -> str:
     """ Converts a `crate_id` into a `crate_name`."""
+    _AssertIsCrateId(crate_id)
     return crate_id[:crate_id.find("@")]
 
 
 def ConvertCrateIdToCrateVersion(crate_id: str) -> str:
     """ Converts a `crate_id` into a `crate_version`."""
+    _AssertIsCrateId(crate_id)
     crate_version = crate_id[crate_id.find("@") + 1:]
     return crate_version
 
@@ -75,6 +78,7 @@
     Example return value:
     `"<path to chromium root>\\third_party\\rust\\foo\\v1"`
     """
+    _AssertIsCrateId(crate_id)
     return os.path.join(
         CHROMIUM_DIR, _ConvertCrateIdToBuildDirRelativeToChromiumRoot(crate_id))
 
@@ -85,6 +89,7 @@
     Example return value:
     `"<path to chromium root>\\third_party\\rust\\chromium_crates_io\\vendor\\foo-v1"`
     """
+    _AssertIsCrateId(crate_id)
     crate_name = ConvertCrateIdToCrateName(crate_id)
     crate_epoch = ConvertCrateIdToCrateEpoch(crate_id)
     crate_vendor_dir = os.path.join(VENDOR_DIR, f"{crate_name}-{crate_epoch}")
@@ -99,11 +104,43 @@
 
     Example return value: `"//third_party/rust/foo/v1:lib"`
     """
+    _AssertIsCrateId(crate_id)
     dir_name = _ConvertCrateIdToBuildDirRelativeToChromiumRoot(crate_id)
     dir_name = dir_name.replace(os.sep, "/")  # GN uses `/` as a path separator.
     return f"//{dir_name}:lib"
 
 
+def IsPlaceholderCrate(crate_id: str) -> bool:
+    """ Determines if `crate_id` corresponds to a placeholder package.
+
+        See also `//tools/crates/gnrt/removed_crate.md`.
+    """
+    _AssertIsCrateId(crate_id)
+    vendor_dir = ConvertCrateIdToVendorDir(crate_id)
+    crate_cargo_toml_path = os.path.join(vendor_dir, "Cargo.toml")
+    KNOWN_PATTERN = "@generated from `tools/crates/gnrt/removed_Cargo.toml.hbs`"
+    try:
+        with open(crate_cargo_toml_path) as f:
+            return KNOWN_PATTERN in f.read()
+    except:
+        return False
+
+
+def GetPlaceholderCrateIdForTesting() -> str:
+    """ Test helper for getting a placeholder `crate_id` like `"cc-1.2.22"`.
+    """
+    # This test helper assumes that `cc` is listed in
+    # `chromium_crates_io/Cargo.lock` and that
+    # `chromium_crates_io/vendor/cc...` contains a placeholder crate (see
+    # `tools/crates/gnrt/removed_crate.md`).
+    #
+    # Unit tests that depend on external state are a bit icky... But it
+    # seems that this assumption should hold "forever" + this helps write
+    # more tests for other stuff, so let ignore the ickiness...
+    return list(filter(lambda crate_id: "cc@" in crate_id,
+                       GetCurrentCrateIds()))[0]
+
+
 def _ConvertCrateIdToBuildDirRelativeToChromiumRoot(crate_id: str) -> str:
     """ Converts a `crate_id` (e.g. "foo@1.2.3") into an epoch dir.
 
@@ -117,4 +154,9 @@
     return target
 
 
+def _AssertIsCrateId(crate_id: str) -> bool:
+    if not isinstance(crate_id, str) or "@" not in crate_id:
+        raise RuntimeError(f"This is not a valid crate id: {crate_id}")
+
+
 assert __name__ != '__main__'
diff --git a/third_party/rust/chromium_crates_io/crate_utils_unittests.py b/third_party/rust/chromium_crates_io/crate_utils_unittests.py
index b453995..e522470 100755
--- a/third_party/rust/chromium_crates_io/crate_utils_unittests.py
+++ b/third_party/rust/chromium_crates_io/crate_utils_unittests.py
@@ -16,6 +16,8 @@
     ConvertCrateIdToGnLabel,
     ConvertCrateIdToVendorDir,
     GetCurrentCrateIds,
+    GetPlaceholderCrateIdForTesting,
+    IsPlaceholderCrate,
 )
 
 
@@ -69,5 +71,25 @@
         self.assertEqual(ConvertCrateIdToCrateVersion("foo-bar@1.2.3"), "1.2.3")
 
 
+class IsPlaceholderCrateTests(unittest.TestCase):
+
+    def testCc(self):
+        crate_id = GetPlaceholderCrateIdForTesting()
+        self.assertTrue(IsPlaceholderCrate(crate_id))
+
+    def testSerde(self):
+        # This test assumes that `serde` is listed in
+        # `chromium_crates_io/Cargo.lock` and that
+        # `chromium_crates_io/vendor/serde...` does *not* contains a placeholder
+        # crate (see `tools/crates/gnrt/removed_crate.md`).
+        #
+        # Unit tests that depend on external state are a bit icky... But it
+        # seems that this assumption should hold "forever", so...
+        crate_id = list(
+            filter(lambda crate_id: "serde@" in crate_id,
+                   GetCurrentCrateIds()))[0]
+        self.assertFalse(IsPlaceholderCrate(crate_id))
+
+
 if __name__ == '__main__':
     unittest.main()
diff --git a/third_party/rust/chromium_crates_io/gnrt_config.toml b/third_party/rust/chromium_crates_io/gnrt_config.toml
index 7b97b7ca..3a4a447 100644
--- a/third_party/rust/chromium_crates_io/gnrt_config.toml
+++ b/third_party/rust/chromium_crates_io/gnrt_config.toml
@@ -2,7 +2,10 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-# This file configures how to generate GN rules for third-party Rust crates.
+# This file configures how `gnrt` vendors and generates `BUILD.gn` files for
+# third-party Rust crates.  After modifying this file you probably want to run
+# either `tools/crates/run_gnrt.py vendor` or `tools/crates/run_gnrt.py gen` to
+# regenerate `gnrt`-managed files with the new `gnrt_config.toml` contents.
 #
 # `[crate.foo]` sections below can configure generation of `BUILD.gn` and/or
 # `README.chromium` by specifying the following options:
@@ -69,6 +72,8 @@
 #   `gnrt`).  The following enries are currently supported/used by
 #   `BUILD.gn.hbs`:
 #
+#     allow_unsafe: If true, then the crate is allowed to use `unsafe` Rust.
+#
 #     allow_unstable_features: A list of unstable feature names to allow
 #       in a given crate.  (Using unstable features adds extra risk
 #       to `rustc` rolls - please justify the risk and think about
@@ -97,23 +102,76 @@
 root = "chromium"
 remove_crates = []
 
-[crate.bytes]
-group = 'test'
+[crate.adler2.extra_kv]
+allow_unsafe = false
+
+[crate.android_system_properties.extra_kv]
+allow_unsafe = true
+
+[crate.anstyle.extra_kv]
+allow_unsafe = true
+
+[crate.anyhow.extra_kv]
+allow_unsafe = true
+
+[crate.autocfg.extra_kv]
+allow_unsafe = false
+
+[crate.base64.extra_kv]
+allow_unsafe = false
+
+[crate.bitflags.extra_kv]
+allow_unsafe = false
+
+[crate.bytemuck_derive.extra_kv]
+allow_unsafe = false
 
 [crate.bytemuck.extra_kv]
+allow_unsafe = true
 # This allows using `std::simd` helpers, which are needed by first-party code.
 allow_unstable_features = ["portable_simd"]
 
+[crate.bytes]
+group = 'test'
+extra_kv = { allow_unsafe = true }
+
+[crate.calendrical_calculations.extra_kv]
+allow_unsafe = false
+
+[crate.cfg-if.extra_kv]
+allow_unsafe = false
+
 [crate.clap]
 # These files are included from source files and are thus required as part of
 # building the crate.
 extra_input_roots = ['../README.md']
 extra_src_roots = ['../examples']
+extra_kv = { allow_unsafe = false }
 
 [crate.clap_builder]
 extra_input_roots = ['../README.md']
+extra_kv = { allow_unsafe = false }
+
+[crate.clap_lex.extra_kv]
+allow_unsafe = true
+
+[crate.codespan-reporting]
+extra_kv = { allow_unsafe = false }
+
+[crate.core-foundation-sys.extra_kv]
+allow_unsafe = true
+
+[crate.core_maths.extra_kv]
+allow_unsafe = false
+
+[crate.crc32fast.extra_kv]
+allow_unsafe = true
+
+[crate.crossbeam-utils.extra_kv]
+allow_unsafe = false
 
 [crate.cxx]
+extra_kv = { allow_unsafe = true }
 remove_deps = ['cc', 'link-cplusplus']
 # This removes the use of cc, and disables the `build_with_cargo` cfg which
 # also removes the use of link-cplusplus.
@@ -122,14 +180,49 @@
 [crate.cxxbridge-cmd]
 shipped = false
 bin_targets = ["cxxbridge"]
+extra_kv = { allow_unsafe = false }
+
+[crate.cxxbridge-flags.extra_kv]
+allow_unsafe = false
+
+[crate.cxxbridge-macro.extra_kv]
+allow_unsafe = false
+
+[crate.derivre.extra_kv]
+allow_unsafe = false
+
+[crate.diplomat.extra_kv]
+allow_unsafe = false
+
+[crate.diplomat_core.extra_kv]
+allow_unsafe = false
+
+[crate.diplomat-runtime.extra_kv]
+allow_unsafe = true
+
+[crate.displaydoc.extra_kv]
+allow_unsafe = false
+
+[crate.either.extra_kv]
+allow_unsafe = true
+
+[crate.equivalent.extra_kv]
+allow_unsafe = false
 
 [crate.fdeflate.extra_kv]
+allow_unsafe = false
 # Web tests (aka WPT tests, also formerly known as Layout Tests) require fast
 # PNG encoding and decoding even in Debug builds.  See
 # https://crbug.com/396419181.
 configs_to_remove = ['//build/config/compiler:default_optimization']
 configs_to_add = ['//build/config/compiler:optimize']
 
+[crate.fend-core.extra_kv]
+allow_unsafe = false
+
+[crate.fixed_decimal.extra_kv]
+allow_unsafe = false
+
 [crate.flate2]
 ban_features = [
     # `any_zlib` feature is banned because Chromium does not use the `zlib`
@@ -143,16 +236,34 @@
 # https://crbug.com/396419181.
 configs_to_remove = ['//build/config/compiler:default_optimization']
 configs_to_add = ['//build/config/compiler:optimize']
+allow_unsafe = false
+
+[crate.font-types.extra_kv]
+allow_unsafe = true
+
+[crate.hashbrown.extra_kv]
+# `hashbrown` crate is implicitly trusted because it comes from
+# https://github.com/rust-lang/ (and we already implicitly trust maintainers of
+# `rustc`).
+allow_unsafe = true
+
+[crate.heck.extra_kv]
+allow_unsafe = false
 
 [crate.hex]
 group = 'test'
+extra_kv = { allow_unsafe = false }
 
-[crate.itertools]
-group = 'test'
+[crate.iana-time-zone.extra_kv]
+allow_unsafe = true
+
+[crate.icu_calendar.extra_kv]
+allow_unsafe = false
 
 [crate.icu_calendar_data]
 extra_input_roots = ['../data']
 extra_src_roots = ['../data']
+extra_kv = { allow_unsafe = false }
 
 [crate.icu_casemap_data]
 extra_input_roots = ['../data']
@@ -162,6 +273,9 @@
 extra_input_roots = ['../data']
 extra_src_roots = ['../data']
 
+[crate.icu_collections]
+extra_kv = { allow_unsafe = true }
+
 [crate.icu_datetime_data]
 extra_input_roots = ['../data']
 extra_src_roots = ['../data']
@@ -178,6 +292,12 @@
 extra_input_roots = ['../data']
 extra_src_roots = ['../data']
 
+[crate.icu_locale]
+extra_kv = { allow_unsafe = true }
+
+[crate.icu_locale_core]
+extra_kv = { allow_unsafe = true }
+
 [crate.icu_locale_data]
 extra_input_roots = ['../data']
 extra_src_roots = ['../data']
@@ -194,6 +314,12 @@
 extra_input_roots = ['../data']
 extra_src_roots = ['../data']
 
+[crate.icu_provider]
+extra_kv = { allow_unsafe = true }
+
+[crate.icu_provider_baked]
+extra_kv = { allow_unsafe = true }
+
 [crate.icu_segmenter_data]
 extra_input_roots = ['../data']
 extra_src_roots = ['../data']
@@ -202,78 +328,336 @@
 extra_input_roots = ['../data']
 extra_src_roots = ['../data']
 
+[crate.icu_capi.extra_kv]
+allow_unsafe = false
+
+[crate.icu_casemap.extra_kv]
+allow_unsafe = false
+
+[crate.icu_casemap_data.extra_kv]
+allow_unsafe = false
+
+[crate.icu_decimal.extra_kv]
+allow_unsafe = false
+
+[crate.icu_decimal_data.extra_kv]
+allow_unsafe = false
+
+[crate.icu_experimental.extra_kv]
+allow_unsafe = false
+
+[crate.icu_experimental_data.extra_kv]
+allow_unsafe = false
+
+[crate.icu_list.extra_kv]
+allow_unsafe = false
+
+[crate.icu_list_data.extra_kv]
+allow_unsafe = false
+
+[crate.icu_locale_data.extra_kv]
+allow_unsafe = false
+
+[crate.icu_normalizer.extra_kv]
+allow_unsafe = false
+
+[crate.icu_normalizer_data.extra_kv]
+allow_unsafe = false
+
+[crate.icu_pattern.extra_kv]
+allow_unsafe = false
+
+[crate.icu_plurals.extra_kv]
+allow_unsafe = false
+
+[crate.icu_plurals_data.extra_kv]
+allow_unsafe = false
+
+[crate.icu_properties.extra_kv]
+allow_unsafe = false
+
+[crate.icu_properties_data.extra_kv]
+allow_unsafe = false
+
+[crate.icu_provider_adapters.extra_kv]
+allow_unsafe = false
+
+[crate.icu_time.extra_kv]
+allow_unsafe = false
+
+[crate.icu_time_data.extra_kv]
+allow_unsafe = false
+
+[crate.foldhash.extra_kv]
+allow_unsafe = true
+
+[crate.itertools]
+extra_kv = { allow_unsafe = true }
+group = 'test'
+
+[crate.indexmap.extra_kv]
+allow_unsafe = true
+
+[crate.itoa.extra_kv]
+allow_unsafe = true
+
+[crate.ixdtf.extra_kv]
+allow_unsafe = false
+
+[crate.lazy_static.extra_kv]
+allow_unsafe = true
+
 [crate.libc]
 ban_features = [
     # `extra_traits` feature is banned because of
     # https://github.com/rust-lang/libc/issues/3560
     'extra_traits',
 ]
+# `libc` crate is implicitly trusted because it comes from
+# https://github.com/rust-lang/ (and we already implicitly trust maintainers of
+# `rustc`).
+extra_kv = { allow_unsafe = true }
 
 [crate.libm]
 license_files= ["LICENSE.txt"]
 extra_build_script_src_roots = ['../configure.rs']
+# `libm` crate is implicitly trusted because it comes from
+# https://github.com/rust-lang/ (and we already implicitly trust maintainers of
+# `rustc`).
+extra_kv = { allow_unsafe = true }
 
-[crate.log]
+[crate.litemap.extra_kv]
+allow_unsafe = false
+
+[crate.llguidance.extra_kv]
+allow_unsafe = true
+
+[crate.log.extra_kv]
+allow_unsafe = true
+
+[crate.memchr.extra_kv]
+allow_unsafe = true
 
 [crate.miniz_oxide.extra_kv]
 # Web tests (aka WPT tests, also formerly known as Layout Tests) require fast
 # PNG encoding and decoding even in Debug builds.  See
 # https://crbug.com/396419181.
+allow_unsafe = false
 configs_to_remove = ['//build/config/compiler:default_optimization']
 configs_to_add = ['//build/config/compiler:optimize']
 
+[crate.num-bigint.extra_kv]
+allow_unsafe = false
+
+[crate.num-integer.extra_kv]
+allow_unsafe = false
+
+[crate.num-rational.extra_kv]
+allow_unsafe = false
+
 [crate.num-traits]
 # Configures the has_total_cmp feature, not necessary
 remove_build_rs = true
+extra_kv = { allow_unsafe = true }
 
 [crate.png.extra_kv]
+allow_unsafe = false
 # Web tests (aka WPT tests, also formerly known as Layout Tests) require fast
 # PNG encoding and decoding even in Debug builds.  See
 # https://crbug.com/396419181.
 configs_to_remove = ['//build/config/compiler:default_optimization']
 configs_to_add = ['//build/config/compiler:optimize']
 
+[crate.potential_utf.extra_kv]
+allow_unsafe = true
+
+[crate.proc-macro2.extra_kv]
+allow_unsafe = true
+
 [crate.prost]
 extra_input_roots = ['../README.md']
+extra_kv = { allow_unsafe = true }
 group = 'test'
 
 [crate.prost-derive]
+extra_kv = { allow_unsafe = false }
 group = 'test'
 
+[crate.qr_code.extra_kv]
+allow_unsafe = false
+
+[crate.quote.extra_kv]
+allow_unsafe = false
+
 [crate.read-fonts]
 extra_src_roots = ['../generated']
+extra_kv = { allow_unsafe = false }
+
+[crate.regex-automata.extra_kv]
+allow_unsafe = false
+
+[crate.regex-syntax.extra_kv]
+allow_unsafe = false
 
 [crate.rustversion]
 allow_first_party_usage = false
-extra_build_script_src_roots = ['../build/rustc.rs']
 build_script_outputs = [ "version.expr" ]
+extra_build_script_src_roots = ['../build/rustc.rs']
+extra_kv = { allow_unsafe = false }
+
+[crate.rustc-demangle.extra_kv]
+allow_unsafe = false
 
 [crate.rustc-demangle-capi]
+extra_kv = { allow_unsafe = true }
 shipped = false
 
+[crate.ryu.extra_kv]
+allow_unsafe = true
+
+[crate.serde.extra_kv]
+allow_unsafe = true
+
+[crate.serde_derive.extra_kv]
+allow_unsafe = false
+
+[crate.serde_json.extra_kv]
+allow_unsafe = true
+
+[crate.serde_json_lenient.extra_kv]
+allow_unsafe = true
+
+[crate.simd-adler32.extra_kv]
+allow_unsafe = true
+
 [crate.skrifa]
 extra_src_roots = ['../generated']
+extra_kv = { allow_unsafe = false }
 
 [crate.small_ctor]
 group = 'test'
+extra_kv = { allow_unsafe = false }
 
 [crate.smallvec]
 ban_features = ['malloc_size_of']
+extra_kv = { allow_unsafe = true }
+
+[crate.stable_deref_trait.extra_kv]
+allow_unsafe = true
+
+[crate.static_assertions.extra_kv]
+allow_unsafe = false
+
+[crate.strck.extra_kv]
+allow_unsafe = true
+
+[crate.strsim.extra_kv]
+allow_unsafe = false
+
+[crate.strum.extra_kv]
+allow_unsafe = false
+
+[crate.strum_macros.extra_kv]
+allow_unsafe = false
+
+[crate.subtle.extra_kv]
+allow_unsafe = false
+
+[crate.syn.extra_kv]
+allow_unsafe = true
+
+[crate.synstructure.extra_kv]
+allow_unsafe = false
 
 [crate.temporal_capi]
+extra_kv = { allow_unsafe = false }
 license_files = ["LICENSE-MIT", "LICENSE-APACHE"]
 
 [crate.temporal_rs]
+extra_kv = { allow_unsafe = true }
 license_files = ["LICENSE-MIT", "LICENSE-APACHE"]
 
-[crate.windows-sys]
-extra_kv = { visibility = [ '//build/rust/tests/windows_sys_test' ] }
+[crate.termcolor.extra_kv]
+allow_unsafe = false
+
+[crate.toktrie.extra_kv]
+allow_unsafe = false
+
+[crate.tinystr.extra_kv]
+allow_unsafe = true
+
+[crate.unicode-ident.extra_kv]
+allow_unsafe = true
+
+[crate.unicode-width.extra_kv]
+allow_unsafe = false
+
+[crate.utf8_iter.extra_kv]
+allow_unsafe = true
+
+[crate.winapi-util.extra_kv]
+allow_unsafe = true
+
+[crate.windows-core.extra_kv]
+allow_unsafe = false
+
+[crate.windows-implement.extra_kv]
+allow_unsafe = false
+
+[crate.windows-interface.extra_kv]
+allow_unsafe = false
+
+[crate.windows-link.extra_kv]
+allow_unsafe = false
+
+[crate.windows-result.extra_kv]
+allow_unsafe = false
+
+[crate.windows-strings.extra_kv]
+allow_unsafe = false
+
+[crate.windows-sys.extra_kv]
+allow_unsafe = false
+visibility = [ '//build/rust/tests/windows_sys_test' ]
+
+[crate.windows-targets.extra_kv]
+allow_unsafe = false
 
 [crate.windows_aarch64_msvc]
 native_libs_roots = [ '../lib' ]
+extra_kv = { allow_unsafe = false }
 
 [crate.windows_i686_msvc]
 native_libs_roots = [ '../lib' ]
+extra_kv = { allow_unsafe = false }
 
 [crate.windows_x86_64_msvc]
 native_libs_roots = [ '../lib' ]
+extra_kv = { allow_unsafe = false }
+
+[crate.writeable.extra_kv]
+allow_unsafe = true
+
+[crate.yoke.extra_kv]
+allow_unsafe = true
+
+[crate.yoke-derive.extra_kv]
+allow_unsafe = false
+
+[crate.zerofrom.extra_kv]
+allow_unsafe = false
+
+[crate.zerofrom-derive.extra_kv]
+allow_unsafe = false
+
+[crate.zerotrie.extra_kv]
+allow_unsafe = true
+
+[crate.zerovec.extra_kv]
+allow_unsafe = true
+
+[crate.zerovec-derive.extra_kv]
+allow_unsafe = false
+
+[crate.zip.extra_kv]
+allow_unsafe = false
diff --git a/third_party/rust/chromium_crates_io/patches/zip/0101-flate2-build-fix.patch b/third_party/rust/chromium_crates_io/patches/zip/0101-flate2-build-fix.patch
deleted file mode 100644
index 3e2cfd60..0000000
--- a/third_party/rust/chromium_crates_io/patches/zip/0101-flate2-build-fix.patch
+++ /dev/null
@@ -1,32 +0,0 @@
-From 11aa88c19ef271e47219beaa3442143f7002418c Mon Sep 17 00:00:00 2001
-From: Alexis Hetu <sugoi@chromium.org>
-Date: Mon, 12 May 2025 15:07:30 -0400
-Subject: [PATCH] flate2 build fix
-
----
- .../rust/chromium_crates_io/vendor/zip-v2/Cargo.toml        | 6 ++++--
- 1 file changed, 4 insertions(+), 2 deletions(-)
-
-diff --git a/third_party/rust/chromium_crates_io/vendor/zip-v2/Cargo.toml b/third_party/rust/chromium_crates_io/vendor/zip-v2/Cargo.toml
-index 0793748cb0447..7052e9869aada 100644
---- a/third_party/rust/chromium_crates_io/vendor/zip-v2/Cargo.toml
-+++ b/third_party/rust/chromium_crates_io/vendor/zip-v2/Cargo.toml
-@@ -76,11 +76,13 @@ default = [
-     "xz",
- ]
- deflate = [
--    "flate2/rust_backend",
-     "deflate-zopfli",
-     "deflate-flate2",
- ]
--deflate-flate2 = ["_deflate-any"]
-+deflate-flate2 = [
-+    "_deflate-any",
-+    "flate2/rust_backend",
-+]
- deflate-miniz = [
-     "deflate",
-     "deflate-flate2",
--- 
-2.49.0.1045.g170613ef41-goog
-
diff --git a/third_party/rust/chromium_crates_io/supply-chain/config.toml b/third_party/rust/chromium_crates_io/supply-chain/config.toml
index 6207e9a..0622c5c 100644
--- a/third_party/rust/chromium_crates_io/supply-chain/config.toml
+++ b/third_party/rust/chromium_crates_io/supply-chain/config.toml
@@ -107,9 +107,6 @@
 [policy."crc32fast:1.4.2"]
 criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
 
-[policy."crossbeam-utils:0.8.21"]
-criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
-
 [policy."cxx:1.0.158"]
 criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
 
@@ -551,7 +548,7 @@
 [policy."zerovec:0.11.1"]
 criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
 
-[policy."zip:2.6.1"]
+[policy."zip:3.0.0"]
 criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
 
 [[exemptions.aho-corasick]]
diff --git a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/.cargo-checksum.json b/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/.cargo-checksum.json
deleted file mode 100644
index 697c9ce..0000000
--- a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/.cargo-checksum.json
+++ /dev/null
@@ -1 +0,0 @@
-{"files":{}}
diff --git a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/.cargo_vcs_info.json b/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/.cargo_vcs_info.json
deleted file mode 100644
index 87108da..0000000
--- a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/.cargo_vcs_info.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-  "git": {
-    "sha1": "ccd83ac4108a2a1b41e9c6e79c87267167d18dfa"
-  },
-  "path_in_vcs": "crossbeam-utils"
-}
\ No newline at end of file
diff --git a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/CHANGELOG.md b/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/CHANGELOG.md
deleted file mode 100644
index 5aa1967..0000000
--- a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/CHANGELOG.md
+++ /dev/null
@@ -1,243 +0,0 @@
-# Version 0.8.21
-
-- Improve implementation of `CachePadded`. (#1152)
-
-# Version 0.8.20
-
-- Implement `Display` for `CachePadded`. (#1097)
-
-# Version 0.8.19
-
-- Remove dependency on `cfg-if`. (#1072)
-
-# Version 0.8.18
-
-- Relax the minimum supported Rust version to 1.60. (#1056)
-- Improve scalability of `AtomicCell` fallback. (#1055)
-
-# Version 0.8.17
-
-- Bump the minimum supported Rust version to 1.61. (#1037)
-- Improve support for targets without atomic CAS or 64-bit atomic. (#1037)
-- Always implement `UnwindSafe` and `RefUnwindSafe` for `AtomicCell`. (#1045)
-- Improve compatibility with Miri, TSan, and loom. (#995, #1003)
-- Improve compatibility with unstable `oom=panic`. (#1045)
-- Improve implementation of `CachePadded`. (#1014, #1025)
-- Update `loom` dependency to 0.7.
-
-# Version 0.8.16
-
-- Improve implementation of `CachePadded`. (#967)
-
-# Version 0.8.15
-
-- Add `#[clippy::has_significant_drop]` to `ShardedLock{Read,Write}Guard`. (#958)
-- Improve handling of very large timeout. (#953)
-- Soft-deprecate `thread::scope()` in favor of the more efficient `std::thread::scope` that stabilized in Rust 1.63. (#954)
-
-# Version 0.8.14
-
-- Fix build script bug introduced in 0.8.13. (#932)
-
-# Version 0.8.13
-
-**Note:** This release has been yanked due to regression fixed in 0.8.14.
-
-- Improve support for custom targets. (#922)
-
-# Version 0.8.12
-
-- Removes the dependency on the `once_cell` crate to restore the MSRV. (#913)
-- Work around [rust-lang#98302](https://github.com/rust-lang/rust/issues/98302), which causes compile error on windows-gnu when LTO is enabled. (#913)
-
-# Version 0.8.11
-
-- Bump the minimum supported Rust version to 1.38. (#877)
-
-# Version 0.8.10
-
-- Fix unsoundness of `AtomicCell` on types containing niches. (#834)
-  This fix contains breaking changes, but they are allowed because this is a soundness bug fix. See #834 for more.
-
-# Version 0.8.9
-
-- Replace lazy_static with once_cell. (#817)
-
-# Version 0.8.8
-
-- Fix a bug when unstable `loom` support is enabled. (#787)
-
-# Version 0.8.7
-
-- Add `AtomicCell<{i*,u*}>::{fetch_max,fetch_min}`. (#785)
-- Add `AtomicCell<{i*,u*,bool}>::fetch_nand`. (#785)
-- Fix unsoundness of `AtomicCell<{i,u}64>` arithmetics on 32-bit targets that support `Atomic{I,U}64` (#781)
-
-# Version 0.8.6
-
-**Note:** This release has been yanked. See [GHSA-qc84-gqf4-9926](https://github.com/crossbeam-rs/crossbeam/security/advisories/GHSA-qc84-gqf4-9926) for details.
-
-- Re-add `AtomicCell<{i,u}64>::{fetch_add,fetch_sub,fetch_and,fetch_or,fetch_xor}` that were accidentally removed in 0.8.0 on targets that do not support `Atomic{I,U}64`. (#767)
-- Re-add `AtomicCell<{i,u}128>::{fetch_add,fetch_sub,fetch_and,fetch_or,fetch_xor}` that were accidentally removed in 0.8.0. (#767)
-
-# Version 0.8.5
-
-**Note:** This release has been yanked. See [GHSA-qc84-gqf4-9926](https://github.com/crossbeam-rs/crossbeam/security/advisories/GHSA-qc84-gqf4-9926) for details.
-
-- Add `AtomicCell::fetch_update`. (#704)
-- Support targets that do not have atomic CAS on stable Rust. (#698)
-
-# Version 0.8.4
-
-**Note:** This release has been yanked. See [GHSA-qc84-gqf4-9926](https://github.com/crossbeam-rs/crossbeam/security/advisories/GHSA-qc84-gqf4-9926) for details.
-
-- Bump `loom` dependency to version 0.5. (#686)
-
-# Version 0.8.3
-
-**Note:** This release has been yanked. See [GHSA-qc84-gqf4-9926](https://github.com/crossbeam-rs/crossbeam/security/advisories/GHSA-qc84-gqf4-9926) for details.
-
-- Make `loom` dependency optional. (#666)
-
-# Version 0.8.2
-
-**Note:** This release has been yanked. See [GHSA-qc84-gqf4-9926](https://github.com/crossbeam-rs/crossbeam/security/advisories/GHSA-qc84-gqf4-9926) for details.
-
-- Deprecate `AtomicCell::compare_and_swap`. Use `AtomicCell::compare_exchange` instead. (#619)
-- Add `Parker::park_deadline`. (#563)
-- Improve implementation of `CachePadded`. (#636)
-- Add unstable support for `loom`. (#487)
-
-# Version 0.8.1
-
-**Note:** This release has been yanked. See [GHSA-qc84-gqf4-9926](https://github.com/crossbeam-rs/crossbeam/security/advisories/GHSA-qc84-gqf4-9926) for details.
-
-- Make `AtomicCell::is_lock_free` always const fn. (#600)
-- Fix a bug in `seq_lock_wide`. (#596)
-- Remove `const_fn` dependency. (#600)
-- `crossbeam-utils` no longer fails to compile if unable to determine rustc version. Instead, it now displays a warning. (#604)
-
-# Version 0.8.0
-
-**Note:** This release has been yanked. See [GHSA-qc84-gqf4-9926](https://github.com/crossbeam-rs/crossbeam/security/advisories/GHSA-qc84-gqf4-9926) for details.
-
-- Bump the minimum supported Rust version to 1.36.
-- Remove deprecated `AtomicCell::get_mut()` and `Backoff::is_complete()` methods.
-- Remove `alloc` feature.
-- Make `CachePadded::new()` const function.
-- Make `AtomicCell::is_lock_free()` const function at 1.46+.
-- Implement `From<T>` for `AtomicCell<T>`.
-
-# Version 0.7.2
-
-- Fix bug in release (yanking 0.7.1)
-
-# Version 0.7.1
-
-- Bump `autocfg` dependency to version 1.0. (#460)
-- Make `AtomicCell` lockfree for u8, u16, u32, u64 sized values at 1.34+. (#454)
-
-# Version 0.7.0
-
-- Bump the minimum required version to 1.28.
-- Fix breakage with nightly feature due to rust-lang/rust#65214.
-- Apply `#[repr(transparent)]` to `AtomicCell`.
-- Make `AtomicCell::new()` const function at 1.31+.
-
-# Version 0.6.6
-
-- Add `UnwindSafe` and `RefUnwindSafe` impls for `AtomicCell`.
-- Add `AtomicCell::as_ptr()`.
-- Add `AtomicCell::take()`.
-- Fix a bug in `AtomicCell::compare_exchange()` and `AtomicCell::compare_and_swap()`.
-- Various documentation improvements.
-
-# Version 0.6.5
-
-- Rename `Backoff::is_complete()` to `Backoff::is_completed()`.
-
-# Version 0.6.4
-
-- Add `WaitGroup`, `ShardedLock`, and `Backoff`.
-- Add `fetch_*` methods for `AtomicCell<i128>` and `AtomicCell<u128>`.
-- Expand documentation.
-
-# Version 0.6.3
-
-- Add `AtomicCell`.
-- Improve documentation.
-
-# Version 0.6.2
-
-- Add `Parker`.
-- Improve documentation.
-
-# Version 0.6.1
-
-- Fix a soundness bug in `Scope::spawn()`.
-- Remove the `T: 'scope` bound on `ScopedJoinHandle`.
-
-# Version 0.6.0
-
-- Move `AtomicConsume` to `atomic` module.
-- `scope()` returns a `Result` of thread joins.
-- Remove `spawn_unchecked`.
-- Fix a soundness bug due to incorrect lifetimes.
-- Improve documentation.
-- Support nested scoped spawns.
-- Implement `Copy`, `Hash`, `PartialEq`, and `Eq` for `CachePadded`.
-- Add `CachePadded::into_inner()`.
-
-# Version 0.5.0
-
-- Reorganize sub-modules and rename functions.
-
-# Version 0.4.1
-
-- Fix a documentation link.
-
-# Version 0.4.0
-
-- `CachePadded` supports types bigger than 64 bytes.
-- Fix a bug in scoped threads where unitialized memory was being dropped.
-- Minimum required Rust version is now 1.25.
-
-# Version 0.3.2
-
-- Mark `load_consume` with `#[inline]`.
-
-# Version 0.3.1
-
-- `load_consume` on ARM and AArch64.
-
-# Version 0.3.0
-
-- Add `join` for scoped thread API.
-- Add `load_consume` for atomic load-consume memory ordering.
-- Remove `AtomicOption`.
-
-# Version 0.2.2
-
-- Support Rust 1.12.1.
-- Call `T::clone` when cloning a `CachePadded<T>`.
-
-# Version 0.2.1
-
-- Add `use_std` feature.
-
-# Version 0.2.0
-
-- Add `nightly` feature.
-- Use `repr(align(64))` on `CachePadded` with the `nightly` feature.
-- Implement `Drop` for `CachePadded<T>`.
-- Implement `Clone` for `CachePadded<T>`.
-- Implement `From<T>` for `CachePadded<T>`.
-- Implement better `Debug` for `CachePadded<T>`.
-- Write more tests.
-- Add this changelog.
-- Change cache line length to 64 bytes.
-- Remove `ZerosValid`.
-
-# Version 0.1.0
-
-- Old implementation of `CachePadded` from `crossbeam` version 0.3.0
diff --git a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/Cargo.toml b/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/Cargo.toml
deleted file mode 100644
index c93dc2f..0000000
--- a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/Cargo.toml
+++ /dev/null
@@ -1,101 +0,0 @@
-# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
-#
-# When uploading crates to the registry Cargo will automatically
-# "normalize" Cargo.toml files for maximal compatibility
-# with all versions of Cargo and also rewrite `path` dependencies
-# to registry (e.g., crates.io) dependencies.
-#
-# If you are reading this file be aware that the original Cargo.toml
-# will likely look very different (and much more reasonable).
-# See Cargo.toml.orig for the original contents.
-
-[package]
-edition = "2021"
-rust-version = "1.60"
-name = "crossbeam-utils"
-version = "0.8.21"
-build = "build.rs"
-autolib = false
-autobins = false
-autoexamples = false
-autotests = false
-autobenches = false
-description = "Utilities for concurrent programming"
-homepage = "https://github.com/crossbeam-rs/crossbeam/tree/master/crossbeam-utils"
-readme = "README.md"
-keywords = [
-    "scoped",
-    "thread",
-    "atomic",
-    "cache",
-]
-categories = [
-    "algorithms",
-    "concurrency",
-    "data-structures",
-    "no-std",
-]
-license = "MIT OR Apache-2.0"
-repository = "https://github.com/crossbeam-rs/crossbeam"
-
-[lib]
-name = "crossbeam_utils"
-path = "src/lib.rs"
-
-[[test]]
-name = "atomic_cell"
-path = "tests/atomic_cell.rs"
-
-[[test]]
-name = "cache_padded"
-path = "tests/cache_padded.rs"
-
-[[test]]
-name = "parker"
-path = "tests/parker.rs"
-
-[[test]]
-name = "sharded_lock"
-path = "tests/sharded_lock.rs"
-
-[[test]]
-name = "thread"
-path = "tests/thread.rs"
-
-[[test]]
-name = "wait_group"
-path = "tests/wait_group.rs"
-
-[[bench]]
-name = "atomic_cell"
-path = "benches/atomic_cell.rs"
-
-[dependencies]
-
-[dev-dependencies.rand]
-version = "0.8"
-
-[features]
-default = ["std"]
-nightly = []
-std = []
-
-[target."cfg(crossbeam_loom)".dependencies.loom]
-version = "0.7.1"
-optional = true
-
-[lints.clippy.declare_interior_mutable_const]
-level = "allow"
-priority = 1
-
-[lints.clippy.lint_groups_priority]
-level = "allow"
-priority = 1
-
-[lints.rust.unexpected_cfgs]
-level = "warn"
-priority = 0
-check-cfg = [
-    "cfg(crossbeam_loom)",
-    "cfg(crossbeam_sanitize)",
-]
diff --git a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/Cargo.toml.orig b/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/Cargo.toml.orig
deleted file mode 100644
index 3a95bae..0000000
--- a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/Cargo.toml.orig
+++ /dev/null
@@ -1,46 +0,0 @@
-[package]
-name = "crossbeam-utils"
-# When publishing a new version:
-# - Update CHANGELOG.md
-# - Update README.md (when increasing major or minor version)
-# - Run './tools/publish.sh crossbeam-utils <version>'
-version = "0.8.21"
-edition = "2021"
-rust-version = "1.60"
-license = "MIT OR Apache-2.0"
-repository = "https://github.com/crossbeam-rs/crossbeam"
-homepage = "https://github.com/crossbeam-rs/crossbeam/tree/master/crossbeam-utils"
-description = "Utilities for concurrent programming"
-keywords = ["scoped", "thread", "atomic", "cache"]
-categories = ["algorithms", "concurrency", "data-structures", "no-std"]
-
-[features]
-default = ["std"]
-
-# Enable to use APIs that require `std`.
-# This is enabled by default.
-std = []
-
-# These features are no longer used.
-# TODO: remove in the next major version.
-# Enable to use of unstable functionality.
-# This is disabled by default and requires recent nightly compiler.
-#
-# NOTE: This feature is outside of the normal semver guarantees and minor or
-# patch versions of crossbeam may make breaking changes to them at any time.
-nightly = []
-
-[dependencies]
-
-# Enable the use of loom for concurrency testing.
-#
-# NOTE: This feature is outside of the normal semver guarantees and minor or
-# patch versions of crossbeam may make breaking changes to them at any time.
-[target.'cfg(crossbeam_loom)'.dependencies]
-loom = { version = "0.7.1", optional = true }
-
-[dev-dependencies]
-rand = "0.8"
-
-[lints]
-workspace = true
diff --git a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/LICENSE-APACHE b/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/LICENSE-APACHE
deleted file mode 100644
index 16fe87b..0000000
--- a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/LICENSE-APACHE
+++ /dev/null
@@ -1,201 +0,0 @@
-                              Apache License
-                        Version 2.0, January 2004
-                     http://www.apache.org/licenses/
-
-TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-1. Definitions.
-
-   "License" shall mean the terms and conditions for use, reproduction,
-   and distribution as defined by Sections 1 through 9 of this document.
-
-   "Licensor" shall mean the copyright owner or entity authorized by
-   the copyright owner that is granting the License.
-
-   "Legal Entity" shall mean the union of the acting entity and all
-   other entities that control, are controlled by, or are under common
-   control with that entity. For the purposes of this definition,
-   "control" means (i) the power, direct or indirect, to cause the
-   direction or management of such entity, whether by contract or
-   otherwise, or (ii) ownership of fifty percent (50%) or more of the
-   outstanding shares, or (iii) beneficial ownership of such entity.
-
-   "You" (or "Your") shall mean an individual or Legal Entity
-   exercising permissions granted by this License.
-
-   "Source" form shall mean the preferred form for making modifications,
-   including but not limited to software source code, documentation
-   source, and configuration files.
-
-   "Object" form shall mean any form resulting from mechanical
-   transformation or translation of a Source form, including but
-   not limited to compiled object code, generated documentation,
-   and conversions to other media types.
-
-   "Work" shall mean the work of authorship, whether in Source or
-   Object form, made available under the License, as indicated by a
-   copyright notice that is included in or attached to the work
-   (an example is provided in the Appendix below).
-
-   "Derivative Works" shall mean any work, whether in Source or Object
-   form, that is based on (or derived from) the Work and for which the
-   editorial revisions, annotations, elaborations, or other modifications
-   represent, as a whole, an original work of authorship. For the purposes
-   of this License, Derivative Works shall not include works that remain
-   separable from, or merely link (or bind by name) to the interfaces of,
-   the Work and Derivative Works thereof.
-
-   "Contribution" shall mean any work of authorship, including
-   the original version of the Work and any modifications or additions
-   to that Work or Derivative Works thereof, that is intentionally
-   submitted to Licensor for inclusion in the Work by the copyright owner
-   or by an individual or Legal Entity authorized to submit on behalf of
-   the copyright owner. For the purposes of this definition, "submitted"
-   means any form of electronic, verbal, or written communication sent
-   to the Licensor or its representatives, including but not limited to
-   communication on electronic mailing lists, source code control systems,
-   and issue tracking systems that are managed by, or on behalf of, the
-   Licensor for the purpose of discussing and improving the Work, but
-   excluding communication that is conspicuously marked or otherwise
-   designated in writing by the copyright owner as "Not a Contribution."
-
-   "Contributor" shall mean Licensor and any individual or Legal Entity
-   on behalf of whom a Contribution has been received by Licensor and
-   subsequently incorporated within the Work.
-
-2. Grant of Copyright License. Subject to the terms and conditions of
-   this License, each Contributor hereby grants to You a perpetual,
-   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-   copyright license to reproduce, prepare Derivative Works of,
-   publicly display, publicly perform, sublicense, and distribute the
-   Work and such Derivative Works in Source or Object form.
-
-3. Grant of Patent License. Subject to the terms and conditions of
-   this License, each Contributor hereby grants to You a perpetual,
-   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-   (except as stated in this section) patent license to make, have made,
-   use, offer to sell, sell, import, and otherwise transfer the Work,
-   where such license applies only to those patent claims licensable
-   by such Contributor that are necessarily infringed by their
-   Contribution(s) alone or by combination of their Contribution(s)
-   with the Work to which such Contribution(s) was submitted. If You
-   institute patent litigation against any entity (including a
-   cross-claim or counterclaim in a lawsuit) alleging that the Work
-   or a Contribution incorporated within the Work constitutes direct
-   or contributory patent infringement, then any patent licenses
-   granted to You under this License for that Work shall terminate
-   as of the date such litigation is filed.
-
-4. Redistribution. You may reproduce and distribute copies of the
-   Work or Derivative Works thereof in any medium, with or without
-   modifications, and in Source or Object form, provided that You
-   meet the following conditions:
-
-   (a) You must give any other recipients of the Work or
-       Derivative Works a copy of this License; and
-
-   (b) You must cause any modified files to carry prominent notices
-       stating that You changed the files; and
-
-   (c) You must retain, in the Source form of any Derivative Works
-       that You distribute, all copyright, patent, trademark, and
-       attribution notices from the Source form of the Work,
-       excluding those notices that do not pertain to any part of
-       the Derivative Works; and
-
-   (d) If the Work includes a "NOTICE" text file as part of its
-       distribution, then any Derivative Works that You distribute must
-       include a readable copy of the attribution notices contained
-       within such NOTICE file, excluding those notices that do not
-       pertain to any part of the Derivative Works, in at least one
-       of the following places: within a NOTICE text file distributed
-       as part of the Derivative Works; within the Source form or
-       documentation, if provided along with the Derivative Works; or,
-       within a display generated by the Derivative Works, if and
-       wherever such third-party notices normally appear. The contents
-       of the NOTICE file are for informational purposes only and
-       do not modify the License. You may add Your own attribution
-       notices within Derivative Works that You distribute, alongside
-       or as an addendum to the NOTICE text from the Work, provided
-       that such additional attribution notices cannot be construed
-       as modifying the License.
-
-   You may add Your own copyright statement to Your modifications and
-   may provide additional or different license terms and conditions
-   for use, reproduction, or distribution of Your modifications, or
-   for any such Derivative Works as a whole, provided Your use,
-   reproduction, and distribution of the Work otherwise complies with
-   the conditions stated in this License.
-
-5. Submission of Contributions. Unless You explicitly state otherwise,
-   any Contribution intentionally submitted for inclusion in the Work
-   by You to the Licensor shall be under the terms and conditions of
-   this License, without any additional terms or conditions.
-   Notwithstanding the above, nothing herein shall supersede or modify
-   the terms of any separate license agreement you may have executed
-   with Licensor regarding such Contributions.
-
-6. Trademarks. This License does not grant permission to use the trade
-   names, trademarks, service marks, or product names of the Licensor,
-   except as required for reasonable and customary use in describing the
-   origin of the Work and reproducing the content of the NOTICE file.
-
-7. Disclaimer of Warranty. Unless required by applicable law or
-   agreed to in writing, Licensor provides the Work (and each
-   Contributor provides its Contributions) on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-   implied, including, without limitation, any warranties or conditions
-   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-   PARTICULAR PURPOSE. You are solely responsible for determining the
-   appropriateness of using or redistributing the Work and assume any
-   risks associated with Your exercise of permissions under this License.
-
-8. Limitation of Liability. In no event and under no legal theory,
-   whether in tort (including negligence), contract, or otherwise,
-   unless required by applicable law (such as deliberate and grossly
-   negligent acts) or agreed to in writing, shall any Contributor be
-   liable to You for damages, including any direct, indirect, special,
-   incidental, or consequential damages of any character arising as a
-   result of this License or out of the use or inability to use the
-   Work (including but not limited to damages for loss of goodwill,
-   work stoppage, computer failure or malfunction, or any and all
-   other commercial damages or losses), even if such Contributor
-   has been advised of the possibility of such damages.
-
-9. Accepting Warranty or Additional Liability. While redistributing
-   the Work or Derivative Works thereof, You may choose to offer,
-   and charge a fee for, acceptance of support, warranty, indemnity,
-   or other liability obligations and/or rights consistent with this
-   License. However, in accepting such obligations, You may act only
-   on Your own behalf and on Your sole responsibility, not on behalf
-   of any other Contributor, and only if You agree to indemnify,
-   defend, and hold each Contributor harmless for any liability
-   incurred by, or claims asserted against, such Contributor by reason
-   of your accepting any such warranty or additional liability.
-
-END OF TERMS AND CONDITIONS
-
-APPENDIX: How to apply the Apache License to your work.
-
-   To apply the Apache License to your work, attach the following
-   boilerplate notice, with the fields enclosed by brackets "[]"
-   replaced with your own identifying information. (Don't include
-   the brackets!)  The text should be enclosed in the appropriate
-   comment syntax for the file format. We also recommend that a
-   file or class name and description of purpose be included on the
-   same "printed page" as the copyright notice for easier
-   identification within third-party archives.
-
-Copyright [yyyy] [name of copyright owner]
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-	http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
diff --git a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/LICENSE-MIT b/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/LICENSE-MIT
deleted file mode 100644
index 068d491..0000000
--- a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/LICENSE-MIT
+++ /dev/null
@@ -1,27 +0,0 @@
-The MIT License (MIT)
-
-Copyright (c) 2019 The Crossbeam Project Developers
-
-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
-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.
diff --git a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/README.md b/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/README.md
deleted file mode 100644
index 7d6a679..0000000
--- a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/README.md
+++ /dev/null
@@ -1,73 +0,0 @@
-# Crossbeam Utils
-
-[![Build Status](https://github.com/crossbeam-rs/crossbeam/workflows/CI/badge.svg)](
-https://github.com/crossbeam-rs/crossbeam/actions)
-[![License](https://img.shields.io/badge/license-MIT_OR_Apache--2.0-blue.svg)](
-https://github.com/crossbeam-rs/crossbeam/tree/master/crossbeam-utils#license)
-[![Cargo](https://img.shields.io/crates/v/crossbeam-utils.svg)](
-https://crates.io/crates/crossbeam-utils)
-[![Documentation](https://docs.rs/crossbeam-utils/badge.svg)](
-https://docs.rs/crossbeam-utils)
-[![Rust 1.60+](https://img.shields.io/badge/rust-1.60+-lightgray.svg)](
-https://www.rust-lang.org)
-[![chat](https://img.shields.io/discord/569610676205781012.svg?logo=discord)](https://discord.com/invite/JXYwgWZ)
-
-This crate provides miscellaneous tools for concurrent programming:
-
-#### Atomics
-
-* [`AtomicCell`], a thread-safe mutable memory location.<sup>(no_std)</sup>
-* [`AtomicConsume`], for reading from primitive atomic types with "consume" ordering.<sup>(no_std)</sup>
-
-#### Thread synchronization
-
-* [`Parker`], a thread parking primitive.
-* [`ShardedLock`], a sharded reader-writer lock with fast concurrent reads.
-* [`WaitGroup`], for synchronizing the beginning or end of some computation.
-
-#### Utilities
-
-* [`Backoff`], for exponential backoff in spin loops.<sup>(no_std)</sup>
-* [`CachePadded`], for padding and aligning a value to the length of a cache line.<sup>(no_std)</sup>
-* [`scope`], for spawning threads that borrow local variables from the stack.
-
-*Features marked with <sup>(no_std)</sup> can be used in `no_std` environments.*<br/>
-
-[`AtomicCell`]: https://docs.rs/crossbeam-utils/*/crossbeam_utils/atomic/struct.AtomicCell.html
-[`AtomicConsume`]: https://docs.rs/crossbeam-utils/*/crossbeam_utils/atomic/trait.AtomicConsume.html
-[`Parker`]: https://docs.rs/crossbeam-utils/*/crossbeam_utils/sync/struct.Parker.html
-[`ShardedLock`]: https://docs.rs/crossbeam-utils/*/crossbeam_utils/sync/struct.ShardedLock.html
-[`WaitGroup`]: https://docs.rs/crossbeam-utils/*/crossbeam_utils/sync/struct.WaitGroup.html
-[`Backoff`]: https://docs.rs/crossbeam-utils/*/crossbeam_utils/struct.Backoff.html
-[`CachePadded`]: https://docs.rs/crossbeam-utils/*/crossbeam_utils/struct.CachePadded.html
-[`scope`]: https://docs.rs/crossbeam-utils/*/crossbeam_utils/thread/fn.scope.html
-
-## Usage
-
-Add this to your `Cargo.toml`:
-
-```toml
-[dependencies]
-crossbeam-utils = "0.8"
-```
-
-## Compatibility
-
-Crossbeam Utils supports stable Rust releases going back at least six months,
-and every time the minimum supported Rust version is increased, a new minor
-version is released. Currently, the minimum supported Rust version is 1.60.
-
-## License
-
-Licensed under either of
-
- * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
- * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
-
-at your option.
-
-#### Contribution
-
-Unless you explicitly state otherwise, any contribution intentionally submitted
-for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
-dual licensed as above, without any additional terms or conditions.
diff --git a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/benches/atomic_cell.rs b/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/benches/atomic_cell.rs
deleted file mode 100644
index 844f7c02..0000000
--- a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/benches/atomic_cell.rs
+++ /dev/null
@@ -1,156 +0,0 @@
-#![feature(test)]
-
-extern crate test;
-
-use std::sync::Barrier;
-
-use crossbeam_utils::atomic::AtomicCell;
-use crossbeam_utils::thread;
-
-#[bench]
-fn load_u8(b: &mut test::Bencher) {
-    let a = AtomicCell::new(0u8);
-    let mut sum = 0;
-    b.iter(|| sum += a.load());
-    test::black_box(sum);
-}
-
-#[bench]
-fn store_u8(b: &mut test::Bencher) {
-    let a = AtomicCell::new(0u8);
-    b.iter(|| a.store(1));
-}
-
-#[bench]
-fn fetch_add_u8(b: &mut test::Bencher) {
-    let a = AtomicCell::new(0u8);
-    b.iter(|| a.fetch_add(1));
-}
-
-#[bench]
-fn compare_exchange_u8(b: &mut test::Bencher) {
-    let a = AtomicCell::new(0u8);
-    let mut i = 0;
-    b.iter(|| {
-        let _ = a.compare_exchange(i, i.wrapping_add(1));
-        i = i.wrapping_add(1);
-    });
-}
-
-#[bench]
-fn concurrent_load_u8(b: &mut test::Bencher) {
-    const THREADS: usize = 2;
-    const STEPS: usize = 1_000_000;
-
-    let start = Barrier::new(THREADS + 1);
-    let end = Barrier::new(THREADS + 1);
-    let exit = AtomicCell::new(false);
-
-    let a = AtomicCell::new(0u8);
-
-    thread::scope(|scope| {
-        for _ in 0..THREADS {
-            scope.spawn(|_| loop {
-                start.wait();
-
-                let mut sum = 0;
-                for _ in 0..STEPS {
-                    sum += a.load();
-                }
-                test::black_box(sum);
-
-                end.wait();
-                if exit.load() {
-                    break;
-                }
-            });
-        }
-
-        start.wait();
-        end.wait();
-
-        b.iter(|| {
-            start.wait();
-            end.wait();
-        });
-
-        start.wait();
-        exit.store(true);
-        end.wait();
-    })
-    .unwrap();
-}
-
-#[bench]
-fn load_usize(b: &mut test::Bencher) {
-    let a = AtomicCell::new(0usize);
-    let mut sum = 0;
-    b.iter(|| sum += a.load());
-    test::black_box(sum);
-}
-
-#[bench]
-fn store_usize(b: &mut test::Bencher) {
-    let a = AtomicCell::new(0usize);
-    b.iter(|| a.store(1));
-}
-
-#[bench]
-fn fetch_add_usize(b: &mut test::Bencher) {
-    let a = AtomicCell::new(0usize);
-    b.iter(|| a.fetch_add(1));
-}
-
-#[bench]
-fn compare_exchange_usize(b: &mut test::Bencher) {
-    let a = AtomicCell::new(0usize);
-    let mut i = 0;
-    b.iter(|| {
-        let _ = a.compare_exchange(i, i.wrapping_add(1));
-        i = i.wrapping_add(1);
-    });
-}
-
-#[bench]
-fn concurrent_load_usize(b: &mut test::Bencher) {
-    const THREADS: usize = 2;
-    const STEPS: usize = 1_000_000;
-
-    let start = Barrier::new(THREADS + 1);
-    let end = Barrier::new(THREADS + 1);
-    let exit = AtomicCell::new(false);
-
-    let a = AtomicCell::new(0usize);
-
-    thread::scope(|scope| {
-        for _ in 0..THREADS {
-            scope.spawn(|_| loop {
-                start.wait();
-
-                let mut sum = 0;
-                for _ in 0..STEPS {
-                    sum += a.load();
-                }
-                test::black_box(sum);
-
-                end.wait();
-                if exit.load() {
-                    break;
-                }
-            });
-        }
-
-        start.wait();
-        end.wait();
-
-        b.iter(|| {
-            start.wait();
-            end.wait();
-        });
-
-        start.wait();
-        exit.store(true);
-        end.wait();
-    })
-    .unwrap();
-}
diff --git a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/build-common.rs b/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/build-common.rs
deleted file mode 100644
index e91bb4d..0000000
--- a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/build-common.rs
+++ /dev/null
@@ -1,13 +0,0 @@
-// The target triplets have the form of 'arch-vendor-system'.
-//
-// When building for Linux (e.g. the 'system' part is
-// 'linux-something'), replace the vendor with 'unknown'
-// so that mapping to rust standard targets happens correctly.
-fn convert_custom_linux_target(target: String) -> String {
-    let mut parts: Vec<&str> = target.split('-').collect();
-    let system = parts.get(2);
-    if system == Some(&"linux") {
-        parts[1] = "unknown";
-    };
-    parts.join("-")
-}
diff --git a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/build.rs b/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/build.rs
deleted file mode 100644
index ff7e81f..0000000
--- a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/build.rs
+++ /dev/null
@@ -1,48 +0,0 @@
-// The rustc-cfg listed below are considered public API, but it is *unstable*
-// and outside of the normal semver guarantees:
-//
-// - `crossbeam_no_atomic`
-//      Assume the target does *not* support any atomic operations.
-//      This is usually detected automatically by the build script, but you may
-//      need to enable it manually when building for custom targets or using
-//      non-cargo build systems that don't run the build script.
-//
-// With the exceptions mentioned above, the rustc-cfg emitted by the build
-// script are *not* public API.
-
-#![warn(rust_2018_idioms)]
-
-use std::env;
-
-include!("no_atomic.rs");
-include!("build-common.rs");
-
-fn main() {
-    println!("cargo:rerun-if-changed=no_atomic.rs");
-    println!("cargo:rustc-check-cfg=cfg(crossbeam_no_atomic,crossbeam_sanitize_thread)");
-
-    let target = match env::var("TARGET") {
-        Ok(target) => convert_custom_linux_target(target),
-        Err(e) => {
-            println!(
-                "cargo:warning={}: unable to get TARGET environment variable: {}",
-                env!("CARGO_PKG_NAME"),
-                e
-            );
-            return;
-        }
-    };
-
-    // Note that this is `no_`*, not `has_*`. This allows treating as the latest
-    // stable rustc is used when the build script doesn't run. This is useful
-    // for non-cargo build systems that don't run the build script.
-    if NO_ATOMIC.contains(&&*target) {
-        println!("cargo:rustc-cfg=crossbeam_no_atomic");
-    }
-
-    // `cfg(sanitize = "..")` is not stabilized.
-    let sanitize = env::var("CARGO_CFG_SANITIZE").unwrap_or_default();
-    if sanitize.contains("thread") {
-        println!("cargo:rustc-cfg=crossbeam_sanitize_thread");
-    }
-}
diff --git a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/no_atomic.rs b/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/no_atomic.rs
deleted file mode 100644
index f7e6d2f..0000000
--- a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/no_atomic.rs
+++ /dev/null
@@ -1,9 +0,0 @@
-// This file is @generated by no_atomic.sh.
-// It is not intended for manual editing.
-
-const NO_ATOMIC: &[&str] = &[
-    "bpfeb-unknown-none",
-    "bpfel-unknown-none",
-    "mipsel-sony-psx",
-    "msp430-none-elf",
-];
diff --git a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/src/atomic/atomic_cell.rs b/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/src/atomic/atomic_cell.rs
deleted file mode 100644
index 4747253..0000000
--- a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/src/atomic/atomic_cell.rs
+++ /dev/null
@@ -1,1182 +0,0 @@
-// Necessary for implementing atomic methods for `AtomicUnit`
-#![allow(clippy::unit_arg)]
-
-use crate::primitive::sync::atomic::{self, Ordering};
-use crate::CachePadded;
-use core::cell::UnsafeCell;
-use core::cmp;
-use core::fmt;
-use core::mem::{self, ManuallyDrop, MaybeUninit};
-use core::panic::{RefUnwindSafe, UnwindSafe};
-use core::ptr;
-
-use super::seq_lock::SeqLock;
-
-/// A thread-safe mutable memory location.
-///
-/// This type is equivalent to [`Cell`], except it can also be shared among multiple threads.
-///
-/// Operations on `AtomicCell`s use atomic instructions whenever possible, and synchronize using
-/// global locks otherwise. You can call [`AtomicCell::<T>::is_lock_free()`] to check whether
-/// atomic instructions or locks will be used.
-///
-/// Atomic loads use the [`Acquire`] ordering and atomic stores use the [`Release`] ordering.
-///
-/// [`Cell`]: std::cell::Cell
-/// [`AtomicCell::<T>::is_lock_free()`]: AtomicCell::is_lock_free
-/// [`Acquire`]: std::sync::atomic::Ordering::Acquire
-/// [`Release`]: std::sync::atomic::Ordering::Release
-#[repr(transparent)]
-pub struct AtomicCell<T> {
-    /// The inner value.
-    ///
-    /// If this value can be transmuted into a primitive atomic type, it will be treated as such.
-    /// Otherwise, all potentially concurrent operations on this data will be protected by a global
-    /// lock.
-    ///
-    /// Using MaybeUninit to prevent code outside the cell from observing partially initialized state:
-    /// <https://github.com/crossbeam-rs/crossbeam/issues/833>
-    /// (This rustc bug has been fixed in Rust 1.64.)
-    ///
-    /// Note:
-    /// - we'll never store uninitialized `T` due to our API only using initialized `T`.
-    /// - this `MaybeUninit` does *not* fix <https://github.com/crossbeam-rs/crossbeam/issues/315>.
-    value: UnsafeCell<MaybeUninit<T>>,
-}
-
-unsafe impl<T: Send> Send for AtomicCell<T> {}
-unsafe impl<T: Send> Sync for AtomicCell<T> {}
-
-impl<T> UnwindSafe for AtomicCell<T> {}
-impl<T> RefUnwindSafe for AtomicCell<T> {}
-
-impl<T> AtomicCell<T> {
-    /// Creates a new atomic cell initialized with `val`.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use crossbeam_utils::atomic::AtomicCell;
-    ///
-    /// let a = AtomicCell::new(7);
-    /// ```
-    pub const fn new(val: T) -> AtomicCell<T> {
-        AtomicCell {
-            value: UnsafeCell::new(MaybeUninit::new(val)),
-        }
-    }
-
-    /// Consumes the atomic and returns the contained value.
-    ///
-    /// This is safe because passing `self` by value guarantees that no other threads are
-    /// concurrently accessing the atomic data.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use crossbeam_utils::atomic::AtomicCell;
-    ///
-    /// let a = AtomicCell::new(7);
-    /// let v = a.into_inner();
-    ///
-    /// assert_eq!(v, 7);
-    /// ```
-    pub fn into_inner(self) -> T {
-        let this = ManuallyDrop::new(self);
-        // SAFETY:
-        // - passing `self` by value guarantees that no other threads are concurrently
-        //   accessing the atomic data
-        // - the raw pointer passed in is valid because we got it from an owned value.
-        // - `ManuallyDrop` prevents double dropping `T`
-        unsafe { this.as_ptr().read() }
-    }
-
-    /// Returns `true` if operations on values of this type are lock-free.
-    ///
-    /// If the compiler or the platform doesn't support the necessary atomic instructions,
-    /// `AtomicCell<T>` will use global locks for every potentially concurrent atomic operation.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use crossbeam_utils::atomic::AtomicCell;
-    ///
-    /// // This type is internally represented as `AtomicUsize` so we can just use atomic
-    /// // operations provided by it.
-    /// assert_eq!(AtomicCell::<usize>::is_lock_free(), true);
-    ///
-    /// // A wrapper struct around `isize`.
-    /// struct Foo {
-    ///     bar: isize,
-    /// }
-    /// // `AtomicCell<Foo>` will be internally represented as `AtomicIsize`.
-    /// assert_eq!(AtomicCell::<Foo>::is_lock_free(), true);
-    ///
-    /// // Operations on zero-sized types are always lock-free.
-    /// assert_eq!(AtomicCell::<()>::is_lock_free(), true);
-    ///
-    /// // Very large types cannot be represented as any of the standard atomic types, so atomic
-    /// // operations on them will have to use global locks for synchronization.
-    /// assert_eq!(AtomicCell::<[u8; 1000]>::is_lock_free(), false);
-    /// ```
-    pub const fn is_lock_free() -> bool {
-        atomic_is_lock_free::<T>()
-    }
-
-    /// Stores `val` into the atomic cell.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use crossbeam_utils::atomic::AtomicCell;
-    ///
-    /// let a = AtomicCell::new(7);
-    ///
-    /// assert_eq!(a.load(), 7);
-    /// a.store(8);
-    /// assert_eq!(a.load(), 8);
-    /// ```
-    pub fn store(&self, val: T) {
-        if mem::needs_drop::<T>() {
-            drop(self.swap(val));
-        } else {
-            unsafe {
-                atomic_store(self.as_ptr(), val);
-            }
-        }
-    }
-
-    /// Stores `val` into the atomic cell and returns the previous value.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use crossbeam_utils::atomic::AtomicCell;
-    ///
-    /// let a = AtomicCell::new(7);
-    ///
-    /// assert_eq!(a.load(), 7);
-    /// assert_eq!(a.swap(8), 7);
-    /// assert_eq!(a.load(), 8);
-    /// ```
-    pub fn swap(&self, val: T) -> T {
-        unsafe { atomic_swap(self.as_ptr(), val) }
-    }
-
-    /// Returns a raw pointer to the underlying data in this atomic cell.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use crossbeam_utils::atomic::AtomicCell;
-    ///
-    /// let a = AtomicCell::new(5);
-    ///
-    /// let ptr = a.as_ptr();
-    /// ```
-    #[inline]
-    pub fn as_ptr(&self) -> *mut T {
-        self.value.get().cast::<T>()
-    }
-}
-
-impl<T: Default> AtomicCell<T> {
-    /// Takes the value of the atomic cell, leaving `Default::default()` in its place.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use crossbeam_utils::atomic::AtomicCell;
-    ///
-    /// let a = AtomicCell::new(5);
-    /// let five = a.take();
-    ///
-    /// assert_eq!(five, 5);
-    /// assert_eq!(a.into_inner(), 0);
-    /// ```
-    pub fn take(&self) -> T {
-        self.swap(Default::default())
-    }
-}
-
-impl<T: Copy> AtomicCell<T> {
-    /// Loads a value from the atomic cell.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use crossbeam_utils::atomic::AtomicCell;
-    ///
-    /// let a = AtomicCell::new(7);
-    ///
-    /// assert_eq!(a.load(), 7);
-    /// ```
-    pub fn load(&self) -> T {
-        unsafe { atomic_load(self.as_ptr()) }
-    }
-}
-
-impl<T: Copy + Eq> AtomicCell<T> {
-    /// If the current value equals `current`, stores `new` into the atomic cell.
-    ///
-    /// The return value is always the previous value. If it is equal to `current`, then the value
-    /// was updated.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # #![allow(deprecated)]
-    /// use crossbeam_utils::atomic::AtomicCell;
-    ///
-    /// let a = AtomicCell::new(1);
-    ///
-    /// assert_eq!(a.compare_and_swap(2, 3), 1);
-    /// assert_eq!(a.load(), 1);
-    ///
-    /// assert_eq!(a.compare_and_swap(1, 2), 1);
-    /// assert_eq!(a.load(), 2);
-    /// ```
-    // TODO: remove in the next major version.
-    #[deprecated(note = "Use `compare_exchange` instead")]
-    pub fn compare_and_swap(&self, current: T, new: T) -> T {
-        match self.compare_exchange(current, new) {
-            Ok(v) => v,
-            Err(v) => v,
-        }
-    }
-
-    /// If the current value equals `current`, stores `new` into the atomic cell.
-    ///
-    /// The return value is a result indicating whether the new value was written and containing
-    /// the previous value. On success this value is guaranteed to be equal to `current`.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use crossbeam_utils::atomic::AtomicCell;
-    ///
-    /// let a = AtomicCell::new(1);
-    ///
-    /// assert_eq!(a.compare_exchange(2, 3), Err(1));
-    /// assert_eq!(a.load(), 1);
-    ///
-    /// assert_eq!(a.compare_exchange(1, 2), Ok(1));
-    /// assert_eq!(a.load(), 2);
-    /// ```
-    pub fn compare_exchange(&self, current: T, new: T) -> Result<T, T> {
-        unsafe { atomic_compare_exchange_weak(self.as_ptr(), current, new) }
-    }
-
-    /// Fetches the value, and applies a function to it that returns an optional
-    /// new value. Returns a `Result` of `Ok(previous_value)` if the function returned `Some(_)`, else
-    /// `Err(previous_value)`.
-    ///
-    /// Note: This may call the function multiple times if the value has been changed from other threads in
-    /// the meantime, as long as the function returns `Some(_)`, but the function will have been applied
-    /// only once to the stored value.
-    ///
-    /// # Examples
-    ///
-    /// ```rust
-    /// use crossbeam_utils::atomic::AtomicCell;
-    ///
-    /// let a = AtomicCell::new(7);
-    /// assert_eq!(a.fetch_update(|_| None), Err(7));
-    /// assert_eq!(a.fetch_update(|a| Some(a + 1)), Ok(7));
-    /// assert_eq!(a.fetch_update(|a| Some(a + 1)), Ok(8));
-    /// assert_eq!(a.load(), 9);
-    /// ```
-    #[inline]
-    pub fn fetch_update<F>(&self, mut f: F) -> Result<T, T>
-    where
-        F: FnMut(T) -> Option<T>,
-    {
-        let mut prev = self.load();
-        while let Some(next) = f(prev) {
-            match self.compare_exchange(prev, next) {
-                x @ Ok(_) => return x,
-                Err(next_prev) => prev = next_prev,
-            }
-        }
-        Err(prev)
-    }
-}
-
-// `MaybeUninit` prevents `T` from being dropped, so we need to implement `Drop`
-// for `AtomicCell` to avoid leaks of non-`Copy` types.
-impl<T> Drop for AtomicCell<T> {
-    fn drop(&mut self) {
-        if mem::needs_drop::<T>() {
-            // SAFETY:
-            // - the mutable reference guarantees that no other threads are concurrently accessing the atomic data
-            // - the raw pointer passed in is valid because we got it from a reference
-            // - `MaybeUninit` prevents double dropping `T`
-            unsafe {
-                self.as_ptr().drop_in_place();
-            }
-        }
-    }
-}
-
-macro_rules! atomic {
-    // If values of type `$t` can be transmuted into values of the primitive atomic type `$atomic`,
-    // declares variable `$a` of type `$atomic` and executes `$atomic_op`, breaking out of the loop.
-    (@check, $t:ty, $atomic:ty, $a:ident, $atomic_op:expr) => {
-        if can_transmute::<$t, $atomic>() {
-            let $a: &$atomic;
-            break $atomic_op;
-        }
-    };
-
-    // If values of type `$t` can be transmuted into values of a primitive atomic type, declares
-    // variable `$a` of that type and executes `$atomic_op`. Otherwise, just executes
-    // `$fallback_op`.
-    ($t:ty, $a:ident, $atomic_op:expr, $fallback_op:expr) => {
-        loop {
-            atomic!(@check, $t, AtomicUnit, $a, $atomic_op);
-
-            atomic!(@check, $t, atomic::AtomicU8, $a, $atomic_op);
-            atomic!(@check, $t, atomic::AtomicU16, $a, $atomic_op);
-            atomic!(@check, $t, atomic::AtomicU32, $a, $atomic_op);
-            #[cfg(target_has_atomic = "64")]
-            atomic!(@check, $t, atomic::AtomicU64, $a, $atomic_op);
-            // TODO: AtomicU128 is unstable
-            // atomic!(@check, $t, atomic::AtomicU128, $a, $atomic_op);
-
-            break $fallback_op;
-        }
-    };
-}
-
-macro_rules! impl_arithmetic {
-    ($t:ty, fallback, $example:tt) => {
-        impl AtomicCell<$t> {
-            /// Increments the current value by `val` and returns the previous value.
-            ///
-            /// The addition wraps on overflow.
-            ///
-            /// # Examples
-            ///
-            /// ```
-            /// use crossbeam_utils::atomic::AtomicCell;
-            ///
-            #[doc = $example]
-            ///
-            /// assert_eq!(a.fetch_add(3), 7);
-            /// assert_eq!(a.load(), 10);
-            /// ```
-            #[inline]
-            pub fn fetch_add(&self, val: $t) -> $t {
-                let _guard = lock(self.as_ptr() as usize).write();
-                let value = unsafe { &mut *(self.as_ptr()) };
-                let old = *value;
-                *value = value.wrapping_add(val);
-                old
-            }
-
-            /// Decrements the current value by `val` and returns the previous value.
-            ///
-            /// The subtraction wraps on overflow.
-            ///
-            /// # Examples
-            ///
-            /// ```
-            /// use crossbeam_utils::atomic::AtomicCell;
-            ///
-            #[doc = $example]
-            ///
-            /// assert_eq!(a.fetch_sub(3), 7);
-            /// assert_eq!(a.load(), 4);
-            /// ```
-            #[inline]
-            pub fn fetch_sub(&self, val: $t) -> $t {
-                let _guard = lock(self.as_ptr() as usize).write();
-                let value = unsafe { &mut *(self.as_ptr()) };
-                let old = *value;
-                *value = value.wrapping_sub(val);
-                old
-            }
-
-            /// Applies bitwise "and" to the current value and returns the previous value.
-            ///
-            /// # Examples
-            ///
-            /// ```
-            /// use crossbeam_utils::atomic::AtomicCell;
-            ///
-            #[doc = $example]
-            ///
-            /// assert_eq!(a.fetch_and(3), 7);
-            /// assert_eq!(a.load(), 3);
-            /// ```
-            #[inline]
-            pub fn fetch_and(&self, val: $t) -> $t {
-                let _guard = lock(self.as_ptr() as usize).write();
-                let value = unsafe { &mut *(self.as_ptr()) };
-                let old = *value;
-                *value &= val;
-                old
-            }
-
-            /// Applies bitwise "nand" to the current value and returns the previous value.
-            ///
-            /// # Examples
-            ///
-            /// ```
-            /// use crossbeam_utils::atomic::AtomicCell;
-            ///
-            #[doc = $example]
-            ///
-            /// assert_eq!(a.fetch_nand(3), 7);
-            /// assert_eq!(a.load(), !(7 & 3));
-            /// ```
-            #[inline]
-            pub fn fetch_nand(&self, val: $t) -> $t {
-                let _guard = lock(self.as_ptr() as usize).write();
-                let value = unsafe { &mut *(self.as_ptr()) };
-                let old = *value;
-                *value = !(old & val);
-                old
-            }
-
-            /// Applies bitwise "or" to the current value and returns the previous value.
-            ///
-            /// # Examples
-            ///
-            /// ```
-            /// use crossbeam_utils::atomic::AtomicCell;
-            ///
-            #[doc = $example]
-            ///
-            /// assert_eq!(a.fetch_or(16), 7);
-            /// assert_eq!(a.load(), 23);
-            /// ```
-            #[inline]
-            pub fn fetch_or(&self, val: $t) -> $t {
-                let _guard = lock(self.as_ptr() as usize).write();
-                let value = unsafe { &mut *(self.as_ptr()) };
-                let old = *value;
-                *value |= val;
-                old
-            }
-
-            /// Applies bitwise "xor" to the current value and returns the previous value.
-            ///
-            /// # Examples
-            ///
-            /// ```
-            /// use crossbeam_utils::atomic::AtomicCell;
-            ///
-            #[doc = $example]
-            ///
-            /// assert_eq!(a.fetch_xor(2), 7);
-            /// assert_eq!(a.load(), 5);
-            /// ```
-            #[inline]
-            pub fn fetch_xor(&self, val: $t) -> $t {
-                let _guard = lock(self.as_ptr() as usize).write();
-                let value = unsafe { &mut *(self.as_ptr()) };
-                let old = *value;
-                *value ^= val;
-                old
-            }
-
-            /// Compares and sets the maximum of the current value and `val`,
-            /// and returns the previous value.
-            ///
-            /// # Examples
-            ///
-            /// ```
-            /// use crossbeam_utils::atomic::AtomicCell;
-            ///
-            #[doc = $example]
-            ///
-            /// assert_eq!(a.fetch_max(2), 7);
-            /// assert_eq!(a.load(), 7);
-            /// ```
-            #[inline]
-            pub fn fetch_max(&self, val: $t) -> $t {
-                let _guard = lock(self.as_ptr() as usize).write();
-                let value = unsafe { &mut *(self.as_ptr()) };
-                let old = *value;
-                *value = cmp::max(old, val);
-                old
-            }
-
-            /// Compares and sets the minimum of the current value and `val`,
-            /// and returns the previous value.
-            ///
-            /// # Examples
-            ///
-            /// ```
-            /// use crossbeam_utils::atomic::AtomicCell;
-            ///
-            #[doc = $example]
-            ///
-            /// assert_eq!(a.fetch_min(2), 7);
-            /// assert_eq!(a.load(), 2);
-            /// ```
-            #[inline]
-            pub fn fetch_min(&self, val: $t) -> $t {
-                let _guard = lock(self.as_ptr() as usize).write();
-                let value = unsafe { &mut *(self.as_ptr()) };
-                let old = *value;
-                *value = cmp::min(old, val);
-                old
-            }
-        }
-    };
-    ($t:ty, $atomic:ident, $example:tt) => {
-        impl AtomicCell<$t> {
-            /// Increments the current value by `val` and returns the previous value.
-            ///
-            /// The addition wraps on overflow.
-            ///
-            /// # Examples
-            ///
-            /// ```
-            /// use crossbeam_utils::atomic::AtomicCell;
-            ///
-            #[doc = $example]
-            ///
-            /// assert_eq!(a.fetch_add(3), 7);
-            /// assert_eq!(a.load(), 10);
-            /// ```
-            #[inline]
-            pub fn fetch_add(&self, val: $t) -> $t {
-                atomic! {
-                    $t, _a,
-                    {
-                        let a = unsafe { &*(self.as_ptr() as *const atomic::$atomic) };
-                        a.fetch_add(val, Ordering::AcqRel)
-                    },
-                    {
-                        let _guard = lock(self.as_ptr() as usize).write();
-                        let value = unsafe { &mut *(self.as_ptr()) };
-                        let old = *value;
-                        *value = value.wrapping_add(val);
-                        old
-                    }
-                }
-            }
-
-            /// Decrements the current value by `val` and returns the previous value.
-            ///
-            /// The subtraction wraps on overflow.
-            ///
-            /// # Examples
-            ///
-            /// ```
-            /// use crossbeam_utils::atomic::AtomicCell;
-            ///
-            #[doc = $example]
-            ///
-            /// assert_eq!(a.fetch_sub(3), 7);
-            /// assert_eq!(a.load(), 4);
-            /// ```
-            #[inline]
-            pub fn fetch_sub(&self, val: $t) -> $t {
-                atomic! {
-                    $t, _a,
-                    {
-                        let a = unsafe { &*(self.as_ptr() as *const atomic::$atomic) };
-                        a.fetch_sub(val, Ordering::AcqRel)
-                    },
-                    {
-                        let _guard = lock(self.as_ptr() as usize).write();
-                        let value = unsafe { &mut *(self.as_ptr()) };
-                        let old = *value;
-                        *value = value.wrapping_sub(val);
-                        old
-                    }
-                }
-            }
-
-            /// Applies bitwise "and" to the current value and returns the previous value.
-            ///
-            /// # Examples
-            ///
-            /// ```
-            /// use crossbeam_utils::atomic::AtomicCell;
-            ///
-            #[doc = $example]
-            ///
-            /// assert_eq!(a.fetch_and(3), 7);
-            /// assert_eq!(a.load(), 3);
-            /// ```
-            #[inline]
-            pub fn fetch_and(&self, val: $t) -> $t {
-                atomic! {
-                    $t, _a,
-                    {
-                        let a = unsafe { &*(self.as_ptr() as *const atomic::$atomic) };
-                        a.fetch_and(val, Ordering::AcqRel)
-                    },
-                    {
-                        let _guard = lock(self.as_ptr() as usize).write();
-                        let value = unsafe { &mut *(self.as_ptr()) };
-                        let old = *value;
-                        *value &= val;
-                        old
-                    }
-                }
-            }
-
-            /// Applies bitwise "nand" to the current value and returns the previous value.
-            ///
-            /// # Examples
-            ///
-            /// ```
-            /// use crossbeam_utils::atomic::AtomicCell;
-            ///
-            #[doc = $example]
-            ///
-            /// assert_eq!(a.fetch_nand(3), 7);
-            /// assert_eq!(a.load(), !(7 & 3));
-            /// ```
-            #[inline]
-            pub fn fetch_nand(&self, val: $t) -> $t {
-                atomic! {
-                    $t, _a,
-                    {
-                        let a = unsafe { &*(self.as_ptr() as *const atomic::$atomic) };
-                        a.fetch_nand(val, Ordering::AcqRel)
-                    },
-                    {
-                        let _guard = lock(self.as_ptr() as usize).write();
-                        let value = unsafe { &mut *(self.as_ptr()) };
-                        let old = *value;
-                        *value = !(old & val);
-                        old
-                    }
-                }
-            }
-
-            /// Applies bitwise "or" to the current value and returns the previous value.
-            ///
-            /// # Examples
-            ///
-            /// ```
-            /// use crossbeam_utils::atomic::AtomicCell;
-            ///
-            #[doc = $example]
-            ///
-            /// assert_eq!(a.fetch_or(16), 7);
-            /// assert_eq!(a.load(), 23);
-            /// ```
-            #[inline]
-            pub fn fetch_or(&self, val: $t) -> $t {
-                atomic! {
-                    $t, _a,
-                    {
-                        let a = unsafe { &*(self.as_ptr() as *const atomic::$atomic) };
-                        a.fetch_or(val, Ordering::AcqRel)
-                    },
-                    {
-                        let _guard = lock(self.as_ptr() as usize).write();
-                        let value = unsafe { &mut *(self.as_ptr()) };
-                        let old = *value;
-                        *value |= val;
-                        old
-                    }
-                }
-            }
-
-            /// Applies bitwise "xor" to the current value and returns the previous value.
-            ///
-            /// # Examples
-            ///
-            /// ```
-            /// use crossbeam_utils::atomic::AtomicCell;
-            ///
-            #[doc = $example]
-            ///
-            /// assert_eq!(a.fetch_xor(2), 7);
-            /// assert_eq!(a.load(), 5);
-            /// ```
-            #[inline]
-            pub fn fetch_xor(&self, val: $t) -> $t {
-                atomic! {
-                    $t, _a,
-                    {
-                        let a = unsafe { &*(self.as_ptr() as *const atomic::$atomic) };
-                        a.fetch_xor(val, Ordering::AcqRel)
-                    },
-                    {
-                        let _guard = lock(self.as_ptr() as usize).write();
-                        let value = unsafe { &mut *(self.as_ptr()) };
-                        let old = *value;
-                        *value ^= val;
-                        old
-                    }
-                }
-            }
-
-            /// Compares and sets the maximum of the current value and `val`,
-            /// and returns the previous value.
-            ///
-            /// # Examples
-            ///
-            /// ```
-            /// use crossbeam_utils::atomic::AtomicCell;
-            ///
-            #[doc = $example]
-            ///
-            /// assert_eq!(a.fetch_max(9), 7);
-            /// assert_eq!(a.load(), 9);
-            /// ```
-            #[inline]
-            pub fn fetch_max(&self, val: $t) -> $t {
-                atomic! {
-                    $t, _a,
-                    {
-                        let a = unsafe { &*(self.as_ptr() as *const atomic::$atomic) };
-                        a.fetch_max(val, Ordering::AcqRel)
-                    },
-                    {
-                        let _guard = lock(self.as_ptr() as usize).write();
-                        let value = unsafe { &mut *(self.as_ptr()) };
-                        let old = *value;
-                        *value = cmp::max(old, val);
-                        old
-                    }
-                }
-            }
-
-            /// Compares and sets the minimum of the current value and `val`,
-            /// and returns the previous value.
-            ///
-            /// # Examples
-            ///
-            /// ```
-            /// use crossbeam_utils::atomic::AtomicCell;
-            ///
-            #[doc = $example]
-            ///
-            /// assert_eq!(a.fetch_min(2), 7);
-            /// assert_eq!(a.load(), 2);
-            /// ```
-            #[inline]
-            pub fn fetch_min(&self, val: $t) -> $t {
-                atomic! {
-                    $t, _a,
-                    {
-                        let a = unsafe { &*(self.as_ptr() as *const atomic::$atomic) };
-                        a.fetch_min(val, Ordering::AcqRel)
-                    },
-                    {
-                        let _guard = lock(self.as_ptr() as usize).write();
-                        let value = unsafe { &mut *(self.as_ptr()) };
-                        let old = *value;
-                        *value = cmp::min(old, val);
-                        old
-                    }
-                }
-            }
-        }
-    };
-}
-
-impl_arithmetic!(u8, AtomicU8, "let a = AtomicCell::new(7u8);");
-impl_arithmetic!(i8, AtomicI8, "let a = AtomicCell::new(7i8);");
-impl_arithmetic!(u16, AtomicU16, "let a = AtomicCell::new(7u16);");
-impl_arithmetic!(i16, AtomicI16, "let a = AtomicCell::new(7i16);");
-
-impl_arithmetic!(u32, AtomicU32, "let a = AtomicCell::new(7u32);");
-impl_arithmetic!(i32, AtomicI32, "let a = AtomicCell::new(7i32);");
-
-#[cfg(target_has_atomic = "64")]
-impl_arithmetic!(u64, AtomicU64, "let a = AtomicCell::new(7u64);");
-#[cfg(target_has_atomic = "64")]
-impl_arithmetic!(i64, AtomicI64, "let a = AtomicCell::new(7i64);");
-#[cfg(not(target_has_atomic = "64"))]
-impl_arithmetic!(u64, fallback, "let a = AtomicCell::new(7u64);");
-#[cfg(not(target_has_atomic = "64"))]
-impl_arithmetic!(i64, fallback, "let a = AtomicCell::new(7i64);");
-
-// TODO: AtomicU128 is unstable
-// impl_arithmetic!(u128, AtomicU128, "let a = AtomicCell::new(7u128);");
-// impl_arithmetic!(i128, AtomicI128, "let a = AtomicCell::new(7i128);");
-impl_arithmetic!(u128, fallback, "let a = AtomicCell::new(7u128);");
-impl_arithmetic!(i128, fallback, "let a = AtomicCell::new(7i128);");
-
-impl_arithmetic!(usize, AtomicUsize, "let a = AtomicCell::new(7usize);");
-impl_arithmetic!(isize, AtomicIsize, "let a = AtomicCell::new(7isize);");
-
-impl AtomicCell<bool> {
-    /// Applies logical "and" to the current value and returns the previous value.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use crossbeam_utils::atomic::AtomicCell;
-    ///
-    /// let a = AtomicCell::new(true);
-    ///
-    /// assert_eq!(a.fetch_and(true), true);
-    /// assert_eq!(a.load(), true);
-    ///
-    /// assert_eq!(a.fetch_and(false), true);
-    /// assert_eq!(a.load(), false);
-    /// ```
-    #[inline]
-    pub fn fetch_and(&self, val: bool) -> bool {
-        atomic! {
-            bool, _a,
-            {
-                let a = unsafe { &*(self.as_ptr() as *const atomic::AtomicBool) };
-                a.fetch_and(val, Ordering::AcqRel)
-            },
-            {
-                let _guard = lock(self.as_ptr() as usize).write();
-                let value = unsafe { &mut *(self.as_ptr()) };
-                let old = *value;
-                *value &= val;
-                old
-            }
-        }
-    }
-
-    /// Applies logical "nand" to the current value and returns the previous value.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use crossbeam_utils::atomic::AtomicCell;
-    ///
-    /// let a = AtomicCell::new(true);
-    ///
-    /// assert_eq!(a.fetch_nand(false), true);
-    /// assert_eq!(a.load(), true);
-    ///
-    /// assert_eq!(a.fetch_nand(true), true);
-    /// assert_eq!(a.load(), false);
-    ///
-    /// assert_eq!(a.fetch_nand(false), false);
-    /// assert_eq!(a.load(), true);
-    /// ```
-    #[inline]
-    pub fn fetch_nand(&self, val: bool) -> bool {
-        atomic! {
-            bool, _a,
-            {
-                let a = unsafe { &*(self.as_ptr() as *const atomic::AtomicBool) };
-                a.fetch_nand(val, Ordering::AcqRel)
-            },
-            {
-                let _guard = lock(self.as_ptr() as usize).write();
-                let value = unsafe { &mut *(self.as_ptr()) };
-                let old = *value;
-                *value = !(old & val);
-                old
-            }
-        }
-    }
-
-    /// Applies logical "or" to the current value and returns the previous value.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use crossbeam_utils::atomic::AtomicCell;
-    ///
-    /// let a = AtomicCell::new(false);
-    ///
-    /// assert_eq!(a.fetch_or(false), false);
-    /// assert_eq!(a.load(), false);
-    ///
-    /// assert_eq!(a.fetch_or(true), false);
-    /// assert_eq!(a.load(), true);
-    /// ```
-    #[inline]
-    pub fn fetch_or(&self, val: bool) -> bool {
-        atomic! {
-            bool, _a,
-            {
-                let a = unsafe { &*(self.as_ptr() as *const atomic::AtomicBool) };
-                a.fetch_or(val, Ordering::AcqRel)
-            },
-            {
-                let _guard = lock(self.as_ptr() as usize).write();
-                let value = unsafe { &mut *(self.as_ptr()) };
-                let old = *value;
-                *value |= val;
-                old
-            }
-        }
-    }
-
-    /// Applies logical "xor" to the current value and returns the previous value.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use crossbeam_utils::atomic::AtomicCell;
-    ///
-    /// let a = AtomicCell::new(true);
-    ///
-    /// assert_eq!(a.fetch_xor(false), true);
-    /// assert_eq!(a.load(), true);
-    ///
-    /// assert_eq!(a.fetch_xor(true), true);
-    /// assert_eq!(a.load(), false);
-    /// ```
-    #[inline]
-    pub fn fetch_xor(&self, val: bool) -> bool {
-        atomic! {
-            bool, _a,
-            {
-                let a = unsafe { &*(self.as_ptr() as *const atomic::AtomicBool) };
-                a.fetch_xor(val, Ordering::AcqRel)
-            },
-            {
-                let _guard = lock(self.as_ptr() as usize).write();
-                let value = unsafe { &mut *(self.as_ptr()) };
-                let old = *value;
-                *value ^= val;
-                old
-            }
-        }
-    }
-}
-
-impl<T: Default> Default for AtomicCell<T> {
-    fn default() -> AtomicCell<T> {
-        AtomicCell::new(T::default())
-    }
-}
-
-impl<T> From<T> for AtomicCell<T> {
-    #[inline]
-    fn from(val: T) -> AtomicCell<T> {
-        AtomicCell::new(val)
-    }
-}
-
-impl<T: Copy + fmt::Debug> fmt::Debug for AtomicCell<T> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct("AtomicCell")
-            .field("value", &self.load())
-            .finish()
-    }
-}
-
-/// Returns `true` if values of type `A` can be transmuted into values of type `B`.
-const fn can_transmute<A, B>() -> bool {
-    // Sizes must be equal, but alignment of `A` must be greater or equal than that of `B`.
-    (mem::size_of::<A>() == mem::size_of::<B>()) & (mem::align_of::<A>() >= mem::align_of::<B>())
-}
-
-/// Returns a reference to the global lock associated with the `AtomicCell` at address `addr`.
-///
-/// This function is used to protect atomic data which doesn't fit into any of the primitive atomic
-/// types in `std::sync::atomic`. Operations on such atomics must therefore use a global lock.
-///
-/// However, there is not only one global lock but an array of many locks, and one of them is
-/// picked based on the given address. Having many locks reduces contention and improves
-/// scalability.
-#[inline]
-#[must_use]
-fn lock(addr: usize) -> &'static SeqLock {
-    // The number of locks is a prime number because we want to make sure `addr % LEN` gets
-    // dispersed across all locks.
-    //
-    // Note that addresses are always aligned to some power of 2, depending on type `T` in
-    // `AtomicCell<T>`. If `LEN` was an even number, then `addr % LEN` would be an even number,
-    // too, which means only half of the locks would get utilized!
-    //
-    // It is also possible for addresses to accidentally get aligned to a number that is not a
-    // power of 2. Consider this example:
-    //
-    // ```
-    // #[repr(C)]
-    // struct Foo {
-    //     a: AtomicCell<u8>,
-    //     b: u8,
-    //     c: u8,
-    // }
-    // ```
-    //
-    // Now, if we have a slice of type `&[Foo]`, it is possible that field `a` in all items gets
-    // stored at addresses that are multiples of 3. It'd be too bad if `LEN` was divisible by 3.
-    // In order to protect from such cases, we simply choose a large prime number for `LEN`.
-    const LEN: usize = 67;
-    const L: CachePadded<SeqLock> = CachePadded::new(SeqLock::new());
-    static LOCKS: [CachePadded<SeqLock>; LEN] = [L; LEN];
-
-    // If the modulus is a constant number, the compiler will use crazy math to transform this into
-    // a sequence of cheap arithmetic operations rather than using the slow modulo instruction.
-    &LOCKS[addr % LEN]
-}
-
-/// An atomic `()`.
-///
-/// All operations are noops.
-struct AtomicUnit;
-
-impl AtomicUnit {
-    #[inline]
-    fn load(&self, _order: Ordering) {}
-
-    #[inline]
-    fn store(&self, _val: (), _order: Ordering) {}
-
-    #[inline]
-    fn swap(&self, _val: (), _order: Ordering) {}
-
-    #[inline]
-    fn compare_exchange_weak(
-        &self,
-        _current: (),
-        _new: (),
-        _success: Ordering,
-        _failure: Ordering,
-    ) -> Result<(), ()> {
-        Ok(())
-    }
-}
-
-/// Returns `true` if operations on `AtomicCell<T>` are lock-free.
-const fn atomic_is_lock_free<T>() -> bool {
-    atomic! { T, _a, true, false }
-}
-
-/// Atomically reads data from `src`.
-///
-/// This operation uses the `Acquire` ordering. If possible, an atomic instructions is used, and a
-/// global lock otherwise.
-unsafe fn atomic_load<T>(src: *mut T) -> T
-where
-    T: Copy,
-{
-    atomic! {
-        T, a,
-        {
-            a = &*(src as *const _ as *const _);
-            mem::transmute_copy(&a.load(Ordering::Acquire))
-        },
-        {
-            let lock = lock(src as usize);
-
-            // Try doing an optimistic read first.
-            if let Some(stamp) = lock.optimistic_read() {
-                // We need a volatile read here because other threads might concurrently modify the
-                // value. In theory, data races are *always* UB, even if we use volatile reads and
-                // discard the data when a data race is detected. The proper solution would be to
-                // do atomic reads and atomic writes, but we can't atomically read and write all
-                // kinds of data since `AtomicU8` is not available on stable Rust yet.
-                // Load as `MaybeUninit` because we may load a value that is not valid as `T`.
-                let val = ptr::read_volatile(src.cast::<MaybeUninit<T>>());
-
-                if lock.validate_read(stamp) {
-                    return val.assume_init();
-                }
-            }
-
-            // Grab a regular write lock so that writers don't starve this load.
-            let guard = lock.write();
-            let val = ptr::read(src);
-            // The value hasn't been changed. Drop the guard without incrementing the stamp.
-            guard.abort();
-            val
-        }
-    }
-}
-
-/// Atomically writes `val` to `dst`.
-///
-/// This operation uses the `Release` ordering. If possible, an atomic instructions is used, and a
-/// global lock otherwise.
-unsafe fn atomic_store<T>(dst: *mut T, val: T) {
-    atomic! {
-        T, a,
-        {
-            a = &*(dst as *const _ as *const _);
-            a.store(mem::transmute_copy(&val), Ordering::Release);
-            mem::forget(val);
-        },
-        {
-            let _guard = lock(dst as usize).write();
-            ptr::write(dst, val);
-        }
-    }
-}
-
-/// Atomically swaps data at `dst` with `val`.
-///
-/// This operation uses the `AcqRel` ordering. If possible, an atomic instructions is used, and a
-/// global lock otherwise.
-unsafe fn atomic_swap<T>(dst: *mut T, val: T) -> T {
-    atomic! {
-        T, a,
-        {
-            a = &*(dst as *const _ as *const _);
-            let res = mem::transmute_copy(&a.swap(mem::transmute_copy(&val), Ordering::AcqRel));
-            mem::forget(val);
-            res
-        },
-        {
-            let _guard = lock(dst as usize).write();
-            ptr::replace(dst, val)
-        }
-    }
-}
-
-/// Atomically compares data at `dst` to `current` and, if equal byte-for-byte, exchanges data at
-/// `dst` with `new`.
-///
-/// Returns the old value on success, or the current value at `dst` on failure.
-///
-/// This operation uses the `AcqRel` ordering. If possible, an atomic instructions is used, and a
-/// global lock otherwise.
-#[allow(clippy::let_unit_value)]
-unsafe fn atomic_compare_exchange_weak<T>(dst: *mut T, mut current: T, new: T) -> Result<T, T>
-where
-    T: Copy + Eq,
-{
-    atomic! {
-        T, a,
-        {
-            a = &*(dst as *const _ as *const _);
-            let mut current_raw = mem::transmute_copy(&current);
-            let new_raw = mem::transmute_copy(&new);
-
-            loop {
-                match a.compare_exchange_weak(
-                    current_raw,
-                    new_raw,
-                    Ordering::AcqRel,
-                    Ordering::Acquire,
-                ) {
-                    Ok(_) => break Ok(current),
-                    Err(previous_raw) => {
-                        let previous = mem::transmute_copy(&previous_raw);
-
-                        if !T::eq(&previous, &current) {
-                            break Err(previous);
-                        }
-
-                        // The compare-exchange operation has failed and didn't store `new`. The
-                        // failure is either spurious, or `previous` was semantically equal to
-                        // `current` but not byte-equal. Let's retry with `previous` as the new
-                        // `current`.
-                        current = previous;
-                        current_raw = previous_raw;
-                    }
-                }
-            }
-        },
-        {
-            let guard = lock(dst as usize).write();
-
-            if T::eq(&*dst, &current) {
-                Ok(ptr::replace(dst, new))
-            } else {
-                let val = ptr::read(dst);
-                // The value hasn't been changed. Drop the guard without incrementing the stamp.
-                guard.abort();
-                Err(val)
-            }
-        }
-    }
-}
diff --git a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/src/atomic/consume.rs b/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/src/atomic/consume.rs
deleted file mode 100644
index ff8e316b..0000000
--- a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/src/atomic/consume.rs
+++ /dev/null
@@ -1,111 +0,0 @@
-#[cfg(not(crossbeam_no_atomic))]
-use core::sync::atomic::Ordering;
-
-/// Trait which allows reading from primitive atomic types with "consume" ordering.
-pub trait AtomicConsume {
-    /// Type returned by `load_consume`.
-    type Val;
-
-    /// Loads a value from the atomic using a "consume" memory ordering.
-    ///
-    /// This is similar to the "acquire" ordering, except that an ordering is
-    /// only guaranteed with operations that "depend on" the result of the load.
-    /// However consume loads are usually much faster than acquire loads on
-    /// architectures with a weak memory model since they don't require memory
-    /// fence instructions.
-    ///
-    /// The exact definition of "depend on" is a bit vague, but it works as you
-    /// would expect in practice since a lot of software, especially the Linux
-    /// kernel, rely on this behavior.
-    ///
-    /// This is currently only implemented on ARM and AArch64, where a fence
-    /// can be avoided. On other architectures this will fall back to a simple
-    /// `load(Ordering::Acquire)`.
-    fn load_consume(&self) -> Self::Val;
-}
-
-#[cfg(not(crossbeam_no_atomic))]
-// Miri and Loom don't support "consume" ordering and ThreadSanitizer doesn't treat
-// load(Relaxed) + compiler_fence(Acquire) as "consume" load.
-// LLVM generates machine code equivalent to fence(Acquire) in compiler_fence(Acquire)
-// on PowerPC, MIPS, etc. (https://godbolt.org/z/hffvjvW7h), so for now the fence
-// can be actually avoided here only on ARM and AArch64. See also
-// https://github.com/rust-lang/rust/issues/62256.
-#[cfg(all(
-    any(target_arch = "arm", target_arch = "aarch64"),
-    not(any(miri, crossbeam_loom, crossbeam_sanitize_thread)),
-))]
-macro_rules! impl_consume {
-    () => {
-        #[inline]
-        fn load_consume(&self) -> Self::Val {
-            use crate::primitive::sync::atomic::compiler_fence;
-            let result = self.load(Ordering::Relaxed);
-            compiler_fence(Ordering::Acquire);
-            result
-        }
-    };
-}
-
-#[cfg(not(crossbeam_no_atomic))]
-#[cfg(not(all(
-    any(target_arch = "arm", target_arch = "aarch64"),
-    not(any(miri, crossbeam_loom, crossbeam_sanitize_thread)),
-)))]
-macro_rules! impl_consume {
-    () => {
-        #[inline]
-        fn load_consume(&self) -> Self::Val {
-            self.load(Ordering::Acquire)
-        }
-    };
-}
-
-macro_rules! impl_atomic {
-    ($atomic:ident, $val:ty) => {
-        #[cfg(not(crossbeam_no_atomic))]
-        impl AtomicConsume for core::sync::atomic::$atomic {
-            type Val = $val;
-            impl_consume!();
-        }
-        #[cfg(crossbeam_loom)]
-        impl AtomicConsume for loom::sync::atomic::$atomic {
-            type Val = $val;
-            impl_consume!();
-        }
-    };
-}
-
-impl_atomic!(AtomicBool, bool);
-impl_atomic!(AtomicUsize, usize);
-impl_atomic!(AtomicIsize, isize);
-impl_atomic!(AtomicU8, u8);
-impl_atomic!(AtomicI8, i8);
-impl_atomic!(AtomicU16, u16);
-impl_atomic!(AtomicI16, i16);
-#[cfg(any(target_has_atomic = "32", not(target_pointer_width = "16")))]
-impl_atomic!(AtomicU32, u32);
-#[cfg(any(target_has_atomic = "32", not(target_pointer_width = "16")))]
-impl_atomic!(AtomicI32, i32);
-#[cfg(any(
-    target_has_atomic = "64",
-    not(any(target_pointer_width = "16", target_pointer_width = "32")),
-))]
-impl_atomic!(AtomicU64, u64);
-#[cfg(any(
-    target_has_atomic = "64",
-    not(any(target_pointer_width = "16", target_pointer_width = "32")),
-))]
-impl_atomic!(AtomicI64, i64);
-
-#[cfg(not(crossbeam_no_atomic))]
-impl<T> AtomicConsume for core::sync::atomic::AtomicPtr<T> {
-    type Val = *mut T;
-    impl_consume!();
-}
-
-#[cfg(crossbeam_loom)]
-impl<T> AtomicConsume for loom::sync::atomic::AtomicPtr<T> {
-    type Val = *mut T;
-    impl_consume!();
-}
diff --git a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/src/atomic/mod.rs b/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/src/atomic/mod.rs
deleted file mode 100644
index 8662ded5..0000000
--- a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/src/atomic/mod.rs
+++ /dev/null
@@ -1,32 +0,0 @@
-//! Atomic types.
-//!
-//! * [`AtomicCell`], a thread-safe mutable memory location.
-//! * [`AtomicConsume`], for reading from primitive atomic types with "consume" ordering.
-
-#[cfg(target_has_atomic = "ptr")]
-#[cfg(not(crossbeam_loom))]
-// Use "wide" sequence lock if the pointer width <= 32 for preventing its counter against wrap
-// around.
-//
-// In narrow architectures (pointer width <= 16), the counter is still <= 32-bit and may be
-// vulnerable to wrap around. But it's mostly okay, since in such a primitive hardware, the
-// counter will not be increased that fast.
-// Note that Rust (and C99) pointers must be at least 16-bit (i.e., 8-bit targets are impossible): https://github.com/rust-lang/rust/pull/49305
-#[cfg_attr(
-    any(target_pointer_width = "16", target_pointer_width = "32"),
-    path = "seq_lock_wide.rs"
-)]
-mod seq_lock;
-
-#[cfg(target_has_atomic = "ptr")]
-// We cannot provide AtomicCell under cfg(crossbeam_loom) because loom's atomic
-// types have a different in-memory representation than the underlying type.
-// TODO: The latest loom supports fences, so fallback using seqlock may be available.
-#[cfg(not(crossbeam_loom))]
-mod atomic_cell;
-#[cfg(target_has_atomic = "ptr")]
-#[cfg(not(crossbeam_loom))]
-pub use atomic_cell::AtomicCell;
-
-mod consume;
-pub use consume::AtomicConsume;
diff --git a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/src/atomic/seq_lock.rs b/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/src/atomic/seq_lock.rs
deleted file mode 100644
index ff8defd2..0000000
--- a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/src/atomic/seq_lock.rs
+++ /dev/null
@@ -1,112 +0,0 @@
-use core::mem;
-use core::sync::atomic::{self, AtomicUsize, Ordering};
-
-use crate::Backoff;
-
-/// A simple stamped lock.
-pub(crate) struct SeqLock {
-    /// The current state of the lock.
-    ///
-    /// All bits except the least significant one hold the current stamp. When locked, the state
-    /// equals 1 and doesn't contain a valid stamp.
-    state: AtomicUsize,
-}
-
-impl SeqLock {
-    pub(crate) const fn new() -> Self {
-        Self {
-            state: AtomicUsize::new(0),
-        }
-    }
-
-    /// If not locked, returns the current stamp.
-    ///
-    /// This method should be called before optimistic reads.
-    #[inline]
-    pub(crate) fn optimistic_read(&self) -> Option<usize> {
-        let state = self.state.load(Ordering::Acquire);
-        if state == 1 {
-            None
-        } else {
-            Some(state)
-        }
-    }
-
-    /// Returns `true` if the current stamp is equal to `stamp`.
-    ///
-    /// This method should be called after optimistic reads to check whether they are valid. The
-    /// argument `stamp` should correspond to the one returned by method `optimistic_read`.
-    #[inline]
-    pub(crate) fn validate_read(&self, stamp: usize) -> bool {
-        atomic::fence(Ordering::Acquire);
-        self.state.load(Ordering::Relaxed) == stamp
-    }
-
-    /// Grabs the lock for writing.
-    #[inline]
-    pub(crate) fn write(&'static self) -> SeqLockWriteGuard {
-        let backoff = Backoff::new();
-        loop {
-            let previous = self.state.swap(1, Ordering::Acquire);
-
-            if previous != 1 {
-                atomic::fence(Ordering::Release);
-
-                return SeqLockWriteGuard {
-                    lock: self,
-                    state: previous,
-                };
-            }
-
-            backoff.snooze();
-        }
-    }
-}
-
-/// An RAII guard that releases the lock and increments the stamp when dropped.
-pub(crate) struct SeqLockWriteGuard {
-    /// The parent lock.
-    lock: &'static SeqLock,
-
-    /// The stamp before locking.
-    state: usize,
-}
-
-impl SeqLockWriteGuard {
-    /// Releases the lock without incrementing the stamp.
-    #[inline]
-    pub(crate) fn abort(self) {
-        self.lock.state.store(self.state, Ordering::Release);
-
-        // We specifically don't want to call drop(), since that's
-        // what increments the stamp.
-        mem::forget(self);
-    }
-}
-
-impl Drop for SeqLockWriteGuard {
-    #[inline]
-    fn drop(&mut self) {
-        // Release the lock and increment the stamp.
-        self.lock
-            .state
-            .store(self.state.wrapping_add(2), Ordering::Release);
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::SeqLock;
-
-    #[test]
-    fn test_abort() {
-        static LK: SeqLock = SeqLock::new();
-        let before = LK.optimistic_read().unwrap();
-        {
-            let guard = LK.write();
-            guard.abort();
-        }
-        let after = LK.optimistic_read().unwrap();
-        assert_eq!(before, after, "aborted write does not update the stamp");
-    }
-}
diff --git a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/src/atomic/seq_lock_wide.rs b/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/src/atomic/seq_lock_wide.rs
deleted file mode 100644
index ef5d94a..0000000
--- a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/src/atomic/seq_lock_wide.rs
+++ /dev/null
@@ -1,155 +0,0 @@
-use core::mem;
-use core::sync::atomic::{self, AtomicUsize, Ordering};
-
-use crate::Backoff;
-
-/// A simple stamped lock.
-///
-/// The state is represented as two `AtomicUsize`: `state_hi` for high bits and `state_lo` for low
-/// bits.
-pub(crate) struct SeqLock {
-    /// The high bits of the current state of the lock.
-    state_hi: AtomicUsize,
-
-    /// The low bits of the current state of the lock.
-    ///
-    /// All bits except the least significant one hold the current stamp. When locked, the state_lo
-    /// equals 1 and doesn't contain a valid stamp.
-    state_lo: AtomicUsize,
-}
-
-impl SeqLock {
-    pub(crate) const fn new() -> Self {
-        Self {
-            state_hi: AtomicUsize::new(0),
-            state_lo: AtomicUsize::new(0),
-        }
-    }
-
-    /// If not locked, returns the current stamp.
-    ///
-    /// This method should be called before optimistic reads.
-    #[inline]
-    pub(crate) fn optimistic_read(&self) -> Option<(usize, usize)> {
-        // The acquire loads from `state_hi` and `state_lo` synchronize with the release stores in
-        // `SeqLockWriteGuard::drop`.
-        //
-        // As a consequence, we can make sure that (1) all writes within the era of `state_hi - 1`
-        // happens before now; and therefore, (2) if `state_lo` is even, all writes within the
-        // critical section of (`state_hi`, `state_lo`) happens before now.
-        let state_hi = self.state_hi.load(Ordering::Acquire);
-        let state_lo = self.state_lo.load(Ordering::Acquire);
-        if state_lo == 1 {
-            None
-        } else {
-            Some((state_hi, state_lo))
-        }
-    }
-
-    /// Returns `true` if the current stamp is equal to `stamp`.
-    ///
-    /// This method should be called after optimistic reads to check whether they are valid. The
-    /// argument `stamp` should correspond to the one returned by method `optimistic_read`.
-    #[inline]
-    pub(crate) fn validate_read(&self, stamp: (usize, usize)) -> bool {
-        // Thanks to the fence, if we're noticing any modification to the data at the critical
-        // section of `(a, b)`, then the critical section's write of 1 to state_lo should be
-        // visible.
-        atomic::fence(Ordering::Acquire);
-
-        // So if `state_lo` coincides with `stamp.1`, then either (1) we're noticing no modification
-        // to the data after the critical section of `(stamp.0, stamp.1)`, or (2) `state_lo` wrapped
-        // around.
-        //
-        // If (2) is the case, the acquire ordering ensures we see the new value of `state_hi`.
-        let state_lo = self.state_lo.load(Ordering::Acquire);
-
-        // If (2) is the case and `state_hi` coincides with `stamp.0`, then `state_hi` also wrapped
-        // around, which we give up to correctly validate the read.
-        let state_hi = self.state_hi.load(Ordering::Relaxed);
-
-        // Except for the case that both `state_hi` and `state_lo` wrapped around, the following
-        // condition implies that we're noticing no modification to the data after the critical
-        // section of `(stamp.0, stamp.1)`.
-        (state_hi, state_lo) == stamp
-    }
-
-    /// Grabs the lock for writing.
-    #[inline]
-    pub(crate) fn write(&'static self) -> SeqLockWriteGuard {
-        let backoff = Backoff::new();
-        loop {
-            let previous = self.state_lo.swap(1, Ordering::Acquire);
-
-            if previous != 1 {
-                // To synchronize with the acquire fence in `validate_read` via any modification to
-                // the data at the critical section of `(state_hi, previous)`.
-                atomic::fence(Ordering::Release);
-
-                return SeqLockWriteGuard {
-                    lock: self,
-                    state_lo: previous,
-                };
-            }
-
-            backoff.snooze();
-        }
-    }
-}
-
-/// An RAII guard that releases the lock and increments the stamp when dropped.
-pub(crate) struct SeqLockWriteGuard {
-    /// The parent lock.
-    lock: &'static SeqLock,
-
-    /// The stamp before locking.
-    state_lo: usize,
-}
-
-impl SeqLockWriteGuard {
-    /// Releases the lock without incrementing the stamp.
-    #[inline]
-    pub(crate) fn abort(self) {
-        self.lock.state_lo.store(self.state_lo, Ordering::Release);
-        mem::forget(self);
-    }
-}
-
-impl Drop for SeqLockWriteGuard {
-    #[inline]
-    fn drop(&mut self) {
-        let state_lo = self.state_lo.wrapping_add(2);
-
-        // Increase the high bits if the low bits wrap around.
-        //
-        // Release ordering for synchronizing with `optimistic_read`.
-        if state_lo == 0 {
-            let state_hi = self.lock.state_hi.load(Ordering::Relaxed);
-            self.lock
-                .state_hi
-                .store(state_hi.wrapping_add(1), Ordering::Release);
-        }
-
-        // Release the lock and increment the stamp.
-        //
-        // Release ordering for synchronizing with `optimistic_read`.
-        self.lock.state_lo.store(state_lo, Ordering::Release);
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::SeqLock;
-
-    #[test]
-    fn test_abort() {
-        static LK: SeqLock = SeqLock::new();
-        let before = LK.optimistic_read().unwrap();
-        {
-            let guard = LK.write();
-            guard.abort();
-        }
-        let after = LK.optimistic_read().unwrap();
-        assert_eq!(before, after, "aborted write does not update the stamp");
-    }
-}
diff --git a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/src/backoff.rs b/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/src/backoff.rs
deleted file mode 100644
index 7a505ed..0000000
--- a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/src/backoff.rs
+++ /dev/null
@@ -1,287 +0,0 @@
-use crate::primitive::hint;
-use core::cell::Cell;
-use core::fmt;
-
-const SPIN_LIMIT: u32 = 6;
-const YIELD_LIMIT: u32 = 10;
-
-/// Performs exponential backoff in spin loops.
-///
-/// Backing off in spin loops reduces contention and improves overall performance.
-///
-/// This primitive can execute *YIELD* and *PAUSE* instructions, yield the current thread to the OS
-/// scheduler, and tell when is a good time to block the thread using a different synchronization
-/// mechanism. Each step of the back off procedure takes roughly twice as long as the previous
-/// step.
-///
-/// # Examples
-///
-/// Backing off in a lock-free loop:
-///
-/// ```
-/// use crossbeam_utils::Backoff;
-/// use std::sync::atomic::AtomicUsize;
-/// use std::sync::atomic::Ordering::SeqCst;
-///
-/// fn fetch_mul(a: &AtomicUsize, b: usize) -> usize {
-///     let backoff = Backoff::new();
-///     loop {
-///         let val = a.load(SeqCst);
-///         if a.compare_exchange(val, val.wrapping_mul(b), SeqCst, SeqCst).is_ok() {
-///             return val;
-///         }
-///         backoff.spin();
-///     }
-/// }
-/// ```
-///
-/// Waiting for an [`AtomicBool`] to become `true`:
-///
-/// ```
-/// use crossbeam_utils::Backoff;
-/// use std::sync::atomic::AtomicBool;
-/// use std::sync::atomic::Ordering::SeqCst;
-///
-/// fn spin_wait(ready: &AtomicBool) {
-///     let backoff = Backoff::new();
-///     while !ready.load(SeqCst) {
-///         backoff.snooze();
-///     }
-/// }
-/// ```
-///
-/// Waiting for an [`AtomicBool`] to become `true` and parking the thread after a long wait.
-/// Note that whoever sets the atomic variable to `true` must notify the parked thread by calling
-/// [`unpark()`]:
-///
-/// ```
-/// use crossbeam_utils::Backoff;
-/// use std::sync::atomic::AtomicBool;
-/// use std::sync::atomic::Ordering::SeqCst;
-/// use std::thread;
-///
-/// fn blocking_wait(ready: &AtomicBool) {
-///     let backoff = Backoff::new();
-///     while !ready.load(SeqCst) {
-///         if backoff.is_completed() {
-///             thread::park();
-///         } else {
-///             backoff.snooze();
-///         }
-///     }
-/// }
-/// ```
-///
-/// [`is_completed`]: Backoff::is_completed
-/// [`std::thread::park()`]: std::thread::park
-/// [`Condvar`]: std::sync::Condvar
-/// [`AtomicBool`]: std::sync::atomic::AtomicBool
-/// [`unpark()`]: std::thread::Thread::unpark
-pub struct Backoff {
-    step: Cell<u32>,
-}
-
-impl Backoff {
-    /// Creates a new `Backoff`.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use crossbeam_utils::Backoff;
-    ///
-    /// let backoff = Backoff::new();
-    /// ```
-    #[inline]
-    pub fn new() -> Self {
-        Backoff { step: Cell::new(0) }
-    }
-
-    /// Resets the `Backoff`.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use crossbeam_utils::Backoff;
-    ///
-    /// let backoff = Backoff::new();
-    /// backoff.reset();
-    /// ```
-    #[inline]
-    pub fn reset(&self) {
-        self.step.set(0);
-    }
-
-    /// Backs off in a lock-free loop.
-    ///
-    /// This method should be used when we need to retry an operation because another thread made
-    /// progress.
-    ///
-    /// The processor may yield using the *YIELD* or *PAUSE* instruction.
-    ///
-    /// # Examples
-    ///
-    /// Backing off in a lock-free loop:
-    ///
-    /// ```
-    /// use crossbeam_utils::Backoff;
-    /// use std::sync::atomic::AtomicUsize;
-    /// use std::sync::atomic::Ordering::SeqCst;
-    ///
-    /// fn fetch_mul(a: &AtomicUsize, b: usize) -> usize {
-    ///     let backoff = Backoff::new();
-    ///     loop {
-    ///         let val = a.load(SeqCst);
-    ///         if a.compare_exchange(val, val.wrapping_mul(b), SeqCst, SeqCst).is_ok() {
-    ///             return val;
-    ///         }
-    ///         backoff.spin();
-    ///     }
-    /// }
-    ///
-    /// let a = AtomicUsize::new(7);
-    /// assert_eq!(fetch_mul(&a, 8), 7);
-    /// assert_eq!(a.load(SeqCst), 56);
-    /// ```
-    #[inline]
-    pub fn spin(&self) {
-        for _ in 0..1 << self.step.get().min(SPIN_LIMIT) {
-            hint::spin_loop();
-        }
-
-        if self.step.get() <= SPIN_LIMIT {
-            self.step.set(self.step.get() + 1);
-        }
-    }
-
-    /// Backs off in a blocking loop.
-    ///
-    /// This method should be used when we need to wait for another thread to make progress.
-    ///
-    /// The processor may yield using the *YIELD* or *PAUSE* instruction and the current thread
-    /// may yield by giving up a timeslice to the OS scheduler.
-    ///
-    /// In `#[no_std]` environments, this method is equivalent to [`spin`].
-    ///
-    /// If possible, use [`is_completed`] to check when it is advised to stop using backoff and
-    /// block the current thread using a different synchronization mechanism instead.
-    ///
-    /// [`spin`]: Backoff::spin
-    /// [`is_completed`]: Backoff::is_completed
-    ///
-    /// # Examples
-    ///
-    /// Waiting for an [`AtomicBool`] to become `true`:
-    ///
-    /// ```
-    /// use crossbeam_utils::Backoff;
-    /// use std::sync::Arc;
-    /// use std::sync::atomic::AtomicBool;
-    /// use std::sync::atomic::Ordering::SeqCst;
-    /// use std::thread;
-    /// use std::time::Duration;
-    ///
-    /// fn spin_wait(ready: &AtomicBool) {
-    ///     let backoff = Backoff::new();
-    ///     while !ready.load(SeqCst) {
-    ///         backoff.snooze();
-    ///     }
-    /// }
-    ///
-    /// let ready = Arc::new(AtomicBool::new(false));
-    /// let ready2 = ready.clone();
-    ///
-    /// thread::spawn(move || {
-    ///     thread::sleep(Duration::from_millis(100));
-    ///     ready2.store(true, SeqCst);
-    /// });
-    ///
-    /// assert_eq!(ready.load(SeqCst), false);
-    /// spin_wait(&ready);
-    /// assert_eq!(ready.load(SeqCst), true);
-    /// # std::thread::sleep(std::time::Duration::from_millis(500)); // wait for background threads closed: https://github.com/rust-lang/miri/issues/1371
-    /// ```
-    ///
-    /// [`AtomicBool`]: std::sync::atomic::AtomicBool
-    #[inline]
-    pub fn snooze(&self) {
-        if self.step.get() <= SPIN_LIMIT {
-            for _ in 0..1 << self.step.get() {
-                hint::spin_loop();
-            }
-        } else {
-            #[cfg(not(feature = "std"))]
-            for _ in 0..1 << self.step.get() {
-                hint::spin_loop();
-            }
-
-            #[cfg(feature = "std")]
-            ::std::thread::yield_now();
-        }
-
-        if self.step.get() <= YIELD_LIMIT {
-            self.step.set(self.step.get() + 1);
-        }
-    }
-
-    /// Returns `true` if exponential backoff has completed and blocking the thread is advised.
-    ///
-    /// # Examples
-    ///
-    /// Waiting for an [`AtomicBool`] to become `true` and parking the thread after a long wait:
-    ///
-    /// ```
-    /// use crossbeam_utils::Backoff;
-    /// use std::sync::Arc;
-    /// use std::sync::atomic::AtomicBool;
-    /// use std::sync::atomic::Ordering::SeqCst;
-    /// use std::thread;
-    /// use std::time::Duration;
-    ///
-    /// fn blocking_wait(ready: &AtomicBool) {
-    ///     let backoff = Backoff::new();
-    ///     while !ready.load(SeqCst) {
-    ///         if backoff.is_completed() {
-    ///             thread::park();
-    ///         } else {
-    ///             backoff.snooze();
-    ///         }
-    ///     }
-    /// }
-    ///
-    /// let ready = Arc::new(AtomicBool::new(false));
-    /// let ready2 = ready.clone();
-    /// let waiter = thread::current();
-    ///
-    /// thread::spawn(move || {
-    ///     thread::sleep(Duration::from_millis(100));
-    ///     ready2.store(true, SeqCst);
-    ///     waiter.unpark();
-    /// });
-    ///
-    /// assert_eq!(ready.load(SeqCst), false);
-    /// blocking_wait(&ready);
-    /// assert_eq!(ready.load(SeqCst), true);
-    /// # std::thread::sleep(std::time::Duration::from_millis(500)); // wait for background threads closed: https://github.com/rust-lang/miri/issues/1371
-    /// ```
-    ///
-    /// [`AtomicBool`]: std::sync::atomic::AtomicBool
-    #[inline]
-    pub fn is_completed(&self) -> bool {
-        self.step.get() > YIELD_LIMIT
-    }
-}
-
-impl fmt::Debug for Backoff {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct("Backoff")
-            .field("step", &self.step)
-            .field("is_completed", &self.is_completed())
-            .finish()
-    }
-}
-
-impl Default for Backoff {
-    fn default() -> Backoff {
-        Backoff::new()
-    }
-}
diff --git a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/src/cache_padded.rs b/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/src/cache_padded.rs
deleted file mode 100644
index 6c930c6f3..0000000
--- a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/src/cache_padded.rs
+++ /dev/null
@@ -1,217 +0,0 @@
-use core::fmt;
-use core::ops::{Deref, DerefMut};
-
-/// Pads and aligns a value to the length of a cache line.
-///
-/// In concurrent programming, sometimes it is desirable to make sure commonly accessed pieces of
-/// data are not placed into the same cache line. Updating an atomic value invalidates the whole
-/// cache line it belongs to, which makes the next access to the same cache line slower for other
-/// CPU cores. Use `CachePadded` to ensure updating one piece of data doesn't invalidate other
-/// cached data.
-///
-/// # Size and alignment
-///
-/// Cache lines are assumed to be N bytes long, depending on the architecture:
-///
-/// * On x86-64, aarch64, and powerpc64, N = 128.
-/// * On arm, mips, mips64, sparc, and hexagon, N = 32.
-/// * On m68k, N = 16.
-/// * On s390x, N = 256.
-/// * On all others, N = 64.
-///
-/// Note that N is just a reasonable guess and is not guaranteed to match the actual cache line
-/// length of the machine the program is running on. On modern Intel architectures, spatial
-/// prefetcher is pulling pairs of 64-byte cache lines at a time, so we pessimistically assume that
-/// cache lines are 128 bytes long.
-///
-/// The size of `CachePadded<T>` is the smallest multiple of N bytes large enough to accommodate
-/// a value of type `T`.
-///
-/// The alignment of `CachePadded<T>` is the maximum of N bytes and the alignment of `T`.
-///
-/// # Examples
-///
-/// Alignment and padding:
-///
-/// ```
-/// use crossbeam_utils::CachePadded;
-///
-/// let array = [CachePadded::new(1i8), CachePadded::new(2i8)];
-/// let addr1 = &*array[0] as *const i8 as usize;
-/// let addr2 = &*array[1] as *const i8 as usize;
-///
-/// assert!(addr2 - addr1 >= 32);
-/// assert_eq!(addr1 % 32, 0);
-/// assert_eq!(addr2 % 32, 0);
-/// ```
-///
-/// When building a concurrent queue with a head and a tail index, it is wise to place them in
-/// different cache lines so that concurrent threads pushing and popping elements don't invalidate
-/// each other's cache lines:
-///
-/// ```
-/// use crossbeam_utils::CachePadded;
-/// use std::sync::atomic::AtomicUsize;
-///
-/// struct Queue<T> {
-///     head: CachePadded<AtomicUsize>,
-///     tail: CachePadded<AtomicUsize>,
-///     buffer: *mut T,
-/// }
-/// ```
-#[derive(Clone, Copy, Default, Hash, PartialEq, Eq)]
-// Starting from Intel's Sandy Bridge, spatial prefetcher is now pulling pairs of 64-byte cache
-// lines at a time, so we have to align to 128 bytes rather than 64.
-//
-// Sources:
-// - https://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-optimization-manual.pdf
-// - https://github.com/facebook/folly/blob/1b5288e6eea6df074758f877c849b6e73bbb9fbb/folly/lang/Align.h#L107
-//
-// aarch64/arm64ec's big.LITTLE architecture has asymmetric cores and "big" cores have 128-byte cache line size.
-//
-// Sources:
-// - https://www.mono-project.com/news/2016/09/12/arm64-icache/
-//
-// powerpc64 has 128-byte cache line size.
-//
-// Sources:
-// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_ppc64x.go#L9
-// - https://github.com/torvalds/linux/blob/3516bd729358a2a9b090c1905bd2a3fa926e24c6/arch/powerpc/include/asm/cache.h#L26
-#[cfg_attr(
-    any(
-        target_arch = "x86_64",
-        target_arch = "aarch64",
-        target_arch = "arm64ec",
-        target_arch = "powerpc64",
-    ),
-    repr(align(128))
-)]
-// arm, mips, mips64, sparc, and hexagon have 32-byte cache line size.
-//
-// Sources:
-// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_arm.go#L7
-// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_mips.go#L7
-// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_mipsle.go#L7
-// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_mips64x.go#L9
-// - https://github.com/torvalds/linux/blob/3516bd729358a2a9b090c1905bd2a3fa926e24c6/arch/sparc/include/asm/cache.h#L17
-// - https://github.com/torvalds/linux/blob/3516bd729358a2a9b090c1905bd2a3fa926e24c6/arch/hexagon/include/asm/cache.h#L12
-#[cfg_attr(
-    any(
-        target_arch = "arm",
-        target_arch = "mips",
-        target_arch = "mips32r6",
-        target_arch = "mips64",
-        target_arch = "mips64r6",
-        target_arch = "sparc",
-        target_arch = "hexagon",
-    ),
-    repr(align(32))
-)]
-// m68k has 16-byte cache line size.
-//
-// Sources:
-// - https://github.com/torvalds/linux/blob/3516bd729358a2a9b090c1905bd2a3fa926e24c6/arch/m68k/include/asm/cache.h#L9
-#[cfg_attr(target_arch = "m68k", repr(align(16)))]
-// s390x has 256-byte cache line size.
-//
-// Sources:
-// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_s390x.go#L7
-// - https://github.com/torvalds/linux/blob/3516bd729358a2a9b090c1905bd2a3fa926e24c6/arch/s390/include/asm/cache.h#L13
-#[cfg_attr(target_arch = "s390x", repr(align(256)))]
-// x86, wasm, riscv, and sparc64 have 64-byte cache line size.
-//
-// Sources:
-// - https://github.com/golang/go/blob/dda2991c2ea0c5914714469c4defc2562a907230/src/internal/cpu/cpu_x86.go#L9
-// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_wasm.go#L7
-// - https://github.com/torvalds/linux/blob/3516bd729358a2a9b090c1905bd2a3fa926e24c6/arch/riscv/include/asm/cache.h#L10
-// - https://github.com/torvalds/linux/blob/3516bd729358a2a9b090c1905bd2a3fa926e24c6/arch/sparc/include/asm/cache.h#L19
-//
-// All others are assumed to have 64-byte cache line size.
-#[cfg_attr(
-    not(any(
-        target_arch = "x86_64",
-        target_arch = "aarch64",
-        target_arch = "arm64ec",
-        target_arch = "powerpc64",
-        target_arch = "arm",
-        target_arch = "mips",
-        target_arch = "mips32r6",
-        target_arch = "mips64",
-        target_arch = "mips64r6",
-        target_arch = "sparc",
-        target_arch = "hexagon",
-        target_arch = "m68k",
-        target_arch = "s390x",
-    )),
-    repr(align(64))
-)]
-pub struct CachePadded<T> {
-    value: T,
-}
-
-unsafe impl<T: Send> Send for CachePadded<T> {}
-unsafe impl<T: Sync> Sync for CachePadded<T> {}
-
-impl<T> CachePadded<T> {
-    /// Pads and aligns a value to the length of a cache line.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use crossbeam_utils::CachePadded;
-    ///
-    /// let padded_value = CachePadded::new(1);
-    /// ```
-    pub const fn new(t: T) -> CachePadded<T> {
-        CachePadded::<T> { value: t }
-    }
-
-    /// Returns the inner value.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use crossbeam_utils::CachePadded;
-    ///
-    /// let padded_value = CachePadded::new(7);
-    /// let value = padded_value.into_inner();
-    /// assert_eq!(value, 7);
-    /// ```
-    pub fn into_inner(self) -> T {
-        self.value
-    }
-}
-
-impl<T> Deref for CachePadded<T> {
-    type Target = T;
-
-    fn deref(&self) -> &T {
-        &self.value
-    }
-}
-
-impl<T> DerefMut for CachePadded<T> {
-    fn deref_mut(&mut self) -> &mut T {
-        &mut self.value
-    }
-}
-
-impl<T: fmt::Debug> fmt::Debug for CachePadded<T> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct("CachePadded")
-            .field("value", &self.value)
-            .finish()
-    }
-}
-
-impl<T> From<T> for CachePadded<T> {
-    fn from(t: T) -> Self {
-        CachePadded::new(t)
-    }
-}
-
-impl<T: fmt::Display> fmt::Display for CachePadded<T> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        fmt::Display::fmt(&self.value, f)
-    }
-}
diff --git a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/src/lib.rs b/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/src/lib.rs
deleted file mode 100644
index 6f124f97..0000000
--- a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/src/lib.rs
+++ /dev/null
@@ -1,110 +0,0 @@
-//! Miscellaneous tools for concurrent programming.
-//!
-//! ## Atomics
-//!
-//! * [`AtomicCell`], a thread-safe mutable memory location.
-//! * [`AtomicConsume`], for reading from primitive atomic types with "consume" ordering.
-//!
-//! ## Thread synchronization
-//!
-//! * [`Parker`], a thread parking primitive.
-//! * [`ShardedLock`], a sharded reader-writer lock with fast concurrent reads.
-//! * [`WaitGroup`], for synchronizing the beginning or end of some computation.
-//!
-//! ## Utilities
-//!
-//! * [`Backoff`], for exponential backoff in spin loops.
-//! * [`CachePadded`], for padding and aligning a value to the length of a cache line.
-//! * [`scope`], for spawning threads that borrow local variables from the stack.
-//!
-//! [`AtomicCell`]: atomic::AtomicCell
-//! [`AtomicConsume`]: atomic::AtomicConsume
-//! [`Parker`]: sync::Parker
-//! [`ShardedLock`]: sync::ShardedLock
-//! [`WaitGroup`]: sync::WaitGroup
-//! [`scope`]: thread::scope
-
-#![no_std]
-#![doc(test(
-    no_crate_inject,
-    attr(
-        deny(warnings, rust_2018_idioms),
-        allow(dead_code, unused_assignments, unused_variables)
-    )
-))]
-#![warn(
-    missing_docs,
-    missing_debug_implementations,
-    rust_2018_idioms,
-    unreachable_pub
-)]
-
-#[cfg(feature = "std")]
-extern crate std;
-
-#[cfg(crossbeam_loom)]
-#[allow(unused_imports)]
-mod primitive {
-    pub(crate) mod hint {
-        pub(crate) use loom::hint::spin_loop;
-    }
-    pub(crate) mod sync {
-        pub(crate) mod atomic {
-            pub(crate) use loom::sync::atomic::{
-                AtomicBool, AtomicI16, AtomicI32, AtomicI64, AtomicI8, AtomicIsize, AtomicU16,
-                AtomicU32, AtomicU64, AtomicU8, AtomicUsize, Ordering,
-            };
-
-            // FIXME: loom does not support compiler_fence at the moment.
-            // https://github.com/tokio-rs/loom/issues/117
-            // we use fence as a stand-in for compiler_fence for the time being.
-            // this may miss some races since fence is stronger than compiler_fence,
-            // but it's the best we can do for the time being.
-            pub(crate) use loom::sync::atomic::fence as compiler_fence;
-        }
-        pub(crate) use loom::sync::{Arc, Condvar, Mutex};
-    }
-}
-#[cfg(not(crossbeam_loom))]
-#[allow(unused_imports)]
-mod primitive {
-    pub(crate) mod hint {
-        pub(crate) use core::hint::spin_loop;
-    }
-    pub(crate) mod sync {
-        pub(crate) mod atomic {
-            pub(crate) use core::sync::atomic::{compiler_fence, Ordering};
-            #[cfg(not(crossbeam_no_atomic))]
-            pub(crate) use core::sync::atomic::{
-                AtomicBool, AtomicI16, AtomicI8, AtomicIsize, AtomicU16, AtomicU8, AtomicUsize,
-            };
-            #[cfg(not(crossbeam_no_atomic))]
-            #[cfg(any(target_has_atomic = "32", not(target_pointer_width = "16")))]
-            pub(crate) use core::sync::atomic::{AtomicI32, AtomicU32};
-            #[cfg(not(crossbeam_no_atomic))]
-            #[cfg(any(
-                target_has_atomic = "64",
-                not(any(target_pointer_width = "16", target_pointer_width = "32")),
-            ))]
-            pub(crate) use core::sync::atomic::{AtomicI64, AtomicU64};
-        }
-
-        #[cfg(feature = "std")]
-        pub(crate) use std::sync::{Arc, Condvar, Mutex};
-    }
-}
-
-pub mod atomic;
-
-mod cache_padded;
-pub use crate::cache_padded::CachePadded;
-
-mod backoff;
-pub use crate::backoff::Backoff;
-
-#[cfg(feature = "std")]
-pub mod sync;
-
-#[cfg(feature = "std")]
-#[cfg(not(crossbeam_loom))]
-pub mod thread;
diff --git a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/src/sync/mod.rs b/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/src/sync/mod.rs
deleted file mode 100644
index f9eec71f..0000000
--- a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/src/sync/mod.rs
+++ /dev/null
@@ -1,17 +0,0 @@
-//! Thread synchronization primitives.
-//!
-//! * [`Parker`], a thread parking primitive.
-//! * [`ShardedLock`], a sharded reader-writer lock with fast concurrent reads.
-//! * [`WaitGroup`], for synchronizing the beginning or end of some computation.
-
-#[cfg(not(crossbeam_loom))]
-mod once_lock;
-mod parker;
-#[cfg(not(crossbeam_loom))]
-mod sharded_lock;
-mod wait_group;
-
-pub use self::parker::{Parker, Unparker};
-#[cfg(not(crossbeam_loom))]
-pub use self::sharded_lock::{ShardedLock, ShardedLockReadGuard, ShardedLockWriteGuard};
-pub use self::wait_group::WaitGroup;
diff --git a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/src/sync/once_lock.rs b/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/src/sync/once_lock.rs
deleted file mode 100644
index e057aca..0000000
--- a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/src/sync/once_lock.rs
+++ /dev/null
@@ -1,88 +0,0 @@
-// Based on unstable std::sync::OnceLock.
-//
-// Source: https://github.com/rust-lang/rust/blob/8e9c93df464b7ada3fc7a1c8ccddd9dcb24ee0a0/library/std/src/sync/once_lock.rs
-
-use core::cell::UnsafeCell;
-use core::mem::MaybeUninit;
-use std::sync::Once;
-
-pub(crate) struct OnceLock<T> {
-    once: Once,
-    value: UnsafeCell<MaybeUninit<T>>,
-    // Unlike std::sync::OnceLock, we don't need PhantomData here because
-    // we don't use #[may_dangle].
-}
-
-unsafe impl<T: Sync + Send> Sync for OnceLock<T> {}
-unsafe impl<T: Send> Send for OnceLock<T> {}
-
-impl<T> OnceLock<T> {
-    /// Creates a new empty cell.
-    #[must_use]
-    pub(crate) const fn new() -> Self {
-        Self {
-            once: Once::new(),
-            value: UnsafeCell::new(MaybeUninit::uninit()),
-        }
-    }
-
-    /// Gets the contents of the cell, initializing it with `f` if the cell
-    /// was empty.
-    ///
-    /// Many threads may call `get_or_init` concurrently with different
-    /// initializing functions, but it is guaranteed that only one function
-    /// will be executed.
-    ///
-    /// # Panics
-    ///
-    /// If `f` panics, the panic is propagated to the caller, and the cell
-    /// remains uninitialized.
-    ///
-    /// It is an error to reentrantly initialize the cell from `f`. The
-    /// exact outcome is unspecified. Current implementation deadlocks, but
-    /// this may be changed to a panic in the future.
-    pub(crate) fn get_or_init<F>(&self, f: F) -> &T
-    where
-        F: FnOnce() -> T,
-    {
-        // Fast path check
-        if self.once.is_completed() {
-            // SAFETY: The inner value has been initialized
-            return unsafe { self.get_unchecked() };
-        }
-        self.initialize(f);
-
-        // SAFETY: The inner value has been initialized
-        unsafe { self.get_unchecked() }
-    }
-
-    #[cold]
-    fn initialize<F>(&self, f: F)
-    where
-        F: FnOnce() -> T,
-    {
-        let slot = self.value.get();
-
-        self.once.call_once(|| {
-            let value = f();
-            unsafe { slot.write(MaybeUninit::new(value)) }
-        });
-    }
-
-    /// # Safety
-    ///
-    /// The value must be initialized
-    unsafe fn get_unchecked(&self) -> &T {
-        debug_assert!(self.once.is_completed());
-        &*self.value.get().cast::<T>()
-    }
-}
-
-impl<T> Drop for OnceLock<T> {
-    fn drop(&mut self) {
-        if self.once.is_completed() {
-            // SAFETY: The inner value has been initialized
-            unsafe { (*self.value.get()).assume_init_drop() };
-        }
-    }
-}
diff --git a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/src/sync/parker.rs b/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/src/sync/parker.rs
deleted file mode 100644
index 971981d..0000000
--- a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/src/sync/parker.rs
+++ /dev/null
@@ -1,415 +0,0 @@
-use crate::primitive::sync::atomic::{AtomicUsize, Ordering::SeqCst};
-use crate::primitive::sync::{Arc, Condvar, Mutex};
-use std::fmt;
-use std::marker::PhantomData;
-use std::time::{Duration, Instant};
-
-/// A thread parking primitive.
-///
-/// Conceptually, each `Parker` has an associated token which is initially not present:
-///
-/// * The [`park`] method blocks the current thread unless or until the token is available, at
-///   which point it automatically consumes the token.
-///
-/// * The [`park_timeout`] and [`park_deadline`] methods work the same as [`park`], but block for
-///   a specified maximum time.
-///
-/// * The [`unpark`] method atomically makes the token available if it wasn't already. Because the
-///   token is initially absent, [`unpark`] followed by [`park`] will result in the second call
-///   returning immediately.
-///
-/// In other words, each `Parker` acts a bit like a spinlock that can be locked and unlocked using
-/// [`park`] and [`unpark`].
-///
-/// # Examples
-///
-/// ```
-/// use std::thread;
-/// use std::time::Duration;
-/// use crossbeam_utils::sync::Parker;
-///
-/// let p = Parker::new();
-/// let u = p.unparker().clone();
-///
-/// // Make the token available.
-/// u.unpark();
-/// // Wakes up immediately and consumes the token.
-/// p.park();
-///
-/// thread::spawn(move || {
-///     thread::sleep(Duration::from_millis(500));
-///     u.unpark();
-/// });
-///
-/// // Wakes up when `u.unpark()` provides the token.
-/// p.park();
-/// # std::thread::sleep(std::time::Duration::from_millis(500)); // wait for background threads closed: https://github.com/rust-lang/miri/issues/1371
-/// ```
-///
-/// [`park`]: Parker::park
-/// [`park_timeout`]: Parker::park_timeout
-/// [`park_deadline`]: Parker::park_deadline
-/// [`unpark`]: Unparker::unpark
-pub struct Parker {
-    unparker: Unparker,
-    _marker: PhantomData<*const ()>,
-}
-
-unsafe impl Send for Parker {}
-
-impl Default for Parker {
-    fn default() -> Self {
-        Self {
-            unparker: Unparker {
-                inner: Arc::new(Inner {
-                    state: AtomicUsize::new(EMPTY),
-                    lock: Mutex::new(()),
-                    cvar: Condvar::new(),
-                }),
-            },
-            _marker: PhantomData,
-        }
-    }
-}
-
-impl Parker {
-    /// Creates a new `Parker`.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use crossbeam_utils::sync::Parker;
-    ///
-    /// let p = Parker::new();
-    /// ```
-    ///
-    pub fn new() -> Parker {
-        Self::default()
-    }
-
-    /// Blocks the current thread until the token is made available.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use crossbeam_utils::sync::Parker;
-    ///
-    /// let p = Parker::new();
-    /// let u = p.unparker().clone();
-    ///
-    /// // Make the token available.
-    /// u.unpark();
-    ///
-    /// // Wakes up immediately and consumes the token.
-    /// p.park();
-    /// ```
-    pub fn park(&self) {
-        self.unparker.inner.park(None);
-    }
-
-    /// Blocks the current thread until the token is made available, but only for a limited time.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use std::time::Duration;
-    /// use crossbeam_utils::sync::Parker;
-    ///
-    /// let p = Parker::new();
-    ///
-    /// // Waits for the token to become available, but will not wait longer than 500 ms.
-    /// p.park_timeout(Duration::from_millis(500));
-    /// ```
-    pub fn park_timeout(&self, timeout: Duration) {
-        match Instant::now().checked_add(timeout) {
-            Some(deadline) => self.park_deadline(deadline),
-            None => self.park(),
-        }
-    }
-
-    /// Blocks the current thread until the token is made available, or until a certain deadline.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use std::time::{Duration, Instant};
-    /// use crossbeam_utils::sync::Parker;
-    ///
-    /// let p = Parker::new();
-    /// let deadline = Instant::now() + Duration::from_millis(500);
-    ///
-    /// // Waits for the token to become available, but will not wait longer than 500 ms.
-    /// p.park_deadline(deadline);
-    /// ```
-    pub fn park_deadline(&self, deadline: Instant) {
-        self.unparker.inner.park(Some(deadline))
-    }
-
-    /// Returns a reference to an associated [`Unparker`].
-    ///
-    /// The returned [`Unparker`] doesn't have to be used by reference - it can also be cloned.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use crossbeam_utils::sync::Parker;
-    ///
-    /// let p = Parker::new();
-    /// let u = p.unparker().clone();
-    ///
-    /// // Make the token available.
-    /// u.unpark();
-    /// // Wakes up immediately and consumes the token.
-    /// p.park();
-    /// ```
-    ///
-    /// [`park`]: Parker::park
-    /// [`park_timeout`]: Parker::park_timeout
-    pub fn unparker(&self) -> &Unparker {
-        &self.unparker
-    }
-
-    /// Converts a `Parker` into a raw pointer.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use crossbeam_utils::sync::Parker;
-    ///
-    /// let p = Parker::new();
-    /// let raw = Parker::into_raw(p);
-    /// # let _ = unsafe { Parker::from_raw(raw) };
-    /// ```
-    pub fn into_raw(this: Parker) -> *const () {
-        Unparker::into_raw(this.unparker)
-    }
-
-    /// Converts a raw pointer into a `Parker`.
-    ///
-    /// # Safety
-    ///
-    /// This method is safe to use only with pointers returned by [`Parker::into_raw`].
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use crossbeam_utils::sync::Parker;
-    ///
-    /// let p = Parker::new();
-    /// let raw = Parker::into_raw(p);
-    /// let p = unsafe { Parker::from_raw(raw) };
-    /// ```
-    pub unsafe fn from_raw(ptr: *const ()) -> Parker {
-        Parker {
-            unparker: Unparker::from_raw(ptr),
-            _marker: PhantomData,
-        }
-    }
-}
-
-impl fmt::Debug for Parker {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.pad("Parker { .. }")
-    }
-}
-
-/// Unparks a thread parked by the associated [`Parker`].
-pub struct Unparker {
-    inner: Arc<Inner>,
-}
-
-unsafe impl Send for Unparker {}
-unsafe impl Sync for Unparker {}
-
-impl Unparker {
-    /// Atomically makes the token available if it is not already.
-    ///
-    /// This method will wake up the thread blocked on [`park`] or [`park_timeout`], if there is
-    /// any.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use std::thread;
-    /// use std::time::Duration;
-    /// use crossbeam_utils::sync::Parker;
-    ///
-    /// let p = Parker::new();
-    /// let u = p.unparker().clone();
-    ///
-    /// thread::spawn(move || {
-    ///     thread::sleep(Duration::from_millis(500));
-    ///     u.unpark();
-    /// });
-    ///
-    /// // Wakes up when `u.unpark()` provides the token.
-    /// p.park();
-    /// # std::thread::sleep(std::time::Duration::from_millis(500)); // wait for background threads closed: https://github.com/rust-lang/miri/issues/1371
-    /// ```
-    ///
-    /// [`park`]: Parker::park
-    /// [`park_timeout`]: Parker::park_timeout
-    pub fn unpark(&self) {
-        self.inner.unpark()
-    }
-
-    /// Converts an `Unparker` into a raw pointer.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use crossbeam_utils::sync::{Parker, Unparker};
-    ///
-    /// let p = Parker::new();
-    /// let u = p.unparker().clone();
-    /// let raw = Unparker::into_raw(u);
-    /// # let _ = unsafe { Unparker::from_raw(raw) };
-    /// ```
-    pub fn into_raw(this: Unparker) -> *const () {
-        Arc::into_raw(this.inner).cast::<()>()
-    }
-
-    /// Converts a raw pointer into an `Unparker`.
-    ///
-    /// # Safety
-    ///
-    /// This method is safe to use only with pointers returned by [`Unparker::into_raw`].
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use crossbeam_utils::sync::{Parker, Unparker};
-    ///
-    /// let p = Parker::new();
-    /// let u = p.unparker().clone();
-    ///
-    /// let raw = Unparker::into_raw(u);
-    /// let u = unsafe { Unparker::from_raw(raw) };
-    /// ```
-    pub unsafe fn from_raw(ptr: *const ()) -> Unparker {
-        Unparker {
-            inner: Arc::from_raw(ptr.cast::<Inner>()),
-        }
-    }
-}
-
-impl fmt::Debug for Unparker {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.pad("Unparker { .. }")
-    }
-}
-
-impl Clone for Unparker {
-    fn clone(&self) -> Unparker {
-        Unparker {
-            inner: self.inner.clone(),
-        }
-    }
-}
-
-const EMPTY: usize = 0;
-const PARKED: usize = 1;
-const NOTIFIED: usize = 2;
-
-struct Inner {
-    state: AtomicUsize,
-    lock: Mutex<()>,
-    cvar: Condvar,
-}
-
-impl Inner {
-    fn park(&self, deadline: Option<Instant>) {
-        // If we were previously notified then we consume this notification and return quickly.
-        if self
-            .state
-            .compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst)
-            .is_ok()
-        {
-            return;
-        }
-
-        // If the timeout is zero, then there is no need to actually block.
-        if let Some(deadline) = deadline {
-            if deadline <= Instant::now() {
-                return;
-            }
-        }
-
-        // Otherwise we need to coordinate going to sleep.
-        let mut m = self.lock.lock().unwrap();
-
-        match self.state.compare_exchange(EMPTY, PARKED, SeqCst, SeqCst) {
-            Ok(_) => {}
-            // Consume this notification to avoid spurious wakeups in the next park.
-            Err(NOTIFIED) => {
-                // We must read `state` here, even though we know it will be `NOTIFIED`. This is
-                // because `unpark` may have been called again since we read `NOTIFIED` in the
-                // `compare_exchange` above. We must perform an acquire operation that synchronizes
-                // with that `unpark` to observe any writes it made before the call to `unpark`. To
-                // do that we must read from the write it made to `state`.
-                let old = self.state.swap(EMPTY, SeqCst);
-                assert_eq!(old, NOTIFIED, "park state changed unexpectedly");
-                return;
-            }
-            Err(n) => panic!("inconsistent park_timeout state: {}", n),
-        }
-
-        loop {
-            // Block the current thread on the conditional variable.
-            m = match deadline {
-                None => self.cvar.wait(m).unwrap(),
-                Some(deadline) => {
-                    let now = Instant::now();
-                    if now < deadline {
-                        // We could check for a timeout here, in the return value of wait_timeout,
-                        // but in the case that a timeout and an unpark arrive simultaneously, we
-                        // prefer to report the former.
-                        self.cvar.wait_timeout(m, deadline - now).unwrap().0
-                    } else {
-                        // We've timed out; swap out the state back to empty on our way out
-                        match self.state.swap(EMPTY, SeqCst) {
-                            NOTIFIED | PARKED => return,
-                            n => panic!("inconsistent park_timeout state: {}", n),
-                        };
-                    }
-                }
-            };
-
-            if self
-                .state
-                .compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst)
-                .is_ok()
-            {
-                // got a notification
-                return;
-            }
-
-            // Spurious wakeup, go back to sleep. Alternatively, if we timed out, it will be caught
-            // in the branch above, when we discover the deadline is in the past
-        }
-    }
-
-    pub(crate) fn unpark(&self) {
-        // To ensure the unparked thread will observe any writes we made before this call, we must
-        // perform a release operation that `park` can synchronize with. To do that we must write
-        // `NOTIFIED` even if `state` is already `NOTIFIED`. That is why this must be a swap rather
-        // than a compare-and-swap that returns if it reads `NOTIFIED` on failure.
-        match self.state.swap(NOTIFIED, SeqCst) {
-            EMPTY => return,    // no one was waiting
-            NOTIFIED => return, // already unparked
-            PARKED => {}        // gotta go wake someone up
-            _ => panic!("inconsistent state in unpark"),
-        }
-
-        // There is a period between when the parked thread sets `state` to `PARKED` (or last
-        // checked `state` in the case of a spurious wakeup) and when it actually waits on `cvar`.
-        // If we were to notify during this period it would be ignored and then when the parked
-        // thread went to sleep it would never wake up. Fortunately, it has `lock` locked at this
-        // stage so we can acquire `lock` to wait until it is ready to receive the notification.
-        //
-        // Releasing `lock` before the call to `notify_one` means that when the parked thread wakes
-        // it doesn't get woken only to have to wait for us to release `lock`.
-        drop(self.lock.lock().unwrap());
-        self.cvar.notify_one();
-    }
-}
diff --git a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/src/sync/sharded_lock.rs b/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/src/sync/sharded_lock.rs
deleted file mode 100644
index 629b975..0000000
--- a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/src/sync/sharded_lock.rs
+++ /dev/null
@@ -1,638 +0,0 @@
-use std::boxed::Box;
-use std::cell::UnsafeCell;
-use std::collections::HashMap;
-use std::fmt;
-use std::marker::PhantomData;
-use std::mem;
-use std::ops::{Deref, DerefMut};
-use std::panic::{RefUnwindSafe, UnwindSafe};
-use std::sync::{LockResult, PoisonError, TryLockError, TryLockResult};
-use std::sync::{Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard};
-use std::thread::{self, ThreadId};
-use std::vec::Vec;
-
-use crate::sync::once_lock::OnceLock;
-use crate::CachePadded;
-
-/// The number of shards per sharded lock. Must be a power of two.
-const NUM_SHARDS: usize = 8;
-
-/// A shard containing a single reader-writer lock.
-struct Shard {
-    /// The inner reader-writer lock.
-    lock: RwLock<()>,
-
-    /// The write-guard keeping this shard locked.
-    ///
-    /// Write operations will lock each shard and store the guard here. These guards get dropped at
-    /// the same time the big guard is dropped.
-    write_guard: UnsafeCell<Option<RwLockWriteGuard<'static, ()>>>,
-}
-
-/// A sharded reader-writer lock.
-///
-/// This lock is equivalent to [`RwLock`], except read operations are faster and write operations
-/// are slower.
-///
-/// A `ShardedLock` is internally made of a list of *shards*, each being a [`RwLock`] occupying a
-/// single cache line. Read operations will pick one of the shards depending on the current thread
-/// and lock it. Write operations need to lock all shards in succession.
-///
-/// By splitting the lock into shards, concurrent read operations will in most cases choose
-/// different shards and thus update different cache lines, which is good for scalability. However,
-/// write operations need to do more work and are therefore slower than usual.
-///
-/// The priority policy of the lock is dependent on the underlying operating system's
-/// implementation, and this type does not guarantee that any particular policy will be used.
-///
-/// # Poisoning
-///
-/// A `ShardedLock`, like [`RwLock`], will become poisoned on a panic. Note that it may only be
-/// poisoned if a panic occurs while a write operation is in progress. If a panic occurs in any
-/// read operation, the lock will not be poisoned.
-///
-/// # Examples
-///
-/// ```
-/// use crossbeam_utils::sync::ShardedLock;
-///
-/// let lock = ShardedLock::new(5);
-///
-/// // Any number of read locks can be held at once.
-/// {
-///     let r1 = lock.read().unwrap();
-///     let r2 = lock.read().unwrap();
-///     assert_eq!(*r1, 5);
-///     assert_eq!(*r2, 5);
-/// } // Read locks are dropped at this point.
-///
-/// // However, only one write lock may be held.
-/// {
-///     let mut w = lock.write().unwrap();
-///     *w += 1;
-///     assert_eq!(*w, 6);
-/// } // Write lock is dropped here.
-/// ```
-///
-/// [`RwLock`]: std::sync::RwLock
-pub struct ShardedLock<T: ?Sized> {
-    /// A list of locks protecting the internal data.
-    shards: Box<[CachePadded<Shard>]>,
-
-    /// The internal data.
-    value: UnsafeCell<T>,
-}
-
-unsafe impl<T: ?Sized + Send> Send for ShardedLock<T> {}
-unsafe impl<T: ?Sized + Send + Sync> Sync for ShardedLock<T> {}
-
-impl<T: ?Sized> UnwindSafe for ShardedLock<T> {}
-impl<T: ?Sized> RefUnwindSafe for ShardedLock<T> {}
-
-impl<T> ShardedLock<T> {
-    /// Creates a new sharded reader-writer lock.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use crossbeam_utils::sync::ShardedLock;
-    ///
-    /// let lock = ShardedLock::new(5);
-    /// ```
-    pub fn new(value: T) -> ShardedLock<T> {
-        ShardedLock {
-            shards: (0..NUM_SHARDS)
-                .map(|_| {
-                    CachePadded::new(Shard {
-                        lock: RwLock::new(()),
-                        write_guard: UnsafeCell::new(None),
-                    })
-                })
-                .collect::<Box<[_]>>(),
-            value: UnsafeCell::new(value),
-        }
-    }
-
-    /// Consumes this lock, returning the underlying data.
-    ///
-    /// # Errors
-    ///
-    /// This method will return an error if the lock is poisoned. A lock gets poisoned when a write
-    /// operation panics.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use crossbeam_utils::sync::ShardedLock;
-    ///
-    /// let lock = ShardedLock::new(String::new());
-    /// {
-    ///     let mut s = lock.write().unwrap();
-    ///     *s = "modified".to_owned();
-    /// }
-    /// assert_eq!(lock.into_inner().unwrap(), "modified");
-    /// ```
-    pub fn into_inner(self) -> LockResult<T> {
-        let is_poisoned = self.is_poisoned();
-        let inner = self.value.into_inner();
-
-        if is_poisoned {
-            Err(PoisonError::new(inner))
-        } else {
-            Ok(inner)
-        }
-    }
-}
-
-impl<T: ?Sized> ShardedLock<T> {
-    /// Returns `true` if the lock is poisoned.
-    ///
-    /// If another thread can still access the lock, it may become poisoned at any time. A `false`
-    /// result should not be trusted without additional synchronization.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use crossbeam_utils::sync::ShardedLock;
-    /// use std::sync::Arc;
-    /// use std::thread;
-    ///
-    /// let lock = Arc::new(ShardedLock::new(0));
-    /// let c_lock = lock.clone();
-    ///
-    /// let _ = thread::spawn(move || {
-    ///     let _lock = c_lock.write().unwrap();
-    ///     panic!(); // the lock gets poisoned
-    /// }).join();
-    /// assert_eq!(lock.is_poisoned(), true);
-    /// ```
-    pub fn is_poisoned(&self) -> bool {
-        self.shards[0].lock.is_poisoned()
-    }
-
-    /// Returns a mutable reference to the underlying data.
-    ///
-    /// Since this call borrows the lock mutably, no actual locking needs to take place.
-    ///
-    /// # Errors
-    ///
-    /// This method will return an error if the lock is poisoned. A lock gets poisoned when a write
-    /// operation panics.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use crossbeam_utils::sync::ShardedLock;
-    ///
-    /// let mut lock = ShardedLock::new(0);
-    /// *lock.get_mut().unwrap() = 10;
-    /// assert_eq!(*lock.read().unwrap(), 10);
-    /// ```
-    pub fn get_mut(&mut self) -> LockResult<&mut T> {
-        let is_poisoned = self.is_poisoned();
-        let inner = unsafe { &mut *self.value.get() };
-
-        if is_poisoned {
-            Err(PoisonError::new(inner))
-        } else {
-            Ok(inner)
-        }
-    }
-
-    /// Attempts to acquire this lock with shared read access.
-    ///
-    /// If the access could not be granted at this time, an error is returned. Otherwise, a guard
-    /// is returned which will release the shared access when it is dropped. This method does not
-    /// provide any guarantees with respect to the ordering of whether contentious readers or
-    /// writers will acquire the lock first.
-    ///
-    /// # Errors
-    ///
-    /// This method will return an error if the lock is poisoned. A lock gets poisoned when a write
-    /// operation panics.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use crossbeam_utils::sync::ShardedLock;
-    ///
-    /// let lock = ShardedLock::new(1);
-    ///
-    /// match lock.try_read() {
-    ///     Ok(n) => assert_eq!(*n, 1),
-    ///     Err(_) => unreachable!(),
-    /// };
-    /// ```
-    pub fn try_read(&self) -> TryLockResult<ShardedLockReadGuard<'_, T>> {
-        // Take the current thread index and map it to a shard index. Thread indices will tend to
-        // distribute shards among threads equally, thus reducing contention due to read-locking.
-        let current_index = current_index().unwrap_or(0);
-        let shard_index = current_index & (self.shards.len() - 1);
-
-        match self.shards[shard_index].lock.try_read() {
-            Ok(guard) => Ok(ShardedLockReadGuard {
-                lock: self,
-                _guard: guard,
-                _marker: PhantomData,
-            }),
-            Err(TryLockError::Poisoned(err)) => {
-                let guard = ShardedLockReadGuard {
-                    lock: self,
-                    _guard: err.into_inner(),
-                    _marker: PhantomData,
-                };
-                Err(TryLockError::Poisoned(PoisonError::new(guard)))
-            }
-            Err(TryLockError::WouldBlock) => Err(TryLockError::WouldBlock),
-        }
-    }
-
-    /// Locks with shared read access, blocking the current thread until it can be acquired.
-    ///
-    /// The calling thread will be blocked until there are no more writers which hold the lock.
-    /// There may be other readers currently inside the lock when this method returns. This method
-    /// does not provide any guarantees with respect to the ordering of whether contentious readers
-    /// or writers will acquire the lock first.
-    ///
-    /// Returns a guard which will release the shared access when dropped.
-    ///
-    /// # Errors
-    ///
-    /// This method will return an error if the lock is poisoned. A lock gets poisoned when a write
-    /// operation panics.
-    ///
-    /// # Panics
-    ///
-    /// This method might panic when called if the lock is already held by the current thread.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use crossbeam_utils::sync::ShardedLock;
-    /// use std::sync::Arc;
-    /// use std::thread;
-    ///
-    /// let lock = Arc::new(ShardedLock::new(1));
-    /// let c_lock = lock.clone();
-    ///
-    /// let n = lock.read().unwrap();
-    /// assert_eq!(*n, 1);
-    ///
-    /// thread::spawn(move || {
-    ///     let r = c_lock.read();
-    ///     assert!(r.is_ok());
-    /// }).join().unwrap();
-    /// ```
-    pub fn read(&self) -> LockResult<ShardedLockReadGuard<'_, T>> {
-        // Take the current thread index and map it to a shard index. Thread indices will tend to
-        // distribute shards among threads equally, thus reducing contention due to read-locking.
-        let current_index = current_index().unwrap_or(0);
-        let shard_index = current_index & (self.shards.len() - 1);
-
-        match self.shards[shard_index].lock.read() {
-            Ok(guard) => Ok(ShardedLockReadGuard {
-                lock: self,
-                _guard: guard,
-                _marker: PhantomData,
-            }),
-            Err(err) => Err(PoisonError::new(ShardedLockReadGuard {
-                lock: self,
-                _guard: err.into_inner(),
-                _marker: PhantomData,
-            })),
-        }
-    }
-
-    /// Attempts to acquire this lock with exclusive write access.
-    ///
-    /// If the access could not be granted at this time, an error is returned. Otherwise, a guard
-    /// is returned which will release the exclusive access when it is dropped. This method does
-    /// not provide any guarantees with respect to the ordering of whether contentious readers or
-    /// writers will acquire the lock first.
-    ///
-    /// # Errors
-    ///
-    /// This method will return an error if the lock is poisoned. A lock gets poisoned when a write
-    /// operation panics.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use crossbeam_utils::sync::ShardedLock;
-    ///
-    /// let lock = ShardedLock::new(1);
-    ///
-    /// let n = lock.read().unwrap();
-    /// assert_eq!(*n, 1);
-    ///
-    /// assert!(lock.try_write().is_err());
-    /// ```
-    pub fn try_write(&self) -> TryLockResult<ShardedLockWriteGuard<'_, T>> {
-        let mut poisoned = false;
-        let mut blocked = None;
-
-        // Write-lock each shard in succession.
-        for (i, shard) in self.shards.iter().enumerate() {
-            let guard = match shard.lock.try_write() {
-                Ok(guard) => guard,
-                Err(TryLockError::Poisoned(err)) => {
-                    poisoned = true;
-                    err.into_inner()
-                }
-                Err(TryLockError::WouldBlock) => {
-                    blocked = Some(i);
-                    break;
-                }
-            };
-
-            // Store the guard into the shard.
-            unsafe {
-                let guard: RwLockWriteGuard<'static, ()> = mem::transmute(guard);
-                let dest: *mut _ = shard.write_guard.get();
-                *dest = Some(guard);
-            }
-        }
-
-        if let Some(i) = blocked {
-            // Unlock the shards in reverse order of locking.
-            for shard in self.shards[0..i].iter().rev() {
-                unsafe {
-                    let dest: *mut _ = shard.write_guard.get();
-                    let guard = (*dest).take();
-                    drop(guard);
-                }
-            }
-            Err(TryLockError::WouldBlock)
-        } else if poisoned {
-            let guard = ShardedLockWriteGuard {
-                lock: self,
-                _marker: PhantomData,
-            };
-            Err(TryLockError::Poisoned(PoisonError::new(guard)))
-        } else {
-            Ok(ShardedLockWriteGuard {
-                lock: self,
-                _marker: PhantomData,
-            })
-        }
-    }
-
-    /// Locks with exclusive write access, blocking the current thread until it can be acquired.
-    ///
-    /// The calling thread will be blocked until there are no more writers which hold the lock.
-    /// There may be other readers currently inside the lock when this method returns. This method
-    /// does not provide any guarantees with respect to the ordering of whether contentious readers
-    /// or writers will acquire the lock first.
-    ///
-    /// Returns a guard which will release the exclusive access when dropped.
-    ///
-    /// # Errors
-    ///
-    /// This method will return an error if the lock is poisoned. A lock gets poisoned when a write
-    /// operation panics.
-    ///
-    /// # Panics
-    ///
-    /// This method might panic when called if the lock is already held by the current thread.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use crossbeam_utils::sync::ShardedLock;
-    ///
-    /// let lock = ShardedLock::new(1);
-    ///
-    /// let mut n = lock.write().unwrap();
-    /// *n = 2;
-    ///
-    /// assert!(lock.try_read().is_err());
-    /// ```
-    pub fn write(&self) -> LockResult<ShardedLockWriteGuard<'_, T>> {
-        let mut poisoned = false;
-
-        // Write-lock each shard in succession.
-        for shard in self.shards.iter() {
-            let guard = match shard.lock.write() {
-                Ok(guard) => guard,
-                Err(err) => {
-                    poisoned = true;
-                    err.into_inner()
-                }
-            };
-
-            // Store the guard into the shard.
-            unsafe {
-                let guard: RwLockWriteGuard<'_, ()> = guard;
-                let guard: RwLockWriteGuard<'static, ()> = mem::transmute(guard);
-                let dest: *mut _ = shard.write_guard.get();
-                *dest = Some(guard);
-            }
-        }
-
-        if poisoned {
-            Err(PoisonError::new(ShardedLockWriteGuard {
-                lock: self,
-                _marker: PhantomData,
-            }))
-        } else {
-            Ok(ShardedLockWriteGuard {
-                lock: self,
-                _marker: PhantomData,
-            })
-        }
-    }
-}
-
-impl<T: ?Sized + fmt::Debug> fmt::Debug for ShardedLock<T> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self.try_read() {
-            Ok(guard) => f
-                .debug_struct("ShardedLock")
-                .field("data", &&*guard)
-                .finish(),
-            Err(TryLockError::Poisoned(err)) => f
-                .debug_struct("ShardedLock")
-                .field("data", &&**err.get_ref())
-                .finish(),
-            Err(TryLockError::WouldBlock) => {
-                struct LockedPlaceholder;
-                impl fmt::Debug for LockedPlaceholder {
-                    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-                        f.write_str("<locked>")
-                    }
-                }
-                f.debug_struct("ShardedLock")
-                    .field("data", &LockedPlaceholder)
-                    .finish()
-            }
-        }
-    }
-}
-
-impl<T: Default> Default for ShardedLock<T> {
-    fn default() -> ShardedLock<T> {
-        ShardedLock::new(Default::default())
-    }
-}
-
-impl<T> From<T> for ShardedLock<T> {
-    fn from(t: T) -> Self {
-        ShardedLock::new(t)
-    }
-}
-
-/// A guard used to release the shared read access of a [`ShardedLock`] when dropped.
-#[clippy::has_significant_drop]
-pub struct ShardedLockReadGuard<'a, T: ?Sized> {
-    lock: &'a ShardedLock<T>,
-    _guard: RwLockReadGuard<'a, ()>,
-    _marker: PhantomData<RwLockReadGuard<'a, T>>,
-}
-
-unsafe impl<T: ?Sized + Sync> Sync for ShardedLockReadGuard<'_, T> {}
-
-impl<T: ?Sized> Deref for ShardedLockReadGuard<'_, T> {
-    type Target = T;
-
-    fn deref(&self) -> &T {
-        unsafe { &*self.lock.value.get() }
-    }
-}
-
-impl<T: fmt::Debug> fmt::Debug for ShardedLockReadGuard<'_, T> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct("ShardedLockReadGuard")
-            .field("lock", &self.lock)
-            .finish()
-    }
-}
-
-impl<T: ?Sized + fmt::Display> fmt::Display for ShardedLockReadGuard<'_, T> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        (**self).fmt(f)
-    }
-}
-
-/// A guard used to release the exclusive write access of a [`ShardedLock`] when dropped.
-#[clippy::has_significant_drop]
-pub struct ShardedLockWriteGuard<'a, T: ?Sized> {
-    lock: &'a ShardedLock<T>,
-    _marker: PhantomData<RwLockWriteGuard<'a, T>>,
-}
-
-unsafe impl<T: ?Sized + Sync> Sync for ShardedLockWriteGuard<'_, T> {}
-
-impl<T: ?Sized> Drop for ShardedLockWriteGuard<'_, T> {
-    fn drop(&mut self) {
-        // Unlock the shards in reverse order of locking.
-        for shard in self.lock.shards.iter().rev() {
-            unsafe {
-                let dest: *mut _ = shard.write_guard.get();
-                let guard = (*dest).take();
-                drop(guard);
-            }
-        }
-    }
-}
-
-impl<T: fmt::Debug> fmt::Debug for ShardedLockWriteGuard<'_, T> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct("ShardedLockWriteGuard")
-            .field("lock", &self.lock)
-            .finish()
-    }
-}
-
-impl<T: ?Sized + fmt::Display> fmt::Display for ShardedLockWriteGuard<'_, T> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        (**self).fmt(f)
-    }
-}
-
-impl<T: ?Sized> Deref for ShardedLockWriteGuard<'_, T> {
-    type Target = T;
-
-    fn deref(&self) -> &T {
-        unsafe { &*self.lock.value.get() }
-    }
-}
-
-impl<T: ?Sized> DerefMut for ShardedLockWriteGuard<'_, T> {
-    fn deref_mut(&mut self) -> &mut T {
-        unsafe { &mut *self.lock.value.get() }
-    }
-}
-
-/// Returns a `usize` that identifies the current thread.
-///
-/// Each thread is associated with an 'index'. While there are no particular guarantees, indices
-/// usually tend to be consecutive numbers between 0 and the number of running threads.
-///
-/// Since this function accesses TLS, `None` might be returned if the current thread's TLS is
-/// tearing down.
-#[inline]
-fn current_index() -> Option<usize> {
-    REGISTRATION.try_with(|reg| reg.index).ok()
-}
-
-/// The global registry keeping track of registered threads and indices.
-struct ThreadIndices {
-    /// Mapping from `ThreadId` to thread index.
-    mapping: HashMap<ThreadId, usize>,
-
-    /// A list of free indices.
-    free_list: Vec<usize>,
-
-    /// The next index to allocate if the free list is empty.
-    next_index: usize,
-}
-
-fn thread_indices() -> &'static Mutex<ThreadIndices> {
-    static THREAD_INDICES: OnceLock<Mutex<ThreadIndices>> = OnceLock::new();
-    fn init() -> Mutex<ThreadIndices> {
-        Mutex::new(ThreadIndices {
-            mapping: HashMap::new(),
-            free_list: Vec::new(),
-            next_index: 0,
-        })
-    }
-    THREAD_INDICES.get_or_init(init)
-}
-
-/// A registration of a thread with an index.
-///
-/// When dropped, unregisters the thread and frees the reserved index.
-struct Registration {
-    index: usize,
-    thread_id: ThreadId,
-}
-
-impl Drop for Registration {
-    fn drop(&mut self) {
-        let mut indices = thread_indices().lock().unwrap();
-        indices.mapping.remove(&self.thread_id);
-        indices.free_list.push(self.index);
-    }
-}
-
-std::thread_local! {
-    static REGISTRATION: Registration = {
-        let thread_id = thread::current().id();
-        let mut indices = thread_indices().lock().unwrap();
-
-        let index = match indices.free_list.pop() {
-            Some(i) => i,
-            None => {
-                let i = indices.next_index;
-                indices.next_index += 1;
-                i
-            }
-        };
-        indices.mapping.insert(thread_id, index);
-
-        Registration {
-            index,
-            thread_id,
-        }
-    };
-}
diff --git a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/src/sync/wait_group.rs b/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/src/sync/wait_group.rs
deleted file mode 100644
index 19d60741..0000000
--- a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/src/sync/wait_group.rs
+++ /dev/null
@@ -1,145 +0,0 @@
-use crate::primitive::sync::{Arc, Condvar, Mutex};
-use std::fmt;
-
-/// Enables threads to synchronize the beginning or end of some computation.
-///
-/// # Wait groups vs barriers
-///
-/// `WaitGroup` is very similar to [`Barrier`], but there are a few differences:
-///
-/// * [`Barrier`] needs to know the number of threads at construction, while `WaitGroup` is cloned to
-///   register more threads.
-///
-/// * A [`Barrier`] can be reused even after all threads have synchronized, while a `WaitGroup`
-///   synchronizes threads only once.
-///
-/// * All threads wait for others to reach the [`Barrier`]. With `WaitGroup`, each thread can choose
-///   to either wait for other threads or to continue without blocking.
-///
-/// # Examples
-///
-/// ```
-/// use crossbeam_utils::sync::WaitGroup;
-/// use std::thread;
-///
-/// // Create a new wait group.
-/// let wg = WaitGroup::new();
-///
-/// for _ in 0..4 {
-///     // Create another reference to the wait group.
-///     let wg = wg.clone();
-///
-///     thread::spawn(move || {
-///         // Do some work.
-///
-///         // Drop the reference to the wait group.
-///         drop(wg);
-///     });
-/// }
-///
-/// // Block until all threads have finished their work.
-/// wg.wait();
-/// # std::thread::sleep(std::time::Duration::from_millis(500)); // wait for background threads closed: https://github.com/rust-lang/miri/issues/1371
-/// ```
-///
-/// [`Barrier`]: std::sync::Barrier
-pub struct WaitGroup {
-    inner: Arc<Inner>,
-}
-
-/// Inner state of a `WaitGroup`.
-struct Inner {
-    cvar: Condvar,
-    count: Mutex<usize>,
-}
-
-impl Default for WaitGroup {
-    fn default() -> Self {
-        Self {
-            inner: Arc::new(Inner {
-                cvar: Condvar::new(),
-                count: Mutex::new(1),
-            }),
-        }
-    }
-}
-
-impl WaitGroup {
-    /// Creates a new wait group and returns the single reference to it.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use crossbeam_utils::sync::WaitGroup;
-    ///
-    /// let wg = WaitGroup::new();
-    /// ```
-    pub fn new() -> Self {
-        Self::default()
-    }
-
-    /// Drops this reference and waits until all other references are dropped.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use crossbeam_utils::sync::WaitGroup;
-    /// use std::thread;
-    ///
-    /// let wg = WaitGroup::new();
-    ///
-    /// thread::spawn({
-    ///     let wg = wg.clone();
-    ///     move || {
-    ///         // Block until both threads have reached `wait()`.
-    ///         wg.wait();
-    ///     }
-    /// });
-    ///
-    /// // Block until both threads have reached `wait()`.
-    /// wg.wait();
-    /// # std::thread::sleep(std::time::Duration::from_millis(500)); // wait for background threads closed: https://github.com/rust-lang/miri/issues/1371
-    /// ```
-    pub fn wait(self) {
-        if *self.inner.count.lock().unwrap() == 1 {
-            return;
-        }
-
-        let inner = self.inner.clone();
-        drop(self);
-
-        let mut count = inner.count.lock().unwrap();
-        while *count > 0 {
-            count = inner.cvar.wait(count).unwrap();
-        }
-    }
-}
-
-impl Drop for WaitGroup {
-    fn drop(&mut self) {
-        let mut count = self.inner.count.lock().unwrap();
-        *count -= 1;
-
-        if *count == 0 {
-            self.inner.cvar.notify_all();
-        }
-    }
-}
-
-impl Clone for WaitGroup {
-    fn clone(&self) -> WaitGroup {
-        let mut count = self.inner.count.lock().unwrap();
-        *count += 1;
-
-        WaitGroup {
-            inner: self.inner.clone(),
-        }
-    }
-}
-
-impl fmt::Debug for WaitGroup {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let count: &usize = &*self.inner.count.lock().unwrap();
-        f.debug_struct("WaitGroup").field("count", count).finish()
-    }
-}
diff --git a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/src/thread.rs b/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/src/thread.rs
deleted file mode 100644
index 847f4cf..0000000
--- a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/src/thread.rs
+++ /dev/null
@@ -1,611 +0,0 @@
-//! Threads that can borrow variables from the stack.
-//!
-//! Create a scope when spawned threads need to access variables on the stack:
-//!
-//! ```
-//! use crossbeam_utils::thread;
-//!
-//! let people = vec![
-//!     "Alice".to_string(),
-//!     "Bob".to_string(),
-//!     "Carol".to_string(),
-//! ];
-//!
-//! thread::scope(|s| {
-//!     for person in &people {
-//!         s.spawn(move |_| {
-//!             println!("Hello, {}!", person);
-//!         });
-//!     }
-//! }).unwrap();
-//! ```
-//!
-//! # Why scoped threads?
-//!
-//! Suppose we wanted to re-write the previous example using plain threads:
-//!
-//! ```compile_fail,E0597
-//! use std::thread;
-//!
-//! let people = vec![
-//!     "Alice".to_string(),
-//!     "Bob".to_string(),
-//!     "Carol".to_string(),
-//! ];
-//!
-//! let mut threads = Vec::new();
-//!
-//! for person in &people {
-//!     threads.push(thread::spawn(move || {
-//!         println!("Hello, {}!", person);
-//!     }));
-//! }
-//!
-//! for thread in threads {
-//!     thread.join().unwrap();
-//! }
-//! ```
-//!
-//! This doesn't work because the borrow checker complains about `people` not living long enough:
-//!
-//! ```text
-//! error[E0597]: `people` does not live long enough
-//!   --> src/main.rs:12:20
-//!    |
-//! 12 |     for person in &people {
-//!    |                    ^^^^^^ borrowed value does not live long enough
-//! ...
-//! 21 | }
-//!    | - borrowed value only lives until here
-//!    |
-//!    = note: borrowed value must be valid for the static lifetime...
-//! ```
-//!
-//! The problem here is that spawned threads are not allowed to borrow variables on stack because
-//! the compiler cannot prove they will be joined before `people` is destroyed.
-//!
-//! Scoped threads are a mechanism to guarantee to the compiler that spawned threads will be joined
-//! before the scope ends.
-//!
-//! # How scoped threads work
-//!
-//! If a variable is borrowed by a thread, the thread must complete before the variable is
-//! destroyed. Threads spawned using [`std::thread::spawn`] can only borrow variables with the
-//! `'static` lifetime because the borrow checker cannot be sure when the thread will complete.
-//!
-//! A scope creates a clear boundary between variables outside the scope and threads inside the
-//! scope. Whenever a scope spawns a thread, it promises to join the thread before the scope ends.
-//! This way we guarantee to the borrow checker that scoped threads only live within the scope and
-//! can safely access variables outside it.
-//!
-//! # Nesting scoped threads
-//!
-//! Sometimes scoped threads need to spawn more threads within the same scope. This is a little
-//! tricky because argument `s` lives *inside* the invocation of `thread::scope()` and as such
-//! cannot be borrowed by scoped threads:
-//!
-//! ```compile_fail,E0521
-//! use crossbeam_utils::thread;
-//!
-//! thread::scope(|s| {
-//!     s.spawn(|_| {
-//!         // Not going to compile because we're trying to borrow `s`,
-//!         // which lives *inside* the scope! :(
-//!         s.spawn(|_| println!("nested thread"));
-//!     });
-//! });
-//! ```
-//!
-//! Fortunately, there is a solution. Every scoped thread is passed a reference to its scope as an
-//! argument, which can be used for spawning nested threads:
-//!
-//! ```
-//! use crossbeam_utils::thread;
-//!
-//! thread::scope(|s| {
-//!     // Note the `|s|` here.
-//!     s.spawn(|s| {
-//!         // Yay, this works because we're using a fresh argument `s`! :)
-//!         s.spawn(|_| println!("nested thread"));
-//!     });
-//! }).unwrap();
-//! ```
-
-use std::boxed::Box;
-use std::fmt;
-use std::io;
-use std::marker::PhantomData;
-use std::mem;
-use std::panic;
-use std::string::String;
-use std::sync::{Arc, Mutex};
-use std::thread;
-use std::vec::Vec;
-
-use crate::sync::WaitGroup;
-
-type SharedVec<T> = Arc<Mutex<Vec<T>>>;
-type SharedOption<T> = Arc<Mutex<Option<T>>>;
-
-/// Creates a new scope for spawning threads.
-///
-/// All child threads that haven't been manually joined will be automatically joined just before
-/// this function invocation ends. If all joined threads have successfully completed, `Ok` is
-/// returned with the return value of `f`. If any of the joined threads has panicked, an `Err` is
-/// returned containing errors from panicked threads. Note that if panics are implemented by
-/// aborting the process, no error is returned; see the notes of [std::panic::catch_unwind].
-///
-/// **Note:** Since Rust 1.63, this function is soft-deprecated in favor of the more efficient [`std::thread::scope`].
-///
-/// # Examples
-///
-/// ```
-/// use crossbeam_utils::thread;
-///
-/// let var = vec![1, 2, 3];
-///
-/// thread::scope(|s| {
-///     s.spawn(|_| {
-///         println!("A child thread borrowing `var`: {:?}", var);
-///     });
-/// }).unwrap();
-/// ```
-pub fn scope<'env, F, R>(f: F) -> thread::Result<R>
-where
-    F: FnOnce(&Scope<'env>) -> R,
-{
-    struct AbortOnPanic;
-    impl Drop for AbortOnPanic {
-        fn drop(&mut self) {
-            if thread::panicking() {
-                std::process::abort();
-            }
-        }
-    }
-
-    let wg = WaitGroup::new();
-    let scope = Scope::<'env> {
-        handles: SharedVec::default(),
-        wait_group: wg.clone(),
-        _marker: PhantomData,
-    };
-
-    // Execute the scoped function, but catch any panics.
-    let result = panic::catch_unwind(panic::AssertUnwindSafe(|| f(&scope)));
-
-    // If an unwinding panic occurs before all threads are joined
-    // promote it to an aborting panic to prevent any threads from escaping the scope.
-    let guard = AbortOnPanic;
-
-    // Wait until all nested scopes are dropped.
-    drop(scope.wait_group);
-    wg.wait();
-
-    // Join all remaining spawned threads.
-    let panics: Vec<_> = scope
-        .handles
-        .lock()
-        .unwrap()
-        // Filter handles that haven't been joined, join them, and collect errors.
-        .drain(..)
-        .filter_map(|handle| handle.lock().unwrap().take())
-        .filter_map(|handle| handle.join().err())
-        .collect();
-
-    mem::forget(guard);
-
-    // If `f` has panicked, resume unwinding.
-    // If any of the child threads have panicked, return the panic errors.
-    // Otherwise, everything is OK and return the result of `f`.
-    match result {
-        Err(err) => panic::resume_unwind(err),
-        Ok(res) => {
-            if panics.is_empty() {
-                Ok(res)
-            } else {
-                Err(Box::new(panics))
-            }
-        }
-    }
-}
-
-/// A scope for spawning threads.
-pub struct Scope<'env> {
-    /// The list of the thread join handles.
-    handles: SharedVec<SharedOption<thread::JoinHandle<()>>>,
-
-    /// Used to wait until all subscopes all dropped.
-    wait_group: WaitGroup,
-
-    /// Borrows data with invariant lifetime `'env`.
-    _marker: PhantomData<&'env mut &'env ()>,
-}
-
-unsafe impl Sync for Scope<'_> {}
-
-impl<'env> Scope<'env> {
-    /// Spawns a scoped thread.
-    ///
-    /// This method is similar to the [`spawn`] function in Rust's standard library. The difference
-    /// is that this thread is scoped, meaning it's guaranteed to terminate before the scope exits,
-    /// allowing it to reference variables outside the scope.
-    ///
-    /// The scoped thread is passed a reference to this scope as an argument, which can be used for
-    /// spawning nested threads.
-    ///
-    /// The returned [handle](ScopedJoinHandle) can be used to manually
-    /// [join](ScopedJoinHandle::join) the thread before the scope exits.
-    ///
-    /// This will create a thread using default parameters of [`ScopedThreadBuilder`], if you want to specify the
-    /// stack size or the name of the thread, use this API instead.
-    ///
-    /// [`spawn`]: std::thread::spawn
-    ///
-    /// # Panics
-    ///
-    /// Panics if the OS fails to create a thread; use [`ScopedThreadBuilder::spawn`]
-    /// to recover from such errors.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use crossbeam_utils::thread;
-    ///
-    /// thread::scope(|s| {
-    ///     let handle = s.spawn(|_| {
-    ///         println!("A child thread is running");
-    ///         42
-    ///     });
-    ///
-    ///     // Join the thread and retrieve its result.
-    ///     let res = handle.join().unwrap();
-    ///     assert_eq!(res, 42);
-    /// }).unwrap();
-    /// ```
-    pub fn spawn<'scope, F, T>(&'scope self, f: F) -> ScopedJoinHandle<'scope, T>
-    where
-        F: FnOnce(&Scope<'env>) -> T,
-        F: Send + 'env,
-        T: Send + 'env,
-    {
-        self.builder()
-            .spawn(f)
-            .expect("failed to spawn scoped thread")
-    }
-
-    /// Creates a builder that can configure a thread before spawning.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use crossbeam_utils::thread;
-    ///
-    /// thread::scope(|s| {
-    ///     s.builder()
-    ///         .spawn(|_| println!("A child thread is running"))
-    ///         .unwrap();
-    /// }).unwrap();
-    /// ```
-    pub fn builder<'scope>(&'scope self) -> ScopedThreadBuilder<'scope, 'env> {
-        ScopedThreadBuilder {
-            scope: self,
-            builder: thread::Builder::new(),
-        }
-    }
-}
-
-impl fmt::Debug for Scope<'_> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.pad("Scope { .. }")
-    }
-}
-
-/// Configures the properties of a new thread.
-///
-/// The two configurable properties are:
-///
-/// - [`name`]: Specifies an [associated name for the thread][naming-threads].
-/// - [`stack_size`]: Specifies the [desired stack size for the thread][stack-size].
-///
-/// The [`spawn`] method will take ownership of the builder and return an [`io::Result`] of the
-/// thread handle with the given configuration.
-///
-/// The [`Scope::spawn`] method uses a builder with default configuration and unwraps its return
-/// value. You may want to use this builder when you want to recover from a failure to launch a
-/// thread.
-///
-/// # Examples
-///
-/// ```
-/// use crossbeam_utils::thread;
-///
-/// thread::scope(|s| {
-///     s.builder()
-///         .spawn(|_| println!("Running a child thread"))
-///         .unwrap();
-/// }).unwrap();
-/// ```
-///
-/// [`name`]: ScopedThreadBuilder::name
-/// [`stack_size`]: ScopedThreadBuilder::stack_size
-/// [`spawn`]: ScopedThreadBuilder::spawn
-/// [`io::Result`]: std::io::Result
-/// [naming-threads]: std::thread#naming-threads
-/// [stack-size]: std::thread#stack-size
-#[derive(Debug)]
-pub struct ScopedThreadBuilder<'scope, 'env> {
-    scope: &'scope Scope<'env>,
-    builder: thread::Builder,
-}
-
-impl<'scope, 'env> ScopedThreadBuilder<'scope, 'env> {
-    /// Sets the name for the new thread.
-    ///
-    /// The name must not contain null bytes (`\0`).
-    ///
-    /// For more information about named threads, see [here][naming-threads].
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use crossbeam_utils::thread;
-    /// use std::thread::current;
-    ///
-    /// thread::scope(|s| {
-    ///     s.builder()
-    ///         .name("my thread".to_string())
-    ///         .spawn(|_| assert_eq!(current().name(), Some("my thread")))
-    ///         .unwrap();
-    /// }).unwrap();
-    /// ```
-    ///
-    /// [naming-threads]: std::thread#naming-threads
-    pub fn name(mut self, name: String) -> ScopedThreadBuilder<'scope, 'env> {
-        self.builder = self.builder.name(name);
-        self
-    }
-
-    /// Sets the size of the stack for the new thread.
-    ///
-    /// The stack size is measured in bytes.
-    ///
-    /// For more information about the stack size for threads, see [here][stack-size].
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use crossbeam_utils::thread;
-    ///
-    /// thread::scope(|s| {
-    ///     s.builder()
-    ///         .stack_size(32 * 1024)
-    ///         .spawn(|_| println!("Running a child thread"))
-    ///         .unwrap();
-    /// }).unwrap();
-    /// ```
-    ///
-    /// [stack-size]: std::thread#stack-size
-    pub fn stack_size(mut self, size: usize) -> ScopedThreadBuilder<'scope, 'env> {
-        self.builder = self.builder.stack_size(size);
-        self
-    }
-
-    /// Spawns a scoped thread with this configuration.
-    ///
-    /// The scoped thread is passed a reference to this scope as an argument, which can be used for
-    /// spawning nested threads.
-    ///
-    /// The returned handle can be used to manually join the thread before the scope exits.
-    ///
-    /// # Errors
-    ///
-    /// Unlike the [`Scope::spawn`] method, this method yields an
-    /// [`io::Result`] to capture any failure to create the thread at
-    /// the OS level.
-    ///
-    /// [`io::Result`]: std::io::Result
-    ///
-    /// # Panics
-    ///
-    /// Panics if a thread name was set and it contained null bytes.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use crossbeam_utils::thread;
-    ///
-    /// thread::scope(|s| {
-    ///     let handle = s.builder()
-    ///         .spawn(|_| {
-    ///             println!("A child thread is running");
-    ///             42
-    ///         })
-    ///         .unwrap();
-    ///
-    ///     // Join the thread and retrieve its result.
-    ///     let res = handle.join().unwrap();
-    ///     assert_eq!(res, 42);
-    /// }).unwrap();
-    /// ```
-    pub fn spawn<F, T>(self, f: F) -> io::Result<ScopedJoinHandle<'scope, T>>
-    where
-        F: FnOnce(&Scope<'env>) -> T,
-        F: Send + 'env,
-        T: Send + 'env,
-    {
-        // The result of `f` will be stored here.
-        let result = SharedOption::default();
-
-        // Spawn the thread and grab its join handle and thread handle.
-        let (handle, thread) = {
-            let result = Arc::clone(&result);
-
-            // A clone of the scope that will be moved into the new thread.
-            let scope = Scope::<'env> {
-                handles: Arc::clone(&self.scope.handles),
-                wait_group: self.scope.wait_group.clone(),
-                _marker: PhantomData,
-            };
-
-            // Spawn the thread.
-            let handle = {
-                let closure = move || {
-                    // Make sure the scope is inside the closure with the proper `'env` lifetime.
-                    let scope: Scope<'env> = scope;
-
-                    // Run the closure.
-                    let res = f(&scope);
-
-                    // Store the result if the closure didn't panic.
-                    *result.lock().unwrap() = Some(res);
-                };
-
-                // Allocate `closure` on the heap and erase the `'env` bound.
-                let closure: Box<dyn FnOnce() + Send + 'env> = Box::new(closure);
-                let closure: Box<dyn FnOnce() + Send + 'static> =
-                    unsafe { mem::transmute(closure) };
-
-                // Finally, spawn the closure.
-                self.builder.spawn(closure)?
-            };
-
-            let thread = handle.thread().clone();
-            let handle = Arc::new(Mutex::new(Some(handle)));
-            (handle, thread)
-        };
-
-        // Add the handle to the shared list of join handles.
-        self.scope.handles.lock().unwrap().push(Arc::clone(&handle));
-
-        Ok(ScopedJoinHandle {
-            handle,
-            result,
-            thread,
-            _marker: PhantomData,
-        })
-    }
-}
-
-unsafe impl<T> Send for ScopedJoinHandle<'_, T> {}
-unsafe impl<T> Sync for ScopedJoinHandle<'_, T> {}
-
-/// A handle that can be used to join its scoped thread.
-///
-/// This struct is created by the [`Scope::spawn`] method and the
-/// [`ScopedThreadBuilder::spawn`] method.
-pub struct ScopedJoinHandle<'scope, T> {
-    /// A join handle to the spawned thread.
-    handle: SharedOption<thread::JoinHandle<()>>,
-
-    /// Holds the result of the inner closure.
-    result: SharedOption<T>,
-
-    /// A handle to the spawned thread.
-    thread: thread::Thread,
-
-    /// Borrows the parent scope with lifetime `'scope`.
-    _marker: PhantomData<&'scope ()>,
-}
-
-impl<T> ScopedJoinHandle<'_, T> {
-    /// Waits for the thread to finish and returns its result.
-    ///
-    /// If the child thread panics, an error is returned. Note that if panics are implemented by
-    /// aborting the process, no error is returned; see the notes of [std::panic::catch_unwind].
-    ///
-    /// # Panics
-    ///
-    /// This function may panic on some platforms if a thread attempts to join itself or otherwise
-    /// may create a deadlock with joining threads.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use crossbeam_utils::thread;
-    ///
-    /// thread::scope(|s| {
-    ///     let handle1 = s.spawn(|_| println!("I'm a happy thread :)"));
-    ///     let handle2 = s.spawn(|_| panic!("I'm a sad thread :("));
-    ///
-    ///     // Join the first thread and verify that it succeeded.
-    ///     let res = handle1.join();
-    ///     assert!(res.is_ok());
-    ///
-    ///     // Join the second thread and verify that it panicked.
-    ///     let res = handle2.join();
-    ///     assert!(res.is_err());
-    /// }).unwrap();
-    /// ```
-    pub fn join(self) -> thread::Result<T> {
-        // Take out the handle. The handle will surely be available because the root scope waits
-        // for nested scopes before joining remaining threads.
-        let handle = self.handle.lock().unwrap().take().unwrap();
-
-        // Join the thread and then take the result out of its inner closure.
-        handle
-            .join()
-            .map(|()| self.result.lock().unwrap().take().unwrap())
-    }
-
-    /// Returns a handle to the underlying thread.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use crossbeam_utils::thread;
-    ///
-    /// thread::scope(|s| {
-    ///     let handle = s.spawn(|_| println!("A child thread is running"));
-    ///     println!("The child thread ID: {:?}", handle.thread().id());
-    /// }).unwrap();
-    /// ```
-    pub fn thread(&self) -> &thread::Thread {
-        &self.thread
-    }
-}
-
-/// Unix-specific extensions.
-#[cfg(unix)]
-mod unix {
-    use super::ScopedJoinHandle;
-    use std::os::unix::thread::{JoinHandleExt, RawPthread};
-
-    impl<T> JoinHandleExt for ScopedJoinHandle<'_, T> {
-        fn as_pthread_t(&self) -> RawPthread {
-            // Borrow the handle. The handle will surely be available because the root scope waits
-            // for nested scopes before joining remaining threads.
-            let handle = self.handle.lock().unwrap();
-            handle.as_ref().unwrap().as_pthread_t()
-        }
-        fn into_pthread_t(self) -> RawPthread {
-            self.as_pthread_t()
-        }
-    }
-}
-/// Windows-specific extensions.
-#[cfg(windows)]
-mod windows {
-    use super::ScopedJoinHandle;
-    use std::os::windows::io::{AsRawHandle, IntoRawHandle, RawHandle};
-
-    impl<T> AsRawHandle for ScopedJoinHandle<'_, T> {
-        fn as_raw_handle(&self) -> RawHandle {
-            // Borrow the handle. The handle will surely be available because the root scope waits
-            // for nested scopes before joining remaining threads.
-            let handle = self.handle.lock().unwrap();
-            handle.as_ref().unwrap().as_raw_handle()
-        }
-    }
-
-    impl<T> IntoRawHandle for ScopedJoinHandle<'_, T> {
-        fn into_raw_handle(self) -> RawHandle {
-            self.as_raw_handle()
-        }
-    }
-}
-
-impl<T> fmt::Debug for ScopedJoinHandle<'_, T> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.pad("ScopedJoinHandle { .. }")
-    }
-}
diff --git a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/tests/atomic_cell.rs b/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/tests/atomic_cell.rs
deleted file mode 100644
index 9fe6932..0000000
--- a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/tests/atomic_cell.rs
+++ /dev/null
@@ -1,374 +0,0 @@
-use std::mem;
-use std::sync::atomic::AtomicUsize;
-use std::sync::atomic::Ordering::SeqCst;
-
-use crossbeam_utils::atomic::AtomicCell;
-
-#[test]
-fn is_lock_free() {
-    struct UsizeWrap(#[allow(dead_code)] usize);
-    struct U8Wrap(#[allow(dead_code)] bool);
-    struct I16Wrap(#[allow(dead_code)] i16);
-    #[repr(align(8))]
-    struct U64Align8(#[allow(dead_code)] u64);
-
-    assert!(AtomicCell::<usize>::is_lock_free());
-    assert!(AtomicCell::<isize>::is_lock_free());
-    assert!(AtomicCell::<UsizeWrap>::is_lock_free());
-
-    assert!(AtomicCell::<()>::is_lock_free());
-
-    assert!(AtomicCell::<u8>::is_lock_free());
-    assert!(AtomicCell::<i8>::is_lock_free());
-    assert!(AtomicCell::<bool>::is_lock_free());
-    assert!(AtomicCell::<U8Wrap>::is_lock_free());
-
-    assert!(AtomicCell::<u16>::is_lock_free());
-    assert!(AtomicCell::<i16>::is_lock_free());
-    assert!(AtomicCell::<I16Wrap>::is_lock_free());
-
-    assert!(AtomicCell::<u32>::is_lock_free());
-    assert!(AtomicCell::<i32>::is_lock_free());
-
-    // Sizes of both types must be equal, and the alignment of `u64` must be greater or equal than
-    // that of `AtomicU64`. In i686-unknown-linux-gnu, the alignment of `u64` is `4` and alignment
-    // of `AtomicU64` is `8`, so `AtomicCell<u64>` is not lock-free.
-    assert_eq!(
-        AtomicCell::<u64>::is_lock_free(),
-        cfg!(target_has_atomic = "64") && std::mem::align_of::<u64>() == 8
-    );
-    assert_eq!(mem::size_of::<U64Align8>(), 8);
-    assert_eq!(mem::align_of::<U64Align8>(), 8);
-    assert_eq!(
-        AtomicCell::<U64Align8>::is_lock_free(),
-        cfg!(target_has_atomic = "64")
-    );
-
-    // AtomicU128 is unstable
-    assert!(!AtomicCell::<u128>::is_lock_free());
-}
-
-#[test]
-fn const_is_lock_free() {
-    const _U: bool = AtomicCell::<usize>::is_lock_free();
-    const _I: bool = AtomicCell::<isize>::is_lock_free();
-}
-
-#[test]
-fn drops_unit() {
-    static CNT: AtomicUsize = AtomicUsize::new(0);
-    CNT.store(0, SeqCst);
-
-    #[derive(Debug, PartialEq, Eq)]
-    struct Foo();
-
-    impl Foo {
-        fn new() -> Foo {
-            CNT.fetch_add(1, SeqCst);
-            Foo()
-        }
-    }
-
-    impl Drop for Foo {
-        fn drop(&mut self) {
-            CNT.fetch_sub(1, SeqCst);
-        }
-    }
-
-    impl Default for Foo {
-        fn default() -> Foo {
-            Foo::new()
-        }
-    }
-
-    let a = AtomicCell::new(Foo::new());
-
-    assert_eq!(a.swap(Foo::new()), Foo::new());
-    assert_eq!(CNT.load(SeqCst), 1);
-
-    a.store(Foo::new());
-    assert_eq!(CNT.load(SeqCst), 1);
-
-    assert_eq!(a.swap(Foo::default()), Foo::new());
-    assert_eq!(CNT.load(SeqCst), 1);
-
-    drop(a);
-    assert_eq!(CNT.load(SeqCst), 0);
-}
-
-#[test]
-fn drops_u8() {
-    static CNT: AtomicUsize = AtomicUsize::new(0);
-    CNT.store(0, SeqCst);
-
-    #[derive(Debug, PartialEq, Eq)]
-    struct Foo(u8);
-
-    impl Foo {
-        fn new(val: u8) -> Foo {
-            CNT.fetch_add(1, SeqCst);
-            Foo(val)
-        }
-    }
-
-    impl Drop for Foo {
-        fn drop(&mut self) {
-            CNT.fetch_sub(1, SeqCst);
-        }
-    }
-
-    impl Default for Foo {
-        fn default() -> Foo {
-            Foo::new(0)
-        }
-    }
-
-    let a = AtomicCell::new(Foo::new(5));
-
-    assert_eq!(a.swap(Foo::new(6)), Foo::new(5));
-    assert_eq!(a.swap(Foo::new(1)), Foo::new(6));
-    assert_eq!(CNT.load(SeqCst), 1);
-
-    a.store(Foo::new(2));
-    assert_eq!(CNT.load(SeqCst), 1);
-
-    assert_eq!(a.swap(Foo::default()), Foo::new(2));
-    assert_eq!(CNT.load(SeqCst), 1);
-
-    assert_eq!(a.swap(Foo::default()), Foo::new(0));
-    assert_eq!(CNT.load(SeqCst), 1);
-
-    drop(a);
-    assert_eq!(CNT.load(SeqCst), 0);
-}
-
-#[test]
-fn drops_usize() {
-    static CNT: AtomicUsize = AtomicUsize::new(0);
-    CNT.store(0, SeqCst);
-
-    #[derive(Debug, PartialEq, Eq)]
-    struct Foo(usize);
-
-    impl Foo {
-        fn new(val: usize) -> Foo {
-            CNT.fetch_add(1, SeqCst);
-            Foo(val)
-        }
-    }
-
-    impl Drop for Foo {
-        fn drop(&mut self) {
-            CNT.fetch_sub(1, SeqCst);
-        }
-    }
-
-    impl Default for Foo {
-        fn default() -> Foo {
-            Foo::new(0)
-        }
-    }
-
-    let a = AtomicCell::new(Foo::new(5));
-
-    assert_eq!(a.swap(Foo::new(6)), Foo::new(5));
-    assert_eq!(a.swap(Foo::new(1)), Foo::new(6));
-    assert_eq!(CNT.load(SeqCst), 1);
-
-    a.store(Foo::new(2));
-    assert_eq!(CNT.load(SeqCst), 1);
-
-    assert_eq!(a.swap(Foo::default()), Foo::new(2));
-    assert_eq!(CNT.load(SeqCst), 1);
-
-    assert_eq!(a.swap(Foo::default()), Foo::new(0));
-    assert_eq!(CNT.load(SeqCst), 1);
-
-    drop(a);
-    assert_eq!(CNT.load(SeqCst), 0);
-}
-
-#[test]
-fn modular_u8() {
-    #[derive(Clone, Copy, Eq, Debug, Default)]
-    struct Foo(u8);
-
-    impl PartialEq for Foo {
-        fn eq(&self, other: &Foo) -> bool {
-            self.0 % 5 == other.0 % 5
-        }
-    }
-
-    let a = AtomicCell::new(Foo(1));
-
-    assert_eq!(a.load(), Foo(1));
-    assert_eq!(a.swap(Foo(2)), Foo(11));
-    assert_eq!(a.load(), Foo(52));
-
-    a.store(Foo(0));
-    assert_eq!(a.compare_exchange(Foo(0), Foo(5)), Ok(Foo(100)));
-    assert_eq!(a.load().0, 5);
-    assert_eq!(a.compare_exchange(Foo(10), Foo(15)), Ok(Foo(100)));
-    assert_eq!(a.load().0, 15);
-}
-
-#[test]
-fn modular_usize() {
-    #[derive(Clone, Copy, Eq, Debug, Default)]
-    struct Foo(usize);
-
-    impl PartialEq for Foo {
-        fn eq(&self, other: &Foo) -> bool {
-            self.0 % 5 == other.0 % 5
-        }
-    }
-
-    let a = AtomicCell::new(Foo(1));
-
-    assert_eq!(a.load(), Foo(1));
-    assert_eq!(a.swap(Foo(2)), Foo(11));
-    assert_eq!(a.load(), Foo(52));
-
-    a.store(Foo(0));
-    assert_eq!(a.compare_exchange(Foo(0), Foo(5)), Ok(Foo(100)));
-    assert_eq!(a.load().0, 5);
-    assert_eq!(a.compare_exchange(Foo(10), Foo(15)), Ok(Foo(100)));
-    assert_eq!(a.load().0, 15);
-}
-
-#[test]
-fn garbage_padding() {
-    #[derive(Copy, Clone, Eq, PartialEq)]
-    struct Object {
-        a: i64,
-        b: i32,
-    }
-
-    let cell = AtomicCell::new(Object { a: 0, b: 0 });
-    let _garbage = [0xfe, 0xfe, 0xfe, 0xfe, 0xfe]; // Needed
-    let next = Object { a: 0, b: 0 };
-
-    let prev = cell.load();
-    assert!(cell.compare_exchange(prev, next).is_ok());
-    println!();
-}
-
-#[test]
-fn const_atomic_cell_new() {
-    static CELL: AtomicCell<usize> = AtomicCell::new(0);
-
-    CELL.store(1);
-    assert_eq!(CELL.load(), 1);
-}
-
-// https://github.com/crossbeam-rs/crossbeam/pull/767
-macro_rules! test_arithmetic {
-    ($test_name:ident, $ty:ident) => {
-        #[test]
-        fn $test_name() {
-            let a: AtomicCell<$ty> = AtomicCell::new(7);
-
-            assert_eq!(a.fetch_add(3), 7);
-            assert_eq!(a.load(), 10);
-
-            assert_eq!(a.fetch_sub(3), 10);
-            assert_eq!(a.load(), 7);
-
-            assert_eq!(a.fetch_and(3), 7);
-            assert_eq!(a.load(), 3);
-
-            assert_eq!(a.fetch_or(16), 3);
-            assert_eq!(a.load(), 19);
-
-            assert_eq!(a.fetch_xor(2), 19);
-            assert_eq!(a.load(), 17);
-
-            assert_eq!(a.fetch_max(18), 17);
-            assert_eq!(a.load(), 18);
-
-            assert_eq!(a.fetch_min(17), 18);
-            assert_eq!(a.load(), 17);
-
-            assert_eq!(a.fetch_nand(7), 17);
-            assert_eq!(a.load(), !(17 & 7));
-        }
-    };
-}
-test_arithmetic!(arithmetic_u8, u8);
-test_arithmetic!(arithmetic_i8, i8);
-test_arithmetic!(arithmetic_u16, u16);
-test_arithmetic!(arithmetic_i16, i16);
-test_arithmetic!(arithmetic_u32, u32);
-test_arithmetic!(arithmetic_i32, i32);
-test_arithmetic!(arithmetic_u64, u64);
-test_arithmetic!(arithmetic_i64, i64);
-test_arithmetic!(arithmetic_u128, u128);
-test_arithmetic!(arithmetic_i128, i128);
-
-// https://github.com/crossbeam-rs/crossbeam/issues/748
-#[cfg_attr(miri, ignore)] // TODO
-#[test]
-fn issue_748() {
-    #[allow(dead_code)]
-    #[repr(align(8))]
-    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
-    enum Test {
-        Field(u32),
-        FieldLess,
-    }
-
-    assert_eq!(mem::size_of::<Test>(), 8);
-    assert_eq!(
-        AtomicCell::<Test>::is_lock_free(),
-        cfg!(target_has_atomic = "64")
-    );
-    let x = AtomicCell::new(Test::FieldLess);
-    assert_eq!(x.load(), Test::FieldLess);
-}
-
-// https://github.com/crossbeam-rs/crossbeam/issues/833
-#[test]
-fn issue_833() {
-    use std::num::NonZeroU128;
-    use std::sync::atomic::{AtomicBool, Ordering};
-    use std::thread;
-
-    #[cfg(miri)]
-    const N: usize = 10_000;
-    #[cfg(not(miri))]
-    const N: usize = 1_000_000;
-
-    #[allow(dead_code)]
-    enum Enum {
-        NeverConstructed,
-        Cell(AtomicCell<NonZeroU128>),
-    }
-
-    static STATIC: Enum = Enum::Cell(AtomicCell::new(match NonZeroU128::new(1) {
-        Some(nonzero) => nonzero,
-        None => unreachable!(),
-    }));
-    static FINISHED: AtomicBool = AtomicBool::new(false);
-
-    let handle = thread::spawn(|| {
-        let cell = match &STATIC {
-            Enum::NeverConstructed => unreachable!(),
-            Enum::Cell(cell) => cell,
-        };
-        let x = NonZeroU128::new(0xFFFF_FFFF_FFFF_FFFF_0000_0000_0000_0000).unwrap();
-        let y = NonZeroU128::new(0x0000_0000_0000_0000_FFFF_FFFF_FFFF_FFFF).unwrap();
-        while !FINISHED.load(Ordering::Relaxed) {
-            cell.store(x);
-            cell.store(y);
-        }
-    });
-
-    for _ in 0..N {
-        if let Enum::NeverConstructed = STATIC {
-            unreachable!(":(");
-        }
-    }
-
-    FINISHED.store(true, Ordering::Relaxed);
-    handle.join().unwrap();
-}
diff --git a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/tests/cache_padded.rs b/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/tests/cache_padded.rs
deleted file mode 100644
index 86e9a77..0000000
--- a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/tests/cache_padded.rs
+++ /dev/null
@@ -1,113 +0,0 @@
-use std::cell::Cell;
-use std::mem;
-
-use crossbeam_utils::CachePadded;
-
-#[test]
-fn default() {
-    let x: CachePadded<u64> = Default::default();
-    assert_eq!(*x, 0);
-}
-
-#[test]
-fn store_u64() {
-    let x: CachePadded<u64> = CachePadded::new(17);
-    assert_eq!(*x, 17);
-}
-
-#[test]
-fn store_pair() {
-    let x: CachePadded<(u64, u64)> = CachePadded::new((17, 37));
-    assert_eq!(x.0, 17);
-    assert_eq!(x.1, 37);
-}
-
-#[test]
-fn distance() {
-    let arr = [CachePadded::new(17u8), CachePadded::new(37u8)];
-    let a = &*arr[0] as *const u8;
-    let b = &*arr[1] as *const u8;
-    let align = mem::align_of::<CachePadded<()>>();
-    assert!(align >= 32);
-    assert_eq!(unsafe { a.add(align) }, b);
-}
-
-#[test]
-fn different_sizes() {
-    CachePadded::new(17u8);
-    CachePadded::new(17u16);
-    CachePadded::new(17u32);
-    CachePadded::new([17u64; 0]);
-    CachePadded::new([17u64; 1]);
-    CachePadded::new([17u64; 2]);
-    CachePadded::new([17u64; 3]);
-    CachePadded::new([17u64; 4]);
-    CachePadded::new([17u64; 5]);
-    CachePadded::new([17u64; 6]);
-    CachePadded::new([17u64; 7]);
-    CachePadded::new([17u64; 8]);
-}
-
-#[test]
-fn large() {
-    let a = [17u64; 9];
-    let b = CachePadded::new(a);
-    assert!(mem::size_of_val(&a) <= mem::size_of_val(&b));
-}
-
-#[test]
-fn debug() {
-    assert_eq!(
-        format!("{:?}", CachePadded::new(17u64)),
-        "CachePadded { value: 17 }"
-    );
-}
-
-#[test]
-fn drops() {
-    let count = Cell::new(0);
-
-    struct Foo<'a>(&'a Cell<usize>);
-
-    impl<'a> Drop for Foo<'a> {
-        fn drop(&mut self) {
-            self.0.set(self.0.get() + 1);
-        }
-    }
-
-    let a = CachePadded::new(Foo(&count));
-    let b = CachePadded::new(Foo(&count));
-
-    assert_eq!(count.get(), 0);
-    drop(a);
-    assert_eq!(count.get(), 1);
-    drop(b);
-    assert_eq!(count.get(), 2);
-}
-
-#[allow(clippy::clone_on_copy)] // This is intentional.
-#[test]
-fn clone() {
-    let a = CachePadded::new(17);
-    let b = a.clone();
-    assert_eq!(*a, *b);
-}
-
-#[test]
-fn runs_custom_clone() {
-    let count = Cell::new(0);
-
-    struct Foo<'a>(&'a Cell<usize>);
-
-    impl<'a> Clone for Foo<'a> {
-        fn clone(&self) -> Foo<'a> {
-            self.0.set(self.0.get() + 1);
-            Foo::<'a>(self.0)
-        }
-    }
-
-    let a = CachePadded::new(Foo(&count));
-    let _ = a.clone();
-
-    assert_eq!(count.get(), 1);
-}
diff --git a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/tests/parker.rs b/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/tests/parker.rs
deleted file mode 100644
index 2bf9c37d..0000000
--- a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/tests/parker.rs
+++ /dev/null
@@ -1,41 +0,0 @@
-use std::thread::sleep;
-use std::time::Duration;
-use std::u32;
-
-use crossbeam_utils::sync::Parker;
-use crossbeam_utils::thread;
-
-#[test]
-fn park_timeout_unpark_before() {
-    let p = Parker::new();
-    for _ in 0..10 {
-        p.unparker().unpark();
-        p.park_timeout(Duration::from_millis(u32::MAX as u64));
-    }
-}
-
-#[test]
-fn park_timeout_unpark_not_called() {
-    let p = Parker::new();
-    for _ in 0..10 {
-        p.park_timeout(Duration::from_millis(10))
-    }
-}
-
-#[test]
-fn park_timeout_unpark_called_other_thread() {
-    for _ in 0..10 {
-        let p = Parker::new();
-        let u = p.unparker().clone();
-
-        thread::scope(|scope| {
-            scope.spawn(move |_| {
-                sleep(Duration::from_millis(50));
-                u.unpark();
-            });
-
-            p.park_timeout(Duration::from_millis(u32::MAX as u64))
-        })
-        .unwrap();
-    }
-}
diff --git a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/tests/sharded_lock.rs b/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/tests/sharded_lock.rs
deleted file mode 100644
index 002f7f5..0000000
--- a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/tests/sharded_lock.rs
+++ /dev/null
@@ -1,252 +0,0 @@
-use std::sync::atomic::{AtomicUsize, Ordering};
-use std::sync::mpsc::channel;
-use std::sync::{Arc, TryLockError};
-use std::thread;
-
-use crossbeam_utils::sync::ShardedLock;
-use rand::Rng;
-
-#[derive(Eq, PartialEq, Debug)]
-struct NonCopy(i32);
-
-#[test]
-fn smoke() {
-    let l = ShardedLock::new(());
-    drop(l.read().unwrap());
-    drop(l.write().unwrap());
-    drop((l.read().unwrap(), l.read().unwrap()));
-    drop(l.write().unwrap());
-}
-
-#[test]
-fn frob() {
-    const N: u32 = 10;
-    #[cfg(miri)]
-    const M: usize = 50;
-    #[cfg(not(miri))]
-    const M: usize = 1000;
-
-    let r = Arc::new(ShardedLock::new(()));
-
-    let (tx, rx) = channel::<()>();
-    for _ in 0..N {
-        let tx = tx.clone();
-        let r = r.clone();
-        thread::spawn(move || {
-            let mut rng = rand::thread_rng();
-            for _ in 0..M {
-                if rng.gen_bool(1.0 / (N as f64)) {
-                    drop(r.write().unwrap());
-                } else {
-                    drop(r.read().unwrap());
-                }
-            }
-            drop(tx);
-        });
-    }
-    drop(tx);
-    let _ = rx.recv();
-}
-
-#[test]
-fn arc_poison_wr() {
-    let arc = Arc::new(ShardedLock::new(1));
-    let arc2 = arc.clone();
-    let _: Result<(), _> = thread::spawn(move || {
-        let _lock = arc2.write().unwrap();
-        panic!();
-    })
-    .join();
-    assert!(arc.read().is_err());
-}
-
-#[test]
-fn arc_poison_ww() {
-    let arc = Arc::new(ShardedLock::new(1));
-    assert!(!arc.is_poisoned());
-    let arc2 = arc.clone();
-    let _: Result<(), _> = thread::spawn(move || {
-        let _lock = arc2.write().unwrap();
-        panic!();
-    })
-    .join();
-    assert!(arc.write().is_err());
-    assert!(arc.is_poisoned());
-}
-
-#[test]
-fn arc_no_poison_rr() {
-    let arc = Arc::new(ShardedLock::new(1));
-    let arc2 = arc.clone();
-    let _: Result<(), _> = thread::spawn(move || {
-        let _lock = arc2.read().unwrap();
-        panic!();
-    })
-    .join();
-    let lock = arc.read().unwrap();
-    assert_eq!(*lock, 1);
-}
-#[test]
-fn arc_no_poison_sl() {
-    let arc = Arc::new(ShardedLock::new(1));
-    let arc2 = arc.clone();
-    let _: Result<(), _> = thread::spawn(move || {
-        let _lock = arc2.read().unwrap();
-        panic!()
-    })
-    .join();
-    let lock = arc.write().unwrap();
-    assert_eq!(*lock, 1);
-}
-
-#[test]
-fn arc() {
-    let arc = Arc::new(ShardedLock::new(0));
-    let arc2 = arc.clone();
-    let (tx, rx) = channel();
-
-    thread::spawn(move || {
-        let mut lock = arc2.write().unwrap();
-        for _ in 0..10 {
-            let tmp = *lock;
-            *lock = -1;
-            thread::yield_now();
-            *lock = tmp + 1;
-        }
-        tx.send(()).unwrap();
-    });
-
-    // Readers try to catch the writer in the act
-    let mut children = Vec::new();
-    for _ in 0..5 {
-        let arc3 = arc.clone();
-        children.push(thread::spawn(move || {
-            let lock = arc3.read().unwrap();
-            assert!(*lock >= 0);
-        }));
-    }
-
-    // Wait for children to pass their asserts
-    for r in children {
-        assert!(r.join().is_ok());
-    }
-
-    // Wait for writer to finish
-    rx.recv().unwrap();
-    let lock = arc.read().unwrap();
-    assert_eq!(*lock, 10);
-}
-
-#[test]
-fn arc_access_in_unwind() {
-    let arc = Arc::new(ShardedLock::new(1));
-    let arc2 = arc.clone();
-    let _ = thread::spawn(move || {
-        struct Unwinder {
-            i: Arc<ShardedLock<isize>>,
-        }
-        impl Drop for Unwinder {
-            fn drop(&mut self) {
-                let mut lock = self.i.write().unwrap();
-                *lock += 1;
-            }
-        }
-        let _u = Unwinder { i: arc2 };
-        panic!();
-    })
-    .join();
-    let lock = arc.read().unwrap();
-    assert_eq!(*lock, 2);
-}
-
-#[test]
-fn unsized_type() {
-    let sl: &ShardedLock<[i32]> = &ShardedLock::new([1, 2, 3]);
-    {
-        let b = &mut *sl.write().unwrap();
-        b[0] = 4;
-        b[2] = 5;
-    }
-    let comp: &[i32] = &[4, 2, 5];
-    assert_eq!(&*sl.read().unwrap(), comp);
-}
-
-#[test]
-fn try_write() {
-    let lock = ShardedLock::new(0isize);
-    let read_guard = lock.read().unwrap();
-
-    let write_result = lock.try_write();
-    match write_result {
-        Err(TryLockError::WouldBlock) => (),
-        Ok(_) => panic!("try_write should not succeed while read_guard is in scope"),
-        Err(_) => panic!("unexpected error"),
-    }
-
-    drop(read_guard);
-}
-
-#[test]
-fn test_into_inner() {
-    let m = ShardedLock::new(NonCopy(10));
-    assert_eq!(m.into_inner().unwrap(), NonCopy(10));
-}
-
-#[test]
-fn test_into_inner_drop() {
-    struct Foo(Arc<AtomicUsize>);
-    impl Drop for Foo {
-        fn drop(&mut self) {
-            self.0.fetch_add(1, Ordering::SeqCst);
-        }
-    }
-    let num_drops = Arc::new(AtomicUsize::new(0));
-    let m = ShardedLock::new(Foo(num_drops.clone()));
-    assert_eq!(num_drops.load(Ordering::SeqCst), 0);
-    {
-        let _inner = m.into_inner().unwrap();
-        assert_eq!(num_drops.load(Ordering::SeqCst), 0);
-    }
-    assert_eq!(num_drops.load(Ordering::SeqCst), 1);
-}
-
-#[test]
-fn test_into_inner_poison() {
-    let m = Arc::new(ShardedLock::new(NonCopy(10)));
-    let m2 = m.clone();
-    let _ = thread::spawn(move || {
-        let _lock = m2.write().unwrap();
-        panic!("test panic in inner thread to poison ShardedLock");
-    })
-    .join();
-
-    assert!(m.is_poisoned());
-    match Arc::try_unwrap(m).unwrap().into_inner() {
-        Err(e) => assert_eq!(e.into_inner(), NonCopy(10)),
-        Ok(x) => panic!("into_inner of poisoned ShardedLock is Ok: {:?}", x),
-    }
-}
-
-#[test]
-fn test_get_mut() {
-    let mut m = ShardedLock::new(NonCopy(10));
-    *m.get_mut().unwrap() = NonCopy(20);
-    assert_eq!(m.into_inner().unwrap(), NonCopy(20));
-}
-
-#[test]
-fn test_get_mut_poison() {
-    let m = Arc::new(ShardedLock::new(NonCopy(10)));
-    let m2 = m.clone();
-    let _ = thread::spawn(move || {
-        let _lock = m2.write().unwrap();
-        panic!("test panic in inner thread to poison ShardedLock");
-    })
-    .join();
-
-    assert!(m.is_poisoned());
-    match Arc::try_unwrap(m).unwrap().get_mut() {
-        Err(e) => assert_eq!(*e.into_inner(), NonCopy(10)),
-        Ok(x) => panic!("get_mut of poisoned ShardedLock is Ok: {:?}", x),
-    }
-}
diff --git a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/tests/thread.rs b/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/tests/thread.rs
deleted file mode 100644
index 0dfad90..0000000
--- a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/tests/thread.rs
+++ /dev/null
@@ -1,215 +0,0 @@
-use std::any::Any;
-use std::sync::atomic::{AtomicUsize, Ordering};
-use std::thread::sleep;
-use std::time::Duration;
-
-use crossbeam_utils::thread;
-
-const THREADS: usize = 10;
-const SMALL_STACK_SIZE: usize = 20;
-
-#[test]
-fn join() {
-    let counter = AtomicUsize::new(0);
-    thread::scope(|scope| {
-        let handle = scope.spawn(|_| {
-            counter.store(1, Ordering::Relaxed);
-        });
-        assert!(handle.join().is_ok());
-
-        let panic_handle = scope.spawn(|_| {
-            panic!("\"My honey is running out!\", said Pooh.");
-        });
-        assert!(panic_handle.join().is_err());
-    })
-    .unwrap();
-
-    // There should be sufficient synchronization.
-    assert_eq!(1, counter.load(Ordering::Relaxed));
-}
-
-#[test]
-fn counter() {
-    let counter = AtomicUsize::new(0);
-    thread::scope(|scope| {
-        for _ in 0..THREADS {
-            scope.spawn(|_| {
-                counter.fetch_add(1, Ordering::Relaxed);
-            });
-        }
-    })
-    .unwrap();
-
-    assert_eq!(THREADS, counter.load(Ordering::Relaxed));
-}
-
-#[test]
-fn counter_builder() {
-    let counter = AtomicUsize::new(0);
-    thread::scope(|scope| {
-        for i in 0..THREADS {
-            scope
-                .builder()
-                .name(format!("child-{}", i))
-                .stack_size(SMALL_STACK_SIZE)
-                .spawn(|_| {
-                    counter.fetch_add(1, Ordering::Relaxed);
-                })
-                .unwrap();
-        }
-    })
-    .unwrap();
-
-    assert_eq!(THREADS, counter.load(Ordering::Relaxed));
-}
-
-#[test]
-fn counter_panic() {
-    let counter = AtomicUsize::new(0);
-    let result = thread::scope(|scope| {
-        scope.spawn(|_| {
-            panic!("\"My honey is running out!\", said Pooh.");
-        });
-        sleep(Duration::from_millis(100));
-
-        for _ in 0..THREADS {
-            scope.spawn(|_| {
-                counter.fetch_add(1, Ordering::Relaxed);
-            });
-        }
-    });
-
-    assert_eq!(THREADS, counter.load(Ordering::Relaxed));
-    assert!(result.is_err());
-}
-
-#[test]
-fn panic_twice() {
-    let result = thread::scope(|scope| {
-        scope.spawn(|_| {
-            sleep(Duration::from_millis(500));
-            panic!("thread #1");
-        });
-        scope.spawn(|_| {
-            panic!("thread #2");
-        });
-    });
-
-    let err = result.unwrap_err();
-    let vec = err
-        .downcast_ref::<Vec<Box<dyn Any + Send + 'static>>>()
-        .unwrap();
-    assert_eq!(2, vec.len());
-
-    let first = vec[0].downcast_ref::<&str>().unwrap();
-    let second = vec[1].downcast_ref::<&str>().unwrap();
-    assert_eq!("thread #1", *first);
-    assert_eq!("thread #2", *second)
-}
-
-#[test]
-fn panic_many() {
-    let result = thread::scope(|scope| {
-        scope.spawn(|_| panic!("deliberate panic #1"));
-        scope.spawn(|_| panic!("deliberate panic #2"));
-        scope.spawn(|_| panic!("deliberate panic #3"));
-    });
-
-    let err = result.unwrap_err();
-    let vec = err
-        .downcast_ref::<Vec<Box<dyn Any + Send + 'static>>>()
-        .unwrap();
-    assert_eq!(3, vec.len());
-
-    for panic in vec.iter() {
-        let panic = panic.downcast_ref::<&str>().unwrap();
-        assert!(
-            *panic == "deliberate panic #1"
-                || *panic == "deliberate panic #2"
-                || *panic == "deliberate panic #3"
-        );
-    }
-}
-
-#[test]
-fn nesting() {
-    let var = "foo".to_string();
-
-    struct Wrapper<'a> {
-        var: &'a String,
-    }
-
-    impl<'a> Wrapper<'a> {
-        fn recurse(&'a self, scope: &thread::Scope<'a>, depth: usize) {
-            assert_eq!(self.var, "foo");
-
-            if depth > 0 {
-                scope.spawn(move |scope| {
-                    self.recurse(scope, depth - 1);
-                });
-            }
-        }
-    }
-
-    let wrapper = Wrapper { var: &var };
-
-    thread::scope(|scope| {
-        scope.spawn(|scope| {
-            scope.spawn(|scope| {
-                wrapper.recurse(scope, 5);
-            });
-        });
-    })
-    .unwrap();
-}
-
-#[test]
-fn join_nested() {
-    thread::scope(|scope| {
-        scope.spawn(|scope| {
-            let handle = scope.spawn(|_| 7);
-
-            sleep(Duration::from_millis(200));
-            handle.join().unwrap();
-        });
-
-        sleep(Duration::from_millis(100));
-    })
-    .unwrap();
-}
-
-#[test]
-fn scope_returns_ok() {
-    let result = thread::scope(|scope| scope.spawn(|_| 1234).join().unwrap()).unwrap();
-    assert_eq!(result, 1234);
-}
-
-#[cfg(unix)]
-#[test]
-fn as_pthread_t() {
-    use std::os::unix::thread::JoinHandleExt;
-    thread::scope(|scope| {
-        let handle = scope.spawn(|_scope| {
-            sleep(Duration::from_millis(100));
-            42
-        });
-        let _pthread_t = handle.as_pthread_t();
-        handle.join().unwrap();
-    })
-    .unwrap();
-}
-
-#[cfg(windows)]
-#[test]
-fn as_raw_handle() {
-    use std::os::windows::io::AsRawHandle;
-    thread::scope(|scope| {
-        let handle = scope.spawn(|_scope| {
-            sleep(Duration::from_millis(100));
-            42
-        });
-        let _raw_handle = handle.as_raw_handle();
-        handle.join().unwrap();
-    })
-    .unwrap();
-}
diff --git a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/tests/wait_group.rs b/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/tests/wait_group.rs
deleted file mode 100644
index 5b549b84..0000000
--- a/third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/tests/wait_group.rs
+++ /dev/null
@@ -1,67 +0,0 @@
-use std::sync::mpsc;
-use std::thread;
-use std::time::Duration;
-
-use crossbeam_utils::sync::WaitGroup;
-
-const THREADS: usize = 10;
-
-#[test]
-fn wait() {
-    let wg = WaitGroup::new();
-    let (tx, rx) = mpsc::channel();
-
-    for _ in 0..THREADS {
-        let wg = wg.clone();
-        let tx = tx.clone();
-
-        thread::spawn(move || {
-            wg.wait();
-            tx.send(()).unwrap();
-        });
-    }
-
-    thread::sleep(Duration::from_millis(100));
-
-    // At this point, all spawned threads should be blocked, so we shouldn't get anything from the
-    // channel.
-    assert!(rx.try_recv().is_err());
-
-    wg.wait();
-
-    // Now, the wait group is cleared and we should receive messages.
-    for _ in 0..THREADS {
-        rx.recv().unwrap();
-    }
-}
-
-#[test]
-fn wait_and_drop() {
-    let wg = WaitGroup::new();
-    let wg2 = WaitGroup::new();
-    let (tx, rx) = mpsc::channel();
-
-    for _ in 0..THREADS {
-        let wg = wg.clone();
-        let wg2 = wg2.clone();
-        let tx = tx.clone();
-
-        thread::spawn(move || {
-            wg2.wait();
-            tx.send(()).unwrap();
-            drop(wg);
-        });
-    }
-
-    // At this point, no thread has gotten past `wg2.wait()`, so we shouldn't get anything from the
-    // channel.
-    assert!(rx.try_recv().is_err());
-    drop(wg2);
-
-    wg.wait();
-
-    // Now, the wait group is cleared and we should receive messages.
-    for _ in 0..THREADS {
-        rx.try_recv().unwrap();
-    }
-}
diff --git a/third_party/rust/chromium_crates_io/vendor/zip-v2/.cargo_vcs_info.json b/third_party/rust/chromium_crates_io/vendor/zip-v2/.cargo_vcs_info.json
deleted file mode 100644
index 1a4f6cb..0000000
--- a/third_party/rust/chromium_crates_io/vendor/zip-v2/.cargo_vcs_info.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-  "git": {
-    "sha1": "1d42731efcad72e9f653a5ca46ed8306f4a3ca72"
-  },
-  "path_in_vcs": ""
-}
\ No newline at end of file
diff --git a/third_party/rust/chromium_crates_io/vendor/zip-v2/src/build.rs b/third_party/rust/chromium_crates_io/vendor/zip-v2/src/build.rs
deleted file mode 100644
index 26d9689..0000000
--- a/third_party/rust/chromium_crates_io/vendor/zip-v2/src/build.rs
+++ /dev/null
@@ -1,7 +0,0 @@
-use std::env::var;
-
-fn main() {
-    if var("CARGO_FEATURE_DEFLATE_MINIZ").is_ok() && var("CARGO_FEATURE__ALL_FEATURES").is_err() {
-        println!("cargo:warning=Feature `deflate-miniz` is deprecated; replace it with `deflate`");
-    }
-}
diff --git a/third_party/rust/chromium_crates_io/vendor/zip-v2/.cargo-checksum.json b/third_party/rust/chromium_crates_io/vendor/zip-v3/.cargo-checksum.json
similarity index 100%
rename from third_party/rust/chromium_crates_io/vendor/zip-v2/.cargo-checksum.json
rename to third_party/rust/chromium_crates_io/vendor/zip-v3/.cargo-checksum.json
diff --git a/third_party/rust/chromium_crates_io/vendor/zip-v3/.cargo_vcs_info.json b/third_party/rust/chromium_crates_io/vendor/zip-v3/.cargo_vcs_info.json
new file mode 100644
index 0000000..76939dc
--- /dev/null
+++ b/third_party/rust/chromium_crates_io/vendor/zip-v3/.cargo_vcs_info.json
@@ -0,0 +1,6 @@
+{
+  "git": {
+    "sha1": "8c01cb7fec6c4f696f8306f1942044dcf1a8f5b2"
+  },
+  "path_in_vcs": ""
+}
\ No newline at end of file
diff --git a/third_party/rust/chromium_crates_io/vendor/zip-v2/.gitattributes b/third_party/rust/chromium_crates_io/vendor/zip-v3/.gitattributes
similarity index 100%
rename from third_party/rust/chromium_crates_io/vendor/zip-v2/.gitattributes
rename to third_party/rust/chromium_crates_io/vendor/zip-v3/.gitattributes
diff --git a/third_party/rust/chromium_crates_io/vendor/zip-v2/.gitignore b/third_party/rust/chromium_crates_io/vendor/zip-v3/.gitignore
similarity index 100%
rename from third_party/rust/chromium_crates_io/vendor/zip-v2/.gitignore
rename to third_party/rust/chromium_crates_io/vendor/zip-v3/.gitignore
diff --git a/third_party/rust/chromium_crates_io/vendor/zip-v2/.whitesource b/third_party/rust/chromium_crates_io/vendor/zip-v3/.whitesource
similarity index 100%
rename from third_party/rust/chromium_crates_io/vendor/zip-v2/.whitesource
rename to third_party/rust/chromium_crates_io/vendor/zip-v3/.whitesource
diff --git a/third_party/rust/chromium_crates_io/vendor/zip-v2/CHANGELOG.md b/third_party/rust/chromium_crates_io/vendor/zip-v3/CHANGELOG.md
similarity index 96%
rename from third_party/rust/chromium_crates_io/vendor/zip-v2/CHANGELOG.md
rename to third_party/rust/chromium_crates_io/vendor/zip-v3/CHANGELOG.md
index 59f83bf9..59a40d4 100644
--- a/third_party/rust/chromium_crates_io/vendor/zip-v2/CHANGELOG.md
+++ b/third_party/rust/chromium_crates_io/vendor/zip-v3/CHANGELOG.md
@@ -1,5 +1,20 @@
 # Changelog
 
+## [3.0.0](https://github.com/zip-rs/zip2/compare/v2.6.1...v3.0.0) - 2025-05-14
+
+### <!-- 1 -->🐛 Bug Fixes
+
+- return correct offset in SeekableTake::seek ([#342](https://github.com/zip-rs/zip2/pull/342))
+- When only zopfli is available, decompression of deflate should not be possible ([#348](https://github.com/zip-rs/zip2/pull/348))
+- Specify `flate2` dependency of the `deflate-flate2` feature. ([#345](https://github.com/zip-rs/zip2/pull/345))
+
+### <!-- 7 -->⚙️ Miscellaneous Tasks
+
+- drop unused crossbeam-utils dependency ([#339](https://github.com/zip-rs/zip2/pull/339))
+- fix typo
+- remove `deflate-flate2` dependency on specific backend
+- [**breaking**] Drop deprecated `deflate-miniz` feature flag ([#351](https://github.com/zip-rs/zip2/pull/351))
+
 ## [2.6.1](https://github.com/zip-rs/zip2/compare/v2.6.0...v2.6.1) - 2025-04-03
 
 ### <!-- 1 -->🐛 Bug Fixes
diff --git a/third_party/rust/chromium_crates_io/vendor/zip-v2/CODE_OF_CONDUCT.md b/third_party/rust/chromium_crates_io/vendor/zip-v3/CODE_OF_CONDUCT.md
similarity index 100%
rename from third_party/rust/chromium_crates_io/vendor/zip-v2/CODE_OF_CONDUCT.md
rename to third_party/rust/chromium_crates_io/vendor/zip-v3/CODE_OF_CONDUCT.md
diff --git a/third_party/rust/chromium_crates_io/vendor/zip-v2/CONTRIBUTING.md b/third_party/rust/chromium_crates_io/vendor/zip-v3/CONTRIBUTING.md
similarity index 100%
rename from third_party/rust/chromium_crates_io/vendor/zip-v2/CONTRIBUTING.md
rename to third_party/rust/chromium_crates_io/vendor/zip-v3/CONTRIBUTING.md
diff --git a/third_party/rust/chromium_crates_io/vendor/zip-v2/Cargo.lock b/third_party/rust/chromium_crates_io/vendor/zip-v3/Cargo.lock
similarity index 91%
rename from third_party/rust/chromium_crates_io/vendor/zip-v2/Cargo.lock
rename to third_party/rust/chromium_crates_io/vendor/zip-v3/Cargo.lock
index 6fad44a8..38643e3 100644
--- a/third_party/rust/chromium_crates_io/vendor/zip-v2/Cargo.lock
+++ b/third_party/rust/chromium_crates_io/vendor/zip-v3/Cargo.lock
@@ -86,9 +86,9 @@
 
 [[package]]
 name = "anyhow"
-version = "1.0.97"
+version = "1.0.98"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f"
+checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
 
 [[package]]
 name = "arbitrary"
@@ -113,9 +113,9 @@
 
 [[package]]
 name = "bitflags"
-version = "2.9.0"
+version = "2.9.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
+checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
 
 [[package]]
 name = "block-buffer"
@@ -159,9 +159,9 @@
 
 [[package]]
 name = "cc"
-version = "1.2.18"
+version = "1.2.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "525046617d8376e3db1deffb079e91cef90a89fc3ca5c185bbf8c9ecdd15cd5c"
+checksum = "32db95edf998450acc7881c932f94cd9b05c87b4b2599e8bab064753da4acfd1"
 dependencies = [
  "jobserver",
  "libc",
@@ -176,9 +176,9 @@
 
 [[package]]
 name = "chrono"
-version = "0.4.40"
+version = "0.4.41"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c"
+checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d"
 dependencies = [
  "android-tzdata",
  "iana-time-zone",
@@ -239,15 +239,6 @@
 checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
 
 [[package]]
-name = "cmake"
-version = "0.1.54"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0"
-dependencies = [
- "cc",
-]
-
-[[package]]
 name = "colorchoice"
 version = "1.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -276,9 +267,9 @@
 
 [[package]]
 name = "crc"
-version = "3.2.1"
+version = "3.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636"
+checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675"
 dependencies = [
  "crc-catalog",
 ]
@@ -299,12 +290,6 @@
 ]
 
 [[package]]
-name = "crossbeam-utils"
-version = "0.8.21"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
-
-[[package]]
 name = "crypto-common"
 version = "0.1.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -322,9 +307,9 @@
 
 [[package]]
 name = "deranged"
-version = "0.4.1"
+version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "28cfac68e08048ae1883171632c2aef3ebc555621ae56fbccce1cbf22dd7f058"
+checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e"
 dependencies = [
  "powerfmt",
 ]
@@ -380,7 +365,7 @@
 checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece"
 dependencies = [
  "crc32fast",
- "libz-ng-sys",
+ "libz-rs-sys",
  "libz-sys",
  "miniz_oxide",
 ]
@@ -397,9 +382,9 @@
 
 [[package]]
 name = "getrandom"
-version = "0.3.2"
+version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0"
+checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
 dependencies = [
  "cfg-if",
  "js-sys",
@@ -411,9 +396,9 @@
 
 [[package]]
 name = "hashbrown"
-version = "0.15.2"
+version = "0.15.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
+checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3"
 
 [[package]]
 name = "heck"
@@ -456,9 +441,9 @@
 
 [[package]]
 name = "indexmap"
-version = "2.8.0"
+version = "2.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058"
+checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
 dependencies = [
  "equivalent",
  "hashbrown",
@@ -487,9 +472,9 @@
 
 [[package]]
 name = "jiff"
-version = "0.2.5"
+version = "0.2.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c102670231191d07d37a35af3eb77f1f0dbf7a71be51a962dcd57ea607be7260"
+checksum = "f02000660d30638906021176af16b17498bd0d12813dbfe7b276d8bc7f3c0806"
 dependencies = [
  "jiff-static",
  "jiff-tzdb-platform",
@@ -502,9 +487,9 @@
 
 [[package]]
 name = "jiff-static"
-version = "0.2.5"
+version = "0.2.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4cdde31a9d349f1b1f51a0b3714a5940ac022976f4b49485fc04be052b183b4c"
+checksum = "f3c30758ddd7188629c6713fc45d1188af4f44c90582311d0c8d8c9907f60c48"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -548,18 +533,17 @@
 
 [[package]]
 name = "libc"
-version = "0.2.171"
+version = "0.2.172"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
+checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
 
 [[package]]
-name = "libz-ng-sys"
-version = "1.1.22"
+name = "libz-rs-sys"
+version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7118c2c2a3c7b6edc279a8b19507672b9c4d716f95e671172dfa4e23f9fd824"
+checksum = "6489ca9bd760fe9642d7644e827b0c9add07df89857b0416ee15c1cc1a3b8c5a"
 dependencies = [
- "cmake",
- "libc",
+ "zlib-rs",
 ]
 
 [[package]]
@@ -575,15 +559,9 @@
 
 [[package]]
 name = "linux-raw-sys"
-version = "0.9.3"
+version = "0.9.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413"
-
-[[package]]
-name = "lockfree-object-pool"
-version = "0.1.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9374ef4228402d4b7e403e5838cb880d9ee663314b0a900d5a6aabf0c213552e"
+checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
 
 [[package]]
 name = "log"
@@ -620,9 +598,9 @@
 
 [[package]]
 name = "miniz_oxide"
-version = "0.8.7"
+version = "0.8.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ff70ce3e48ae43fa075863cef62e8b43b71a4f2382229920e0df362592919430"
+checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
 dependencies = [
  "adler2",
 ]
@@ -696,9 +674,9 @@
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.94"
+version = "1.0.95"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
+checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
 dependencies = [
  "unicode-ident",
 ]
@@ -720,9 +698,9 @@
 
 [[package]]
 name = "rustix"
-version = "1.0.5"
+version = "1.0.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf"
+checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266"
 dependencies = [
  "bitflags",
  "errno",
@@ -803,9 +781,9 @@
 
 [[package]]
 name = "syn"
-version = "2.0.100"
+version = "2.0.101"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
+checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -814,9 +792,9 @@
 
 [[package]]
 name = "tempfile"
-version = "3.19.1"
+version = "3.20.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf"
+checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1"
 dependencies = [
  "fastrand",
  "getrandom",
@@ -1144,7 +1122,7 @@
 
 [[package]]
 name = "zip"
-version = "2.6.1"
+version = "3.0.0"
 dependencies = [
  "aes",
  "anyhow",
@@ -1155,7 +1133,6 @@
  "clap",
  "constant_time_eq",
  "crc32fast",
- "crossbeam-utils",
  "deflate64",
  "flate2",
  "getrandom",
@@ -1166,7 +1143,6 @@
  "memchr",
  "nt-time",
  "pbkdf2",
- "proc-macro2",
  "sha1",
  "tempfile",
  "time",
@@ -1178,16 +1154,20 @@
 ]
 
 [[package]]
-name = "zopfli"
-version = "0.8.1"
+name = "zlib-rs"
+version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5019f391bac5cf252e93bbcc53d039ffd62c7bfb7c150414d61369afe57e946"
+checksum = "868b928d7949e09af2f6086dfc1e01936064cc7a819253bce650d4e2a2d63ba8"
+
+[[package]]
+name = "zopfli"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "edfc5ee405f504cd4984ecc6f14d02d55cfda60fa4b689434ef4102aae150cd7"
 dependencies = [
  "bumpalo",
  "crc32fast",
- "lockfree-object-pool",
  "log",
- "once_cell",
  "simd-adler32",
 ]
 
diff --git a/third_party/rust/chromium_crates_io/vendor/zip-v2/Cargo.toml b/third_party/rust/chromium_crates_io/vendor/zip-v3/Cargo.toml
similarity index 89%
rename from third_party/rust/chromium_crates_io/vendor/zip-v2/Cargo.toml
rename to third_party/rust/chromium_crates_io/vendor/zip-v3/Cargo.toml
index 7052e986..bb31fe73 100644
--- a/third_party/rust/chromium_crates_io/vendor/zip-v2/Cargo.toml
+++ b/third_party/rust/chromium_crates_io/vendor/zip-v3/Cargo.toml
@@ -11,16 +11,16 @@
 
 [package]
 edition = "2021"
-rust-version = "1.73.0"
+rust-version = "1.75.0"
 name = "zip"
-version = "2.6.1"
+version = "3.0.0"
 authors = [
     "Mathijs van de Nes <git@mathijs.vd-nes.nl>",
     "Marli Frost <marli@frost.red>",
     "Ryan Levick <ryan.levick@gmail.com>",
     "Chris Hennick <hennickc@amazon.com>",
 ]
-build = "src/build.rs"
+build = false
 exclude = [
     "tests/**",
     "examples/**",
@@ -77,23 +77,19 @@
 ]
 deflate = [
     "deflate-zopfli",
-    "deflate-flate2",
+    "deflate-flate2-zlib-rs",
 ]
 deflate-flate2 = [
     "_deflate-any",
-    "flate2/rust_backend",
+    "dep:flate2",
 ]
-deflate-miniz = [
-    "deflate",
+deflate-flate2-zlib = [
     "deflate-flate2",
-]
-deflate-zlib = [
     "flate2/zlib",
-    "deflate-flate2",
 ]
-deflate-zlib-ng = [
-    "flate2/zlib-ng",
+deflate-flate2-zlib-rs = [
     "deflate-flate2",
+    "flate2/zlib-rs",
 ]
 deflate-zopfli = [
     "zopfli",
@@ -148,7 +144,7 @@
 optional = true
 
 [dependencies.flate2]
-version = "1.0"
+version = "1.1.1"
 optional = true
 default-features = false
 
@@ -189,10 +185,6 @@
 version = "0.12"
 optional = true
 
-[dependencies.proc-macro2]
-version = ">=1.0.60"
-optional = true
-
 [dependencies.sha1]
 version = "0.10"
 optional = true
@@ -252,9 +244,6 @@
 [dev-dependencies.walkdir]
 version = "2.5"
 
-[target.'cfg(any(all(target_arch = "arm", target_pointer_width = "32"), target_arch = "mips", target_arch = "powerpc"))'.dependencies.crossbeam-utils]
-version = "0.8.21"
-
 [target."cfg(fuzzing)".dependencies.arbitrary]
 version = "1.4.1"
 features = ["derive"]
diff --git a/third_party/rust/chromium_crates_io/vendor/zip-v2/Cargo.toml.orig b/third_party/rust/chromium_crates_io/vendor/zip-v3/Cargo.toml.orig
similarity index 78%
rename from third_party/rust/chromium_crates_io/vendor/zip-v2/Cargo.toml.orig
rename to third_party/rust/chromium_crates_io/vendor/zip-v3/Cargo.toml.orig
index fae78e2..20ecb8e 100644
--- a/third_party/rust/chromium_crates_io/vendor/zip-v2/Cargo.toml.orig
+++ b/third_party/rust/chromium_crates_io/vendor/zip-v3/Cargo.toml.orig
@@ -1,6 +1,6 @@
 [package]
 name = "zip"
-version = "2.6.1"
+version = "3.0.0"
 authors = [
     "Mathijs van de Nes <git@mathijs.vd-nes.nl>",
     "Marli Frost <marli@frost.red>",
@@ -12,13 +12,12 @@
 keywords = ["zip", "archive", "compression"]
 # Any change to rust-version must be reflected also in `README.md` and `.github/workflows/ci.yaml`.
 # The MSRV policy is documented in `README.md`.
-rust-version = "1.73.0"
+rust-version = "1.75.0"
 description = """
 Library to support the reading and writing of zip files.
 """
 edition = "2021"
 exclude = ["tests/**", "examples/**", ".github/**", "fuzz_read/**", "fuzz_write/**"]
-build = "src/build.rs"
 
 [package.metadata.docs.rs]
 all-features = true
@@ -33,7 +32,7 @@
 chrono = { version = "0.4", optional = true }
 constant_time_eq = { version = "0.3", optional = true }
 crc32fast = "1.4"
-flate2 = { version = "1.0", default-features = false, optional = true }
+flate2 = { version = "1.1.1", default-features = false, optional = true }
 getrandom = { version = "0.3.1", features = ["wasm_js", "std"], optional = true}
 hmac = { version = "0.12", optional = true, features = ["reset"] }
 indexmap = "2"
@@ -51,10 +50,6 @@
 deflate64 = { version = "0.1.9", optional = true }
 lzma-rs = { version = "0.3", default-features = false, optional = true }
 xz2 = { version = "0.1.7", optional = true }
-proc-macro2 = { version = ">=1.0.60", optional = true } # Override transitive dep on 1.0.59 due to https://github.com/rust-lang/rust/issues/113152
-
-[target.'cfg(any(all(target_arch = "arm", target_pointer_width = "32"), target_arch = "mips", target_arch = "powerpc"))'.dependencies]
-crossbeam-utils = "0.8.21"
 
 [target.'cfg(fuzzing)'.dependencies]
 arbitrary = { version = "1.4.1", features = ["derive"] }
@@ -73,12 +68,13 @@
 chrono = ["chrono/default"]
 _deflate-any = []
 _all-features = [] # Detect when --all-features is used
-deflate = ["flate2/rust_backend", "deflate-zopfli", "deflate-flate2"]
-deflate-flate2 = ["_deflate-any"]
-# DEPRECATED: previously enabled `flate2/miniz_oxide` which is equivalent to `flate2/rust_backend`
-deflate-miniz = ["deflate", "deflate-flate2"]
-deflate-zlib = ["flate2/zlib", "deflate-flate2"]
-deflate-zlib-ng = ["flate2/zlib-ng", "deflate-flate2"]
+deflate = ["deflate-zopfli", "deflate-flate2-zlib-rs"]
+# Pull in flate2, but don't choose a backend; useful if you want to choose your own flate2 backend
+deflate-flate2 = ["_deflate-any", "dep:flate2"]
+# Pull in flate2 and the fast zlib-rs backend; this is what most users will want
+deflate-flate2-zlib-rs = ["deflate-flate2", "flate2/zlib-rs"]
+# Pull in flate2 and the zlib backend; only use this if you need a dynamically linked system zlib
+deflate-flate2-zlib = ["deflate-flate2", "flate2/zlib"]
 deflate-zopfli = ["zopfli", "_deflate-any"]
 jiff-02 = ["dep:jiff"]
 nt-time = ["dep:nt-time"]
diff --git a/third_party/rust/chromium_crates_io/vendor/zip-v2/LICENSE b/third_party/rust/chromium_crates_io/vendor/zip-v3/LICENSE
similarity index 100%
rename from third_party/rust/chromium_crates_io/vendor/zip-v2/LICENSE
rename to third_party/rust/chromium_crates_io/vendor/zip-v3/LICENSE
diff --git a/third_party/rust/chromium_crates_io/vendor/zip-v2/README.md b/third_party/rust/chromium_crates_io/vendor/zip-v3/README.md
similarity index 93%
rename from third_party/rust/chromium_crates_io/vendor/zip-v2/README.md
rename to third_party/rust/chromium_crates_io/vendor/zip-v3/README.md
index 1f51e911..b305fba 100644
--- a/third_party/rust/chromium_crates_io/vendor/zip-v2/README.md
+++ b/third_party/rust/chromium_crates_io/vendor/zip-v3/README.md
@@ -50,14 +50,10 @@
 
 By default `aes-crypto`, `bzip2`, `deflate`, `deflate64`, `lzma`, `time` and `zstd` are enabled.
 
-The following feature flags are deprecated:
-
-* `deflate-miniz`: Use `flate2`'s default backend for compression. Currently the same as `deflate`.
-
 MSRV
 ----
 
-Our current Minimum Supported Rust Version is **1.73**. When adding features,
+Our current Minimum Supported Rust Version is **1.75**. When adding features,
 we will follow these guidelines:
 
 - We will always support a minor Rust version that has been stable for at least 6 months.
diff --git a/third_party/rust/chromium_crates_io/vendor/zip-v2/SECURITY.md b/third_party/rust/chromium_crates_io/vendor/zip-v3/SECURITY.md
similarity index 100%
rename from third_party/rust/chromium_crates_io/vendor/zip-v2/SECURITY.md
rename to third_party/rust/chromium_crates_io/vendor/zip-v3/SECURITY.md
diff --git a/third_party/rust/chromium_crates_io/vendor/zip-v2/benches/merge_archive.rs b/third_party/rust/chromium_crates_io/vendor/zip-v3/benches/merge_archive.rs
similarity index 98%
rename from third_party/rust/chromium_crates_io/vendor/zip-v2/benches/merge_archive.rs
rename to third_party/rust/chromium_crates_io/vendor/zip-v3/benches/merge_archive.rs
index b8dd492..9e5598f 100644
--- a/third_party/rust/chromium_crates_io/vendor/zip-v2/benches/merge_archive.rs
+++ b/third_party/rust/chromium_crates_io/vendor/zip-v3/benches/merge_archive.rs
@@ -15,7 +15,7 @@
 
     let mut bytes = vec![0u8; entry_size];
     for i in 0..num_entries {
-        let name = format!("random{}.dat", i);
+        let name = format!("random{i}.dat");
         zip.start_file(name, options)?;
         getrandom::fill(&mut bytes).unwrap();
         zip.write_all(&bytes)?;
diff --git a/third_party/rust/chromium_crates_io/vendor/zip-v2/benches/read_entry.rs b/third_party/rust/chromium_crates_io/vendor/zip-v3/benches/read_entry.rs
similarity index 100%
rename from third_party/rust/chromium_crates_io/vendor/zip-v2/benches/read_entry.rs
rename to third_party/rust/chromium_crates_io/vendor/zip-v3/benches/read_entry.rs
diff --git a/third_party/rust/chromium_crates_io/vendor/zip-v2/benches/read_metadata.rs b/third_party/rust/chromium_crates_io/vendor/zip-v3/benches/read_metadata.rs
similarity index 100%
rename from third_party/rust/chromium_crates_io/vendor/zip-v2/benches/read_metadata.rs
rename to third_party/rust/chromium_crates_io/vendor/zip-v3/benches/read_metadata.rs
diff --git a/third_party/rust/chromium_crates_io/vendor/zip-v2/cliff.toml b/third_party/rust/chromium_crates_io/vendor/zip-v3/cliff.toml
similarity index 100%
rename from third_party/rust/chromium_crates_io/vendor/zip-v2/cliff.toml
rename to third_party/rust/chromium_crates_io/vendor/zip-v3/cliff.toml
diff --git a/third_party/rust/chromium_crates_io/vendor/zip-v2/pull_request_template.md b/third_party/rust/chromium_crates_io/vendor/zip-v3/pull_request_template.md
similarity index 100%
rename from third_party/rust/chromium_crates_io/vendor/zip-v2/pull_request_template.md
rename to third_party/rust/chromium_crates_io/vendor/zip-v3/pull_request_template.md
diff --git a/third_party/rust/chromium_crates_io/vendor/zip-v2/release-plz.toml b/third_party/rust/chromium_crates_io/vendor/zip-v3/release-plz.toml
similarity index 100%
rename from third_party/rust/chromium_crates_io/vendor/zip-v2/release-plz.toml
rename to third_party/rust/chromium_crates_io/vendor/zip-v3/release-plz.toml
diff --git a/third_party/rust/chromium_crates_io/vendor/zip-v2/src/aes.rs b/third_party/rust/chromium_crates_io/vendor/zip-v3/src/aes.rs
similarity index 100%
rename from third_party/rust/chromium_crates_io/vendor/zip-v2/src/aes.rs
rename to third_party/rust/chromium_crates_io/vendor/zip-v3/src/aes.rs
diff --git a/third_party/rust/chromium_crates_io/vendor/zip-v2/src/aes_ctr.rs b/third_party/rust/chromium_crates_io/vendor/zip-v3/src/aes_ctr.rs
similarity index 100%
rename from third_party/rust/chromium_crates_io/vendor/zip-v2/src/aes_ctr.rs
rename to third_party/rust/chromium_crates_io/vendor/zip-v3/src/aes_ctr.rs
diff --git a/third_party/rust/chromium_crates_io/vendor/zip-v2/src/compression.rs b/third_party/rust/chromium_crates_io/vendor/zip-v3/src/compression.rs
similarity index 98%
rename from third_party/rust/chromium_crates_io/vendor/zip-v2/src/compression.rs
rename to third_party/rust/chromium_crates_io/vendor/zip-v3/src/compression.rs
index c3e8f5a..3e9b7d94 100644
--- a/third_party/rust/chromium_crates_io/vendor/zip-v2/src/compression.rs
+++ b/third_party/rust/chromium_crates_io/vendor/zip-v3/src/compression.rs
@@ -193,7 +193,7 @@
 
 pub(crate) enum Decompressor<R: io::BufRead> {
     Stored(R),
-    #[cfg(feature = "_deflate-any")]
+    #[cfg(feature = "deflate-flate2")]
     Deflated(flate2::bufread::DeflateDecoder<R>),
     #[cfg(feature = "deflate64")]
     Deflate64(deflate64::Deflate64Decoder<R>),
@@ -211,7 +211,7 @@
     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
         match self {
             Decompressor::Stored(r) => r.read(buf),
-            #[cfg(feature = "_deflate-any")]
+            #[cfg(feature = "deflate-flate2")]
             Decompressor::Deflated(r) => r.read(buf),
             #[cfg(feature = "deflate64")]
             Decompressor::Deflate64(r) => r.read(buf),
@@ -231,7 +231,7 @@
     pub fn new(reader: R, compression_method: CompressionMethod) -> crate::result::ZipResult<Self> {
         Ok(match compression_method {
             CompressionMethod::Stored => Decompressor::Stored(reader),
-            #[cfg(feature = "_deflate-any")]
+            #[cfg(feature = "deflate-flate2")]
             CompressionMethod::Deflated => {
                 Decompressor::Deflated(flate2::bufread::DeflateDecoder::new(reader))
             }
@@ -261,7 +261,7 @@
     pub fn into_inner(self) -> R {
         match self {
             Decompressor::Stored(r) => r,
-            #[cfg(feature = "_deflate-any")]
+            #[cfg(feature = "deflate-flate2")]
             Decompressor::Deflated(r) => r.into_inner(),
             #[cfg(feature = "deflate64")]
             Decompressor::Deflate64(r) => r.into_inner(),
diff --git a/third_party/rust/chromium_crates_io/vendor/zip-v2/src/cp437.rs b/third_party/rust/chromium_crates_io/vendor/zip-v3/src/cp437.rs
similarity index 100%
rename from third_party/rust/chromium_crates_io/vendor/zip-v2/src/cp437.rs
rename to third_party/rust/chromium_crates_io/vendor/zip-v3/src/cp437.rs
diff --git a/third_party/rust/chromium_crates_io/vendor/zip-v2/src/crc32.rs b/third_party/rust/chromium_crates_io/vendor/zip-v3/src/crc32.rs
similarity index 100%
rename from third_party/rust/chromium_crates_io/vendor/zip-v2/src/crc32.rs
rename to third_party/rust/chromium_crates_io/vendor/zip-v3/src/crc32.rs
diff --git a/third_party/rust/chromium_crates_io/vendor/zip-v2/src/extra_fields/extended_timestamp.rs b/third_party/rust/chromium_crates_io/vendor/zip-v3/src/extra_fields/extended_timestamp.rs
similarity index 100%
rename from third_party/rust/chromium_crates_io/vendor/zip-v2/src/extra_fields/extended_timestamp.rs
rename to third_party/rust/chromium_crates_io/vendor/zip-v3/src/extra_fields/extended_timestamp.rs
diff --git a/third_party/rust/chromium_crates_io/vendor/zip-v2/src/extra_fields/mod.rs b/third_party/rust/chromium_crates_io/vendor/zip-v3/src/extra_fields/mod.rs
similarity index 100%
rename from third_party/rust/chromium_crates_io/vendor/zip-v2/src/extra_fields/mod.rs
rename to third_party/rust/chromium_crates_io/vendor/zip-v3/src/extra_fields/mod.rs
diff --git a/third_party/rust/chromium_crates_io/vendor/zip-v2/src/extra_fields/ntfs.rs b/third_party/rust/chromium_crates_io/vendor/zip-v3/src/extra_fields/ntfs.rs
similarity index 100%
rename from third_party/rust/chromium_crates_io/vendor/zip-v2/src/extra_fields/ntfs.rs
rename to third_party/rust/chromium_crates_io/vendor/zip-v3/src/extra_fields/ntfs.rs
diff --git a/third_party/rust/chromium_crates_io/vendor/zip-v2/src/extra_fields/zipinfo_utf8.rs b/third_party/rust/chromium_crates_io/vendor/zip-v3/src/extra_fields/zipinfo_utf8.rs
similarity index 100%
rename from third_party/rust/chromium_crates_io/vendor/zip-v2/src/extra_fields/zipinfo_utf8.rs
rename to third_party/rust/chromium_crates_io/vendor/zip-v3/src/extra_fields/zipinfo_utf8.rs
diff --git a/third_party/rust/chromium_crates_io/vendor/zip-v2/src/lib.rs b/third_party/rust/chromium_crates_io/vendor/zip-v3/src/lib.rs
similarity index 100%
rename from third_party/rust/chromium_crates_io/vendor/zip-v2/src/lib.rs
rename to third_party/rust/chromium_crates_io/vendor/zip-v3/src/lib.rs
diff --git a/third_party/rust/chromium_crates_io/vendor/zip-v2/src/path.rs b/third_party/rust/chromium_crates_io/vendor/zip-v3/src/path.rs
similarity index 100%
rename from third_party/rust/chromium_crates_io/vendor/zip-v2/src/path.rs
rename to third_party/rust/chromium_crates_io/vendor/zip-v3/src/path.rs
diff --git a/third_party/rust/chromium_crates_io/vendor/zip-v2/src/read.rs b/third_party/rust/chromium_crates_io/vendor/zip-v3/src/read.rs
similarity index 99%
rename from third_party/rust/chromium_crates_io/vendor/zip-v2/src/read.rs
rename to third_party/rust/chromium_crates_io/vendor/zip-v3/src/read.rs
index 604c81c..f0212aa 100644
--- a/third_party/rust/chromium_crates_io/vendor/zip-v2/src/read.rs
+++ b/third_party/rust/chromium_crates_io/vendor/zip-v3/src/read.rs
@@ -190,10 +190,7 @@
 
 #[cold]
 fn invalid_state<T>() -> io::Result<T> {
-    Err(io::Error::new(
-        io::ErrorKind::Other,
-        "ZipFileReader was in an invalid state",
-    ))
+    Err(io::Error::other("ZipFileReader was in an invalid state"))
 }
 
 pub(crate) enum ZipFileReader<'a, R: Read> {
@@ -301,7 +298,7 @@
                     .inner
                     .seek(SeekFrom::Start(self.inner_starting_offset + clamped_offset))?;
                 self.current_offset = new_inner_offset - self.inner_starting_offset;
-                Ok(new_inner_offset)
+                Ok(self.current_offset)
             }
         }
     }
@@ -2146,7 +2143,7 @@
         ZipArchive::new(Cursor::new(v)).expect_err("Invalid file");
     }
 
-    #[cfg(feature = "_deflate-any")]
+    #[cfg(feature = "deflate-flate2")]
     #[test]
     fn test_read_with_data_descriptor() {
         use std::io::Read;
@@ -2172,7 +2169,7 @@
     }
 
     #[test]
-    #[cfg(feature = "_deflate-any")]
+    #[cfg(feature = "deflate-flate2")]
     fn test_utf8_extra_field() {
         let mut v = Vec::new();
         v.extend_from_slice(include_bytes!("../tests/data/chinese.zip"));
diff --git a/third_party/rust/chromium_crates_io/vendor/zip-v2/src/read/config.rs b/third_party/rust/chromium_crates_io/vendor/zip-v3/src/read/config.rs
similarity index 100%
rename from third_party/rust/chromium_crates_io/vendor/zip-v2/src/read/config.rs
rename to third_party/rust/chromium_crates_io/vendor/zip-v3/src/read/config.rs
diff --git a/third_party/rust/chromium_crates_io/vendor/zip-v2/src/read/lzma.rs b/third_party/rust/chromium_crates_io/vendor/zip-v3/src/read/lzma.rs
similarity index 100%
rename from third_party/rust/chromium_crates_io/vendor/zip-v2/src/read/lzma.rs
rename to third_party/rust/chromium_crates_io/vendor/zip-v3/src/read/lzma.rs
diff --git a/third_party/rust/chromium_crates_io/vendor/zip-v2/src/read/magic_finder.rs b/third_party/rust/chromium_crates_io/vendor/zip-v3/src/read/magic_finder.rs
similarity index 100%
rename from third_party/rust/chromium_crates_io/vendor/zip-v2/src/read/magic_finder.rs
rename to third_party/rust/chromium_crates_io/vendor/zip-v3/src/read/magic_finder.rs
diff --git a/third_party/rust/chromium_crates_io/vendor/zip-v2/src/read/stream.rs b/third_party/rust/chromium_crates_io/vendor/zip-v3/src/read/stream.rs
similarity index 100%
rename from third_party/rust/chromium_crates_io/vendor/zip-v2/src/read/stream.rs
rename to third_party/rust/chromium_crates_io/vendor/zip-v3/src/read/stream.rs
diff --git a/third_party/rust/chromium_crates_io/vendor/zip-v2/src/result.rs b/third_party/rust/chromium_crates_io/vendor/zip-v3/src/result.rs
similarity index 98%
rename from third_party/rust/chromium_crates_io/vendor/zip-v2/src/result.rs
rename to third_party/rust/chromium_crates_io/vendor/zip-v3/src/result.rs
index b12ab7e..550f2bda 100644
--- a/third_party/rust/chromium_crates_io/vendor/zip-v2/src/result.rs
+++ b/third_party/rust/chromium_crates_io/vendor/zip-v3/src/result.rs
@@ -51,8 +51,8 @@
     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
         match self {
             Self::Io(_) => f.write_str("i/o error"),
-            Self::InvalidArchive(e) => write!(f, "invalid Zip archive: {}", e),
-            Self::UnsupportedArchive(e) => write!(f, "unsupported Zip archive: {}", e),
+            Self::InvalidArchive(e) => write!(f, "invalid Zip archive: {e}"),
+            Self::UnsupportedArchive(e) => write!(f, "unsupported Zip archive: {e}"),
             Self::FileNotFound => f.write_str("specified file not found in archive"),
             Self::InvalidPassword => f.write_str("provided password is incorrect"),
         }
diff --git a/third_party/rust/chromium_crates_io/vendor/zip-v2/src/spec.rs b/third_party/rust/chromium_crates_io/vendor/zip-v3/src/spec.rs
similarity index 100%
rename from third_party/rust/chromium_crates_io/vendor/zip-v2/src/spec.rs
rename to third_party/rust/chromium_crates_io/vendor/zip-v3/src/spec.rs
diff --git a/third_party/rust/chromium_crates_io/vendor/zip-v2/src/types.rs b/third_party/rust/chromium_crates_io/vendor/zip-v3/src/types.rs
similarity index 100%
rename from third_party/rust/chromium_crates_io/vendor/zip-v2/src/types.rs
rename to third_party/rust/chromium_crates_io/vendor/zip-v3/src/types.rs
diff --git a/third_party/rust/chromium_crates_io/vendor/zip-v2/src/unstable.rs b/third_party/rust/chromium_crates_io/vendor/zip-v3/src/unstable.rs
similarity index 100%
rename from third_party/rust/chromium_crates_io/vendor/zip-v2/src/unstable.rs
rename to third_party/rust/chromium_crates_io/vendor/zip-v3/src/unstable.rs
diff --git a/third_party/rust/chromium_crates_io/vendor/zip-v2/src/write.rs b/third_party/rust/chromium_crates_io/vendor/zip-v3/src/write.rs
similarity index 98%
rename from third_party/rust/chromium_crates_io/vendor/zip-v2/src/write.rs
rename to third_party/rust/chromium_crates_io/vendor/zip-v3/src/write.rs
index 0afd140..63e724de 100644
--- a/third_party/rust/chromium_crates_io/vendor/zip-v2/src/write.rs
+++ b/third_party/rust/chromium_crates_io/vendor/zip-v3/src/write.rs
@@ -13,7 +13,7 @@
     ZipRawValues, MIN_VERSION,
 };
 use crate::write::ffi::S_IFLNK;
-#[cfg(any(feature = "_deflate-any", feature = "bzip2", feature = "zstd",))]
+#[cfg(feature = "deflate-zopfli")]
 use core::num::NonZeroU64;
 use crc32fast::Hasher;
 use indexmap::IndexMap;
@@ -105,7 +105,7 @@
     fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
         match self {
             Closed => f.write_str("Closed"),
-            Storer(w) => f.write_fmt(format_args!("Storer({:?})", w)),
+            Storer(w) => f.write_fmt(format_args!("Storer({w:?})")),
             #[cfg(feature = "deflate-flate2")]
             GenericZipWriter::Deflater(w) => {
                 f.write_fmt(format_args!("Deflater({:?})", w.get_ref()))
@@ -329,8 +329,7 @@
             return Ok(());
         }
         if len > u16::MAX as u64 {
-            return Err(ZipError::Io(io::Error::new(
-                io::ErrorKind::Other,
+            return Err(ZipError::Io(io::Error::other(
                 "Extra-data field can't exceed u16::MAX bytes",
             )));
         }
@@ -338,8 +337,7 @@
         let mut pos = data.position();
         while pos < len {
             if len - data.position() < 4 {
-                return Err(ZipError::Io(io::Error::new(
-                    io::ErrorKind::Other,
+                return Err(ZipError::Io(io::Error::other(
                     "Extra-data field doesn't have room for ID and length",
                 )));
             }
@@ -348,8 +346,7 @@
                 use crate::unstable::LittleEndianReadExt;
                 let header_id = data.read_u16_le()?;
                 if EXTRA_FIELD_MAPPING.contains(&header_id) {
-                    return Err(ZipError::Io(io::Error::new(
-                        io::ErrorKind::Other,
+                    return Err(ZipError::Io(io::Error::other(
                         format!(
                             "Extra data header ID {header_id:#06} requires crate feature \"unreserved\"",
                         ),
@@ -571,10 +568,7 @@
 impl<W: Write + Seek> Write for ZipWriter<W> {
     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
         if !self.writing_to_file {
-            return Err(io::Error::new(
-                io::ErrorKind::Other,
-                "No file has been started",
-            ));
+            return Err(io::Error::other("No file has been started"));
         }
         if buf.is_empty() {
             return Ok(0);
@@ -588,10 +582,7 @@
                         && !self.files.last_mut().unwrap().1.large_file
                     {
                         let _ = self.abort_file();
-                        return Err(io::Error::new(
-                            io::ErrorKind::Other,
-                            "Large file option has not been set",
-                        ));
+                        return Err(io::Error::other("Large file option has not been set"));
                     }
                 }
                 write_result
@@ -755,6 +746,8 @@
     ///
     ///```
     /// # fn main() -> Result<(), zip::result::ZipError> {
+    /// # #[cfg(any(feature = "deflate-flate2", not(feature = "_deflate-any")))]
+    /// # {
     /// use std::io::{Cursor, prelude::*};
     /// use zip::{ZipArchive, ZipWriter, write::SimpleFileOptions};
     ///
@@ -768,6 +761,7 @@
     /// let mut s: String = String::new();
     /// zip.by_name("a.txt")?.read_to_string(&mut s)?;
     /// assert_eq!(s, "hello\n");
+    /// # }
     /// # Ok(())
     /// # }
     ///```
@@ -874,10 +868,7 @@
     /// the underlying [Write] is written independently and you need to adjust the zip metadata.
     pub unsafe fn set_file_metadata(&mut self, length: u64, crc32: u32) -> ZipResult<()> {
         if !self.writing_to_file {
-            return Err(ZipError::Io(io::Error::new(
-                io::ErrorKind::Other,
-                "No file has been started",
-            )));
+            return Err(ZipError::Io(io::Error::other("No file has been started")));
         }
         self.stats.hasher = Hasher::new_with_initial_len(crc32, length);
         self.stats.bytes_written = length;
@@ -1192,6 +1183,8 @@
     ///
     ///```
     /// # fn main() -> Result<(), zip::result::ZipError> {
+    /// # #[cfg(any(feature = "deflate-flate2", not(feature = "_deflate-any")))]
+    /// # {
     /// use std::io::{Cursor, prelude::*};
     /// use zip::{ZipArchive, ZipWriter, write::SimpleFileOptions};
     ///
@@ -1208,6 +1201,7 @@
     /// let src2 = ZipArchive::new(zip.finish()?)?;
     ///
     /// let buf = Cursor::new(Vec::new());
+    ///
     /// let mut zip = ZipWriter::new(buf);
     /// zip.merge_archive(src)?;
     /// zip.merge_archive(src2)?;
@@ -1219,6 +1213,7 @@
     /// s.clear();
     /// result.by_name("b.txt")?.read_to_string(&mut s)?;
     /// assert_eq!(s, "hey\n");
+    /// # }
     /// # Ok(())
     /// # }
     ///```
@@ -1626,7 +1621,7 @@
     fn drop(&mut self) {
         if !self.inner.is_closed() {
             if let Err(e) = self.finalize() {
-                let _ = write!(io::stderr(), "ZipWriter drop failed: {:?}", e);
+                let _ = write!(io::stderr(), "ZipWriter drop failed: {e:?}");
             }
         }
     }
@@ -1660,14 +1655,11 @@
                 }
                 #[cfg(feature = "_deflate-any")]
                 CompressionMethod::Deflated => {
-                    let default = if cfg!(all(
-                        feature = "deflate-zopfli",
-                        not(feature = "deflate-flate2")
-                    )) {
-                        24
-                    } else {
-                        Compression::default().level() as i64
-                    };
+                    #[cfg(feature = "deflate-flate2")]
+                    let default = Compression::default().level() as i64;
+
+                    #[cfg(all(feature = "deflate-zopfli", not(feature = "deflate-flate2")))]
+                    let default = 24;
 
                     let level = clamp_opt(
                         compression_level.unwrap_or(default),
@@ -1677,12 +1669,11 @@
                         as u32;
 
                     #[cfg(feature = "deflate-zopfli")]
-                    {
-                        let best_non_zopfli = Compression::best().level();
-                        if level > best_non_zopfli {
+                    macro_rules! deflate_zopfli_and_return {
+                        ($bare:expr, $best_non_zopfli:expr) => {
                             let options = Options {
                                 iteration_count: NonZeroU64::try_from(
-                                    (level - best_non_zopfli) as u64,
+                                    (level - $best_non_zopfli) as u64,
                                 )
                                 .unwrap(),
                                 ..Default::default()
@@ -1702,9 +1693,23 @@
                                     zopfli::DeflateEncoder::new(options, Default::default(), bare),
                                 ),
                             }));
+                        };
+                    }
+
+                    #[cfg(all(feature = "deflate-zopfli", feature = "deflate-flate2"))]
+                    {
+                        let best_non_zopfli = Compression::best().level();
+                        if level > best_non_zopfli {
+                            deflate_zopfli_and_return!(bare, best_non_zopfli);
                         }
                     }
 
+                    #[cfg(all(feature = "deflate-zopfli", not(feature = "deflate-flate2")))]
+                    {
+                        let best_non_zopfli = 9;
+                        deflate_zopfli_and_return!(bare, best_non_zopfli);
+                    }
+
                     #[cfg(feature = "deflate-flate2")]
                     {
                         Ok(Box::new(move |bare| {
@@ -1838,18 +1843,15 @@
 
 #[cfg(feature = "_deflate-any")]
 fn deflate_compression_level_range() -> std::ops::RangeInclusive<i64> {
-    let min = if cfg!(feature = "deflate-flate2") {
-        Compression::fast().level() as i64
-    } else {
-        Compression::best().level() as i64 + 1
-    };
+    #[cfg(feature = "deflate-flate2")]
+    let min = Compression::fast().level() as i64;
+    #[cfg(all(feature = "deflate-zopfli", not(feature = "deflate-flate2")))]
+    let min = 1;
 
-    let max = Compression::best().level() as i64
-        + if cfg!(feature = "deflate-zopfli") {
-            u8::MAX as i64
-        } else {
-            0
-        };
+    #[cfg(feature = "deflate-zopfli")]
+    let max = 264;
+    #[cfg(all(feature = "deflate-flate2", not(feature = "deflate-zopfli")))]
+    let max = Compression::best().level() as i64;
 
     min..=max
 }
@@ -1927,8 +1929,7 @@
     } else {
         // check compressed size as well as it can also be slightly larger than uncompressed size
         if file.compressed_size > spec::ZIP64_BYTES_THR {
-            return Err(ZipError::Io(io::Error::new(
-                io::ErrorKind::Other,
+            return Err(ZipError::Io(io::Error::other(
                 "Large file option has not been set",
             )));
         }
@@ -2001,7 +2002,9 @@
     use crate::zipcrypto::ZipCryptoKeys;
     use crate::CompressionMethod::Stored;
     use crate::ZipArchive;
-    use std::io::{Cursor, Read, Write};
+    #[cfg(feature = "deflate-flate2")]
+    use std::io::Read;
+    use std::io::{Cursor, Write};
     use std::marker::PhantomData;
     use std::path::PathBuf;
 
@@ -2156,14 +2159,18 @@
         assert_eq!(result.get_ref(), &v);
     }
 
+    #[cfg(feature = "deflate-flate2")]
     const RT_TEST_TEXT: &str = "And I can't stop thinking about the moments that I lost to you\
                             And I can't stop thinking of things I used to do\
                             And I can't stop making bad decisions\
                             And I can't stop eating stuff you make me chew\
                             I put on a smile like you wanna see\
                             Another day goes by that I long to be like you";
+    #[cfg(feature = "deflate-flate2")]
     const RT_TEST_FILENAME: &str = "subfolder/sub-subfolder/can't_stop.txt";
+    #[cfg(feature = "deflate-flate2")]
     const SECOND_FILENAME: &str = "different_name.xyz";
+    #[cfg(feature = "deflate-flate2")]
     const THIRD_FILENAME: &str = "third_name.xyz";
 
     #[test]
@@ -2219,6 +2226,7 @@
     }
 
     #[test]
+    #[cfg(feature = "deflate-flate2")]
     fn test_shallow_copy() {
         let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
         let options = FileOptions {
@@ -2269,6 +2277,7 @@
     }
 
     #[test]
+    #[cfg(feature = "deflate-flate2")]
     fn test_deep_copy() {
         let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
         let options = FileOptions {
@@ -2289,7 +2298,7 @@
             .deep_copy_file(RT_TEST_FILENAME, SECOND_FILENAME)
             .unwrap();
         let zip = writer.finish().unwrap().into_inner();
-        zip.iter().copied().for_each(|x| print!("{:02x}", x));
+        zip.iter().copied().for_each(|x| print!("{x:02x}"));
         println!();
         let mut writer = ZipWriter::new_append(Cursor::new(zip)).unwrap();
         writer
@@ -2440,6 +2449,7 @@
     }
 
     #[test]
+    #[cfg(feature = "deflate-flate2")]
     fn remove_shallow_copy_keeps_original() -> ZipResult<()> {
         let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
         writer
diff --git a/third_party/rust/chromium_crates_io/vendor/zip-v2/src/zipcrypto.rs b/third_party/rust/chromium_crates_io/vendor/zip-v3/src/zipcrypto.rs
similarity index 100%
rename from third_party/rust/chromium_crates_io/vendor/zip-v2/src/zipcrypto.rs
rename to third_party/rust/chromium_crates_io/vendor/zip-v3/src/zipcrypto.rs
diff --git a/third_party/rust/clap/v4/BUILD.gn b/third_party/rust/clap/v4/BUILD.gn
index d18d4fad..f8ebeae 100644
--- a/third_party/rust/clap/v4/BUILD.gn
+++ b/third_party/rust/clap/v4/BUILD.gn
@@ -151,9 +151,12 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "4.5.37"
   cargo_pkg_name = "clap"
   cargo_pkg_description = "A simple to use, efficient, and full-featured Command Line Argument Parser"
+  cargo_pkg_version = "4.5.37"
+
+  allow_unsafe = false
+
   deps = [ "//third_party/rust/clap_builder/v4:lib" ]
   features = [
     "error-context",
diff --git a/third_party/rust/clap_builder/v4/BUILD.gn b/third_party/rust/clap_builder/v4/BUILD.gn
index 1b4b067..a6c659e 100644
--- a/third_party/rust/clap_builder/v4/BUILD.gn
+++ b/third_party/rust/clap_builder/v4/BUILD.gn
@@ -76,9 +76,12 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "4.5.37"
   cargo_pkg_name = "clap_builder"
   cargo_pkg_description = "A simple to use, efficient, and full-featured Command Line Argument Parser"
+  cargo_pkg_version = "4.5.37"
+
+  allow_unsafe = false
+
   deps = [
     "//third_party/rust/anstyle/v1:lib",
     "//third_party/rust/clap_lex/v0_7:lib",
diff --git a/third_party/rust/clap_lex/v0_7/BUILD.gn b/third_party/rust/clap_lex/v0_7/BUILD.gn
index 0c279395..fe14959 100644
--- a/third_party/rust/clap_lex/v0_7/BUILD.gn
+++ b/third_party/rust/clap_lex/v0_7/BUILD.gn
@@ -22,9 +22,11 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.7.4"
   cargo_pkg_name = "clap_lex"
   cargo_pkg_description = "Minimal, flexible command line parser"
+  cargo_pkg_version = "0.7.4"
+
+  allow_unsafe = true
 
   # Only for usage from third-party crates. Add the crate to
   # //third_party/rust/chromium_crates_io/Cargo.toml to use
diff --git a/third_party/rust/codespan_reporting/v0_12/BUILD.gn b/third_party/rust/codespan_reporting/v0_12/BUILD.gn
index ca61cfa..e985ce7 100644
--- a/third_party/rust/codespan_reporting/v0_12/BUILD.gn
+++ b/third_party/rust/codespan_reporting/v0_12/BUILD.gn
@@ -26,11 +26,14 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.12.0"
   cargo_pkg_authors = "Brendan Zabarauskas <bjzaba@yahoo.com.au>"
   cargo_pkg_name = "codespan-reporting"
   cargo_pkg_description =
       "Beautiful diagnostic reporting for text-based programming languages"
+  cargo_pkg_version = "0.12.0"
+
+  allow_unsafe = false
+
   deps = [
     "//third_party/rust/termcolor/v1:lib",
     "//third_party/rust/unicode_width/v0_1:lib",
diff --git a/third_party/rust/core_foundation_sys/v0_8/BUILD.gn b/third_party/rust/core_foundation_sys/v0_8/BUILD.gn
index 5617002..020aa447 100644
--- a/third_party/rust/core_foundation_sys/v0_8/BUILD.gn
+++ b/third_party/rust/core_foundation_sys/v0_8/BUILD.gn
@@ -60,10 +60,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2018"
-  cargo_pkg_version = "0.8.7"
   cargo_pkg_authors = "The Servo Project Developers"
   cargo_pkg_name = "core-foundation-sys"
   cargo_pkg_description = "Bindings to Core Foundation for macOS"
+  cargo_pkg_version = "0.8.7"
+
+  allow_unsafe = true
+
   features = [
     "default",
     "link",
diff --git a/third_party/rust/core_maths/v0_1/BUILD.gn b/third_party/rust/core_maths/v0_1/BUILD.gn
index ea20f94f..98c52d0 100644
--- a/third_party/rust/core_maths/v0_1/BUILD.gn
+++ b/third_party/rust/core_maths/v0_1/BUILD.gn
@@ -21,10 +21,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2015"
-  cargo_pkg_version = "0.1.1"
   cargo_pkg_authors = "Robert Bastian <me@robertbastian.dev"
   cargo_pkg_name = "core_maths"
   cargo_pkg_description = "Extension trait for full float functionality in `#[no_std]` backed by `libm`."
+  cargo_pkg_version = "0.1.1"
+
+  allow_unsafe = false
+
   deps = [ "//third_party/rust/libm/v0_2:lib" ]
 
   # Only for usage from third-party crates. Add the crate to
diff --git a/third_party/rust/crc32fast/v1/BUILD.gn b/third_party/rust/crc32fast/v1/BUILD.gn
index 6c231a9..ba67fd5 100644
--- a/third_party/rust/crc32fast/v1/BUILD.gn
+++ b/third_party/rust/crc32fast/v1/BUILD.gn
@@ -27,12 +27,15 @@
 
   build_native_rust_unit_tests = false
   edition = "2015"
-  cargo_pkg_version = "1.4.2"
   cargo_pkg_authors =
       "Sam Rijs <srijs@airpost.net>, Alex Crichton <alex@alexcrichton.com>"
   cargo_pkg_name = "crc32fast"
   cargo_pkg_description =
       "Fast, SIMD-accelerated CRC32 (IEEE) checksum computation"
+  cargo_pkg_version = "1.4.2"
+
+  allow_unsafe = true
+
   deps = [ "//third_party/rust/cfg_if/v1:lib" ]
   features = [
     "default",
diff --git a/third_party/rust/crossbeam_utils/v0_8/BUILD.gn b/third_party/rust/crossbeam_utils/v0_8/BUILD.gn
index 0928e0e..af39994 100644
--- a/third_party/rust/crossbeam_utils/v0_8/BUILD.gn
+++ b/third_party/rust/crossbeam_utils/v0_8/BUILD.gn
@@ -34,9 +34,12 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.8.21"
   cargo_pkg_name = "crossbeam-utils"
   cargo_pkg_description = "Utilities for concurrent programming"
+  cargo_pkg_version = "0.8.21"
+
+  allow_unsafe = false
+
   features = [
     "default",
     "std",
diff --git a/third_party/rust/crossbeam_utils/v0_8/README.chromium b/third_party/rust/crossbeam_utils/v0_8/README.chromium
deleted file mode 100644
index 04d19da..0000000
--- a/third_party/rust/crossbeam_utils/v0_8/README.chromium
+++ /dev/null
@@ -1,10 +0,0 @@
-Name: crossbeam-utils
-URL: https://crates.io/crates/crossbeam-utils
-Version: 0.8.21
-Revision: ccd83ac4108a2a1b41e9c6e79c87267167d18dfa
-License: Apache-2.0
-License File: //third_party/rust/chromium_crates_io/vendor/crossbeam-utils-v0_8/LICENSE-APACHE
-Shipped: yes
-Security Critical: yes
-
-Description: Utilities for concurrent programming
diff --git a/third_party/rust/cxx/v1/BUILD.gn b/third_party/rust/cxx/v1/BUILD.gn
index 2e7b3717..2b02fb4 100644
--- a/third_party/rust/cxx/v1/BUILD.gn
+++ b/third_party/rust/cxx/v1/BUILD.gn
@@ -50,10 +50,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "1.0.158"
   cargo_pkg_authors = "David Tolnay <dtolnay@gmail.com>"
   cargo_pkg_name = "cxx"
   cargo_pkg_description = "Safe interop between Rust and C++"
+  cargo_pkg_version = "1.0.158"
+
+  allow_unsafe = true
+
   deps = [
     "//third_party/rust/cxxbridge_macro/v1:lib",
     "//third_party/rust/foldhash/v0_1:lib",
diff --git a/third_party/rust/cxxbridge_cmd/v1/BUILD.gn b/third_party/rust/cxxbridge_cmd/v1/BUILD.gn
index 6cd4044..23c58d8 100644
--- a/third_party/rust/cxxbridge_cmd/v1/BUILD.gn
+++ b/third_party/rust/cxxbridge_cmd/v1/BUILD.gn
@@ -68,11 +68,14 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "1.0.158"
   cargo_pkg_authors = "David Tolnay <dtolnay@gmail.com>"
   cargo_pkg_name = "cxxbridge-cmd"
   cargo_pkg_description =
       "C++ code generator for integrating `cxx` crate into a non-Cargo build."
+  cargo_pkg_version = "1.0.158"
+
+  allow_unsafe = false
+
   deps = [
     "//third_party/rust/clap/v4:lib",
     "//third_party/rust/codespan_reporting/v0_12:lib",
diff --git a/third_party/rust/cxxbridge_flags/v1/BUILD.gn b/third_party/rust/cxxbridge_flags/v1/BUILD.gn
index e94cf6d7..da6beac 100644
--- a/third_party/rust/cxxbridge_flags/v1/BUILD.gn
+++ b/third_party/rust/cxxbridge_flags/v1/BUILD.gn
@@ -21,11 +21,14 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "1.0.158"
   cargo_pkg_authors = "David Tolnay <dtolnay@gmail.com>"
   cargo_pkg_name = "cxxbridge-flags"
   cargo_pkg_description =
       "Compiler configuration of the `cxx` crate (implementation detail)"
+  cargo_pkg_version = "1.0.158"
+
+  allow_unsafe = false
+
   features = [ "default" ]
 
   # Only for usage from third-party crates. Add the crate to
diff --git a/third_party/rust/cxxbridge_macro/v1/BUILD.gn b/third_party/rust/cxxbridge_macro/v1/BUILD.gn
index 8e1f951..80e9781 100644
--- a/third_party/rust/cxxbridge_macro/v1/BUILD.gn
+++ b/third_party/rust/cxxbridge_macro/v1/BUILD.gn
@@ -58,10 +58,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "1.0.158"
   cargo_pkg_authors = "David Tolnay <dtolnay@gmail.com>"
   cargo_pkg_name = "cxxbridge-macro"
   cargo_pkg_description = "Implementation detail of the `cxx` crate."
+  cargo_pkg_version = "1.0.158"
+
+  allow_unsafe = false
+
   deps = [
     "//third_party/rust/proc_macro2/v1:lib",
     "//third_party/rust/quote/v1:lib",
diff --git a/third_party/rust/derivre/v0_3/BUILD.gn b/third_party/rust/derivre/v0_3/BUILD.gn
index ed85e54..6a827e0 100644
--- a/third_party/rust/derivre/v0_3/BUILD.gn
+++ b/third_party/rust/derivre/v0_3/BUILD.gn
@@ -34,10 +34,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.3.7"
   cargo_pkg_authors = "Michal Moskal <michal@moskal.me>"
   cargo_pkg_name = "derivre"
   cargo_pkg_description = "A derivative-based regular expression engine"
+  cargo_pkg_version = "0.3.7"
+
+  allow_unsafe = false
+
   deps = [
     "//third_party/rust/anyhow/v1:lib",
     "//third_party/rust/bytemuck/v1:lib",
diff --git a/third_party/rust/diplomat/v0_10/BUILD.gn b/third_party/rust/diplomat/v0_10/BUILD.gn
index b8bbab2..fc140b1 100644
--- a/third_party/rust/diplomat/v0_10/BUILD.gn
+++ b/third_party/rust/diplomat/v0_10/BUILD.gn
@@ -23,10 +23,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.10.0"
   cargo_pkg_authors = "Shadaj Laddad <shadaj@users.noreply.github.com>, Manish Goregaokar <manishsmail@gmail.com>, Quinn Okabayashi <QnnOkabayashi@users.noreply.github.com>"
   cargo_pkg_name = "diplomat"
   cargo_pkg_description = "The diplomat FFI generation macro"
+  cargo_pkg_version = "0.10.0"
+
+  allow_unsafe = false
+
   deps = [
     "//third_party/rust/diplomat_core/v0_10:lib",
     "//third_party/rust/proc_macro2/v1:lib",
diff --git a/third_party/rust/diplomat_core/v0_10/BUILD.gn b/third_party/rust/diplomat_core/v0_10/BUILD.gn
index b591b64..1e5fdd23 100644
--- a/third_party/rust/diplomat_core/v0_10/BUILD.gn
+++ b/third_party/rust/diplomat_core/v0_10/BUILD.gn
@@ -48,11 +48,14 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.10.0"
   cargo_pkg_authors = "Shadaj Laddad <shadaj@users.noreply.github.com>, Manish Goregaokar <manishsmail@gmail.com>, Quinn Okabayashi <QnnOkabayashi@users.noreply.github.com>"
   cargo_pkg_name = "diplomat_core"
   cargo_pkg_description =
       "Shared utilities between Diplomat macros and code generation"
+  cargo_pkg_version = "0.10.0"
+
+  allow_unsafe = false
+
   deps = [
     "//third_party/rust/proc_macro2/v1:lib",
     "//third_party/rust/quote/v1:lib",
diff --git a/third_party/rust/diplomat_runtime/v0_10/BUILD.gn b/third_party/rust/diplomat_runtime/v0_10/BUILD.gn
index a3250a8..4139e82 100644
--- a/third_party/rust/diplomat_runtime/v0_10/BUILD.gn
+++ b/third_party/rust/diplomat_runtime/v0_10/BUILD.gn
@@ -25,10 +25,12 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.10.0"
   cargo_pkg_authors = "Shadaj Laddad <shadaj@users.noreply.github.com>, Manish Goregaokar <manishsmail@gmail.com>, Quinn Okabayashi <QnnOkabayashi@users.noreply.github.com>"
   cargo_pkg_name = "diplomat-runtime"
   cargo_pkg_description = "Common runtime utilities used by diplomat codegen"
+  cargo_pkg_version = "0.10.0"
+
+  allow_unsafe = true
 
   # Only for usage from third-party crates. Add the crate to
   # //third_party/rust/chromium_crates_io/Cargo.toml to use
diff --git a/third_party/rust/displaydoc/v0_2/BUILD.gn b/third_party/rust/displaydoc/v0_2/BUILD.gn
index de3a67b..8ce6626 100644
--- a/third_party/rust/displaydoc/v0_2/BUILD.gn
+++ b/third_party/rust/displaydoc/v0_2/BUILD.gn
@@ -24,10 +24,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.2.5"
   cargo_pkg_authors = "Jane Lusby <jlusby@yaah.dev>"
   cargo_pkg_name = "displaydoc"
   cargo_pkg_description = "A derive macro for implementing the display Trait via a doc comment and string interpolation"
+  cargo_pkg_version = "0.2.5"
+
+  allow_unsafe = false
+
   deps = [
     "//third_party/rust/proc_macro2/v1:lib",
     "//third_party/rust/quote/v1:lib",
diff --git a/third_party/rust/either/v1/BUILD.gn b/third_party/rust/either/v1/BUILD.gn
index f1fb2dbd..caf9a81 100644
--- a/third_party/rust/either/v1/BUILD.gn
+++ b/third_party/rust/either/v1/BUILD.gn
@@ -25,10 +25,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "1.15.0"
   cargo_pkg_authors = "bluss"
   cargo_pkg_name = "either"
   cargo_pkg_description = "The enum `Either` with variants `Left` and `Right` is a general purpose sum type with two cases."
+  cargo_pkg_version = "1.15.0"
+
+  allow_unsafe = true
+
   features = [
     "std",
     "use_std",
diff --git a/third_party/rust/equivalent/v1/BUILD.gn b/third_party/rust/equivalent/v1/BUILD.gn
index 8c47bc7..23092c5 100644
--- a/third_party/rust/equivalent/v1/BUILD.gn
+++ b/third_party/rust/equivalent/v1/BUILD.gn
@@ -21,9 +21,11 @@
 
   build_native_rust_unit_tests = false
   edition = "2015"
-  cargo_pkg_version = "1.0.2"
   cargo_pkg_name = "equivalent"
   cargo_pkg_description = "Traits for key comparison in maps."
+  cargo_pkg_version = "1.0.2"
+
+  allow_unsafe = false
 
   # Only for usage from third-party crates. Add the crate to
   # //third_party/rust/chromium_crates_io/Cargo.toml to use
diff --git a/third_party/rust/fdeflate/v0_3/BUILD.gn b/third_party/rust/fdeflate/v0_3/BUILD.gn
index 58511130..6d1fae5 100644
--- a/third_party/rust/fdeflate/v0_3/BUILD.gn
+++ b/third_party/rust/fdeflate/v0_3/BUILD.gn
@@ -26,10 +26,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.3.7"
   cargo_pkg_authors = "The image-rs Developers"
   cargo_pkg_name = "fdeflate"
   cargo_pkg_description = "Fast specialized deflate implementation"
+  cargo_pkg_version = "0.3.7"
+
+  allow_unsafe = false
+
   deps = [ "//third_party/rust/simd_adler32/v0_3:lib" ]
 
   # Only for usage from third-party crates. Add the crate to
diff --git a/third_party/rust/fend_core/v1/BUILD.gn b/third_party/rust/fend_core/v1/BUILD.gn
index 89102ea..d4b8414 100644
--- a/third_party/rust/fend_core/v1/BUILD.gn
+++ b/third_party/rust/fend_core/v1/BUILD.gn
@@ -58,9 +58,11 @@
 
   build_native_rust_unit_tests = false
   edition = "2024"
-  cargo_pkg_version = "1.5.6"
   cargo_pkg_name = "fend-core"
   cargo_pkg_description = "Arbitrary-precision unit-aware calculator"
+  cargo_pkg_version = "1.5.6"
+
+  allow_unsafe = false
 
   #####################################################################
   # Tweaking which GN `config`s apply to this target.
diff --git a/third_party/rust/fixed_decimal/v0_7/BUILD.gn b/third_party/rust/fixed_decimal/v0_7/BUILD.gn
index 514eab6d..5e65845 100644
--- a/third_party/rust/fixed_decimal/v0_7/BUILD.gn
+++ b/third_party/rust/fixed_decimal/v0_7/BUILD.gn
@@ -29,11 +29,14 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.7.0"
   cargo_pkg_authors = "The ICU4X Project Developers"
   cargo_pkg_name = "fixed_decimal"
   cargo_pkg_description =
       "An API for representing numbers in a human-readable form"
+  cargo_pkg_version = "0.7.0"
+
+  allow_unsafe = false
+
   deps = [
     "//third_party/rust/displaydoc/v0_2:lib",
     "//third_party/rust/smallvec/v1:lib",
diff --git a/third_party/rust/flate2/v1/BUILD.gn b/third_party/rust/flate2/v1/BUILD.gn
index f83be7d4d..f3b5eb9e 100644
--- a/third_party/rust/flate2/v1/BUILD.gn
+++ b/third_party/rust/flate2/v1/BUILD.gn
@@ -40,10 +40,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2018"
-  cargo_pkg_version = "1.1.1"
   cargo_pkg_authors = "Alex Crichton <alex@alexcrichton.com>, Josh Triplett <josh@joshtriplett.org>"
   cargo_pkg_name = "flate2"
   cargo_pkg_description = "DEFLATE compression and decompression exposed as Read/BufRead/Write streams. Supports miniz_oxide and multiple zlib implementations. Supports zlib, gzip, and raw deflate streams."
+  cargo_pkg_version = "1.1.1"
+
+  allow_unsafe = false
+
   deps = [
     "//third_party/rust/crc32fast/v1:lib",
     "//third_party/rust/miniz_oxide/v0_8:lib",
diff --git a/third_party/rust/foldhash/v0_1/BUILD.gn b/third_party/rust/foldhash/v0_1/BUILD.gn
index 7458c02..158ae774 100644
--- a/third_party/rust/foldhash/v0_1/BUILD.gn
+++ b/third_party/rust/foldhash/v0_1/BUILD.gn
@@ -25,11 +25,14 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.1.5"
   cargo_pkg_authors = "Orson Peters <orsonpeters@gmail.com>"
   cargo_pkg_name = "foldhash"
   cargo_pkg_description =
       "A fast, non-cryptographic, minimally DoS-resistant hashing algorithm."
+  cargo_pkg_version = "0.1.5"
+
+  allow_unsafe = true
+
   features = [ "std" ]
 
   # Only for usage from third-party crates. Add the crate to
diff --git a/third_party/rust/font_types/v0_8/BUILD.gn b/third_party/rust/font_types/v0_8/BUILD.gn
index dc6a3efb..ee9e43c 100644
--- a/third_party/rust/font_types/v0_8/BUILD.gn
+++ b/third_party/rust/font_types/v0_8/BUILD.gn
@@ -35,9 +35,12 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.8.4"
   cargo_pkg_name = "font-types"
   cargo_pkg_description = "Scalar types used in fonts."
+  cargo_pkg_version = "0.8.4"
+
+  allow_unsafe = true
+
   deps = [ "//third_party/rust/bytemuck/v1:lib" ]
   features = [
     "bytemuck",
diff --git a/third_party/rust/hashbrown/v0_15/BUILD.gn b/third_party/rust/hashbrown/v0_15/BUILD.gn
index baed9657..96f1f359 100644
--- a/third_party/rust/hashbrown/v0_15/BUILD.gn
+++ b/third_party/rust/hashbrown/v0_15/BUILD.gn
@@ -47,10 +47,12 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  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"
+  cargo_pkg_version = "0.15.3"
+
+  allow_unsafe = true
 
   # Only for usage from third-party crates. Add the crate to
   # //third_party/rust/chromium_crates_io/Cargo.toml to use
diff --git a/third_party/rust/heck/v0_5/BUILD.gn b/third_party/rust/heck/v0_5/BUILD.gn
index 792123f..8a4563e 100644
--- a/third_party/rust/heck/v0_5/BUILD.gn
+++ b/third_party/rust/heck/v0_5/BUILD.gn
@@ -29,9 +29,11 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.5.0"
   cargo_pkg_name = "heck"
   cargo_pkg_description = "heck is a case conversion library."
+  cargo_pkg_version = "0.5.0"
+
+  allow_unsafe = false
 
   # Only for usage from third-party crates. Add the crate to
   # //third_party/rust/chromium_crates_io/Cargo.toml to use
diff --git a/third_party/rust/hex/v0_4/BUILD.gn b/third_party/rust/hex/v0_4/BUILD.gn
index d5172116..7194be4 100644
--- a/third_party/rust/hex/v0_4/BUILD.gn
+++ b/third_party/rust/hex/v0_4/BUILD.gn
@@ -23,11 +23,14 @@
 
   build_native_rust_unit_tests = false
   edition = "2018"
-  cargo_pkg_version = "0.4.3"
   cargo_pkg_authors = "KokaKiwi <kokakiwi@kokakiwi.net>"
   cargo_pkg_name = "hex"
   cargo_pkg_description =
       "Encoding and decoding data into/from hexadecimal representation."
+  cargo_pkg_version = "0.4.3"
+
+  allow_unsafe = false
+
   features = [
     "alloc",
     "default",
diff --git a/third_party/rust/iana_time_zone/v0_1/BUILD.gn b/third_party/rust/iana_time_zone/v0_1/BUILD.gn
index c74f2ff..e1b29e2 100644
--- a/third_party/rust/iana_time_zone/v0_1/BUILD.gn
+++ b/third_party/rust/iana_time_zone/v0_1/BUILD.gn
@@ -34,10 +34,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.1.63"
   cargo_pkg_authors = "Andrew Straw <strawman@astraw.com>, René Kijewski <rene.kijewski@fu-berlin.de>, Ryan Lopopolo <rjl@hyperbo.la>"
   cargo_pkg_name = "iana-time-zone"
   cargo_pkg_description = "get the IANA time zone for the current system"
+  cargo_pkg_version = "0.1.63"
+
+  allow_unsafe = true
+
   deps = []
   if (is_android) {
     deps += [ "//third_party/rust/android_system_properties/v0_1:lib" ]
diff --git a/third_party/rust/icu_calendar/v2/BUILD.gn b/third_party/rust/icu_calendar/v2/BUILD.gn
index e02e7cf..c654916 100644
--- a/third_party/rust/icu_calendar/v2/BUILD.gn
+++ b/third_party/rust/icu_calendar/v2/BUILD.gn
@@ -50,10 +50,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "2.0.0-beta2"
   cargo_pkg_authors = "The ICU4X Project Developers"
   cargo_pkg_name = "icu_calendar"
   cargo_pkg_description = "API for supporting various types of calendars"
+  cargo_pkg_version = "2.0.0-beta2"
+
+  allow_unsafe = false
+
   deps = [
     "//third_party/rust/calendrical_calculations/v0_1:lib",
     "//third_party/rust/displaydoc/v0_2:lib",
diff --git a/third_party/rust/icu_calendar_data/v2/BUILD.gn b/third_party/rust/icu_calendar_data/v2/BUILD.gn
index f6ddbf0..96f480ba 100644
--- a/third_party/rust/icu_calendar_data/v2/BUILD.gn
+++ b/third_party/rust/icu_calendar_data/v2/BUILD.gn
@@ -34,10 +34,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "2.0.0-beta2"
   cargo_pkg_authors = "The ICU4X Project Developers"
   cargo_pkg_name = "icu_calendar_data"
   cargo_pkg_description = "Data for the icu_calendar crate"
+  cargo_pkg_version = "2.0.0-beta2"
+
+  allow_unsafe = false
+
   deps = [
     "//third_party/rust/icu_locale/v2:lib",
     "//third_party/rust/icu_provider_baked/v2:lib",
diff --git a/third_party/rust/icu_capi/v2/BUILD.gn b/third_party/rust/icu_capi/v2/BUILD.gn
index b758a0d..cfbd228 100644
--- a/third_party/rust/icu_capi/v2/BUILD.gn
+++ b/third_party/rust/icu_capi/v2/BUILD.gn
@@ -66,10 +66,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "2.0.0-beta2"
   cargo_pkg_authors = "The ICU4X Project Developers"
   cargo_pkg_name = "icu_capi"
   cargo_pkg_description = "C interface to ICU4X"
+  cargo_pkg_version = "2.0.0-beta2"
+
+  allow_unsafe = false
+
   deps = [
     "//third_party/rust/diplomat/v0_10:lib",
     "//third_party/rust/diplomat_runtime/v0_10:lib",
diff --git a/third_party/rust/icu_casemap/v2/BUILD.gn b/third_party/rust/icu_casemap/v2/BUILD.gn
index 352da6b03..2b216e9 100644
--- a/third_party/rust/icu_casemap/v2/BUILD.gn
+++ b/third_party/rust/icu_casemap/v2/BUILD.gn
@@ -34,10 +34,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "2.0.0-beta2"
   cargo_pkg_authors = "The ICU4X Project Developers"
   cargo_pkg_name = "icu_casemap"
   cargo_pkg_description = "Unicode case mapping and folding algorithms"
+  cargo_pkg_version = "2.0.0-beta2"
+
+  allow_unsafe = false
+
   deps = [
     "//third_party/rust/displaydoc/v0_2:lib",
     "//third_party/rust/icu_casemap_data/v2:lib",
diff --git a/third_party/rust/icu_casemap_data/v2/BUILD.gn b/third_party/rust/icu_casemap_data/v2/BUILD.gn
index 7cbf5ea0..5bfe48e6 100644
--- a/third_party/rust/icu_casemap_data/v2/BUILD.gn
+++ b/third_party/rust/icu_casemap_data/v2/BUILD.gn
@@ -24,10 +24,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "2.0.0-beta2"
   cargo_pkg_authors = "The ICU4X Project Developers"
   cargo_pkg_name = "icu_casemap_data"
   cargo_pkg_description = "Data for the icu_casemap crate"
+  cargo_pkg_version = "2.0.0-beta2"
+
+  allow_unsafe = false
+
   deps = [ "//third_party/rust/icu_provider_baked/v2:lib" ]
 
   # Only for usage from third-party crates. Add the crate to
diff --git a/third_party/rust/icu_collections/v2/BUILD.gn b/third_party/rust/icu_collections/v2/BUILD.gn
index 7a88828..1772d21 100644
--- a/third_party/rust/icu_collections/v2/BUILD.gn
+++ b/third_party/rust/icu_collections/v2/BUILD.gn
@@ -36,10 +36,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "2.0.0-beta2"
   cargo_pkg_authors = "The ICU4X Project Developers"
   cargo_pkg_name = "icu_collections"
   cargo_pkg_description = "Collection of API for use in ICU libraries."
+  cargo_pkg_version = "2.0.0-beta2"
+
+  allow_unsafe = true
+
   deps = [
     "//third_party/rust/displaydoc/v0_2:lib",
     "//third_party/rust/potential_utf/v0_1:lib",
diff --git a/third_party/rust/icu_decimal/v2/BUILD.gn b/third_party/rust/icu_decimal/v2/BUILD.gn
index fea619b..1986f55 100644
--- a/third_party/rust/icu_decimal/v2/BUILD.gn
+++ b/third_party/rust/icu_decimal/v2/BUILD.gn
@@ -27,11 +27,14 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "2.0.0-beta2"
   cargo_pkg_authors = "The ICU4X Project Developers"
   cargo_pkg_name = "icu_decimal"
   cargo_pkg_description =
       "API for formatting basic decimal numbers in a locale-sensitive way"
+  cargo_pkg_version = "2.0.0-beta2"
+
+  allow_unsafe = false
+
   deps = [
     "//third_party/rust/displaydoc/v0_2:lib",
     "//third_party/rust/fixed_decimal/v0_7:lib",
diff --git a/third_party/rust/icu_decimal_data/v2/BUILD.gn b/third_party/rust/icu_decimal_data/v2/BUILD.gn
index 8c1f0ac..dfbd129 100644
--- a/third_party/rust/icu_decimal_data/v2/BUILD.gn
+++ b/third_party/rust/icu_decimal_data/v2/BUILD.gn
@@ -24,10 +24,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "2.0.0-beta2"
   cargo_pkg_authors = "The ICU4X Project Developers"
   cargo_pkg_name = "icu_decimal_data"
   cargo_pkg_description = "Data for the icu_decimal crate"
+  cargo_pkg_version = "2.0.0-beta2"
+
+  allow_unsafe = false
+
   deps = [
     "//third_party/rust/icu_locale/v2:lib",
     "//third_party/rust/icu_provider_baked/v2:lib",
diff --git a/third_party/rust/icu_experimental/v0_3/BUILD.gn b/third_party/rust/icu_experimental/v0_3/BUILD.gn
index ccc04010..573e5ee 100644
--- a/third_party/rust/icu_experimental/v0_3/BUILD.gn
+++ b/third_party/rust/icu_experimental/v0_3/BUILD.gn
@@ -115,10 +115,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.3.0-beta2"
   cargo_pkg_authors = "The ICU4X Project Developers"
   cargo_pkg_name = "icu_experimental"
   cargo_pkg_description = "ICU4X pre-release components under active development; all code in this crate is unstable."
+  cargo_pkg_version = "0.3.0-beta2"
+
+  allow_unsafe = false
+
   deps = [
     "//third_party/rust/displaydoc/v0_2:lib",
     "//third_party/rust/either/v1:lib",
diff --git a/third_party/rust/icu_experimental_data/v0_3/BUILD.gn b/third_party/rust/icu_experimental_data/v0_3/BUILD.gn
index 4396d45..2821680 100644
--- a/third_party/rust/icu_experimental_data/v0_3/BUILD.gn
+++ b/third_party/rust/icu_experimental_data/v0_3/BUILD.gn
@@ -108,10 +108,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.3.0-beta2"
   cargo_pkg_authors = "The ICU4X Project Developers"
   cargo_pkg_name = "icu_experimental_data"
   cargo_pkg_description = "Data for the icu_experimental crate"
+  cargo_pkg_version = "0.3.0-beta2"
+
+  allow_unsafe = false
+
   deps = [
     "//third_party/rust/icu_locale/v2:lib",
     "//third_party/rust/icu_provider_baked/v2:lib",
diff --git a/third_party/rust/icu_list/v2/BUILD.gn b/third_party/rust/icu_list/v2/BUILD.gn
index 1b7e8e5..ab8caf6 100644
--- a/third_party/rust/icu_list/v2/BUILD.gn
+++ b/third_party/rust/icu_list/v2/BUILD.gn
@@ -27,10 +27,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "2.0.0-beta2"
   cargo_pkg_authors = "The ICU4X Project Developers"
   cargo_pkg_name = "icu_list"
   cargo_pkg_description = "ECMA-402 ListFormatter"
+  cargo_pkg_version = "2.0.0-beta2"
+
+  allow_unsafe = false
+
   deps = [
     "//third_party/rust/displaydoc/v0_2:lib",
     "//third_party/rust/icu_list_data/v2:lib",
diff --git a/third_party/rust/icu_list_data/v2/BUILD.gn b/third_party/rust/icu_list_data/v2/BUILD.gn
index d8317f0..55ed5c2 100644
--- a/third_party/rust/icu_list_data/v2/BUILD.gn
+++ b/third_party/rust/icu_list_data/v2/BUILD.gn
@@ -29,10 +29,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "2.0.0-beta2"
   cargo_pkg_authors = "The ICU4X Project Developers"
   cargo_pkg_name = "icu_list_data"
   cargo_pkg_description = "Data for the icu_list crate"
+  cargo_pkg_version = "2.0.0-beta2"
+
+  allow_unsafe = false
+
   deps = [
     "//third_party/rust/icu_locale/v2:lib",
     "//third_party/rust/icu_provider_baked/v2:lib",
diff --git a/third_party/rust/icu_locale/v2/BUILD.gn b/third_party/rust/icu_locale/v2/BUILD.gn
index 3efd1f1..c2e011c 100644
--- a/third_party/rust/icu_locale/v2/BUILD.gn
+++ b/third_party/rust/icu_locale/v2/BUILD.gn
@@ -28,11 +28,14 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "2.0.0-beta2"
   cargo_pkg_authors = "The ICU4X Project Developers"
   cargo_pkg_name = "icu_locale"
   cargo_pkg_description =
       "API for Unicode Language and Locale Identifiers canonicalization"
+  cargo_pkg_version = "2.0.0-beta2"
+
+  allow_unsafe = true
+
   deps = [
     "//third_party/rust/displaydoc/v0_2:lib",
     "//third_party/rust/icu_collections/v2:lib",
diff --git a/third_party/rust/icu_locale_core/v2/BUILD.gn b/third_party/rust/icu_locale_core/v2/BUILD.gn
index a529c42..cc25772 100644
--- a/third_party/rust/icu_locale_core/v2/BUILD.gn
+++ b/third_party/rust/icu_locale_core/v2/BUILD.gn
@@ -82,11 +82,14 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "2.0.0-beta2"
   cargo_pkg_authors = "The ICU4X Project Developers"
   cargo_pkg_name = "icu_locale_core"
   cargo_pkg_description =
       "API for managing Unicode Language and Locale Identifiers"
+  cargo_pkg_version = "2.0.0-beta2"
+
+  allow_unsafe = true
+
   deps = [
     "//third_party/rust/displaydoc/v0_2:lib",
     "//third_party/rust/litemap/v0_7:lib",
diff --git a/third_party/rust/icu_locale_data/v2/BUILD.gn b/third_party/rust/icu_locale_data/v2/BUILD.gn
index b92a04aa..87f32b4 100644
--- a/third_party/rust/icu_locale_data/v2/BUILD.gn
+++ b/third_party/rust/icu_locale_data/v2/BUILD.gn
@@ -42,10 +42,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "2.0.0-beta2"
   cargo_pkg_authors = "The ICU4X Project Developers"
   cargo_pkg_name = "icu_locale_data"
   cargo_pkg_description = "Data for the icu_locale crate"
+  cargo_pkg_version = "2.0.0-beta2"
+
+  allow_unsafe = false
+
   deps = [ "//third_party/rust/icu_provider_baked/v2:lib" ]
 
   # Only for usage from third-party crates. Add the crate to
diff --git a/third_party/rust/icu_normalizer/v2/BUILD.gn b/third_party/rust/icu_normalizer/v2/BUILD.gn
index c6b88b2..51f6b50 100644
--- a/third_party/rust/icu_normalizer/v2/BUILD.gn
+++ b/third_party/rust/icu_normalizer/v2/BUILD.gn
@@ -23,11 +23,14 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "2.0.0-beta2"
   cargo_pkg_authors = "The ICU4X Project Developers"
   cargo_pkg_name = "icu_normalizer"
   cargo_pkg_description =
       "API for normalizing text into Unicode Normalization Forms"
+  cargo_pkg_version = "2.0.0-beta2"
+
+  allow_unsafe = false
+
   deps = [
     "//third_party/rust/displaydoc/v0_2:lib",
     "//third_party/rust/icu_collections/v2:lib",
diff --git a/third_party/rust/icu_normalizer_data/v2/BUILD.gn b/third_party/rust/icu_normalizer_data/v2/BUILD.gn
index 644dbdf..429eed9 100644
--- a/third_party/rust/icu_normalizer_data/v2/BUILD.gn
+++ b/third_party/rust/icu_normalizer_data/v2/BUILD.gn
@@ -34,10 +34,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "2.0.0-beta2"
   cargo_pkg_authors = "The ICU4X Project Developers"
   cargo_pkg_name = "icu_normalizer_data"
   cargo_pkg_description = "Data for the icu_normalizer crate"
+  cargo_pkg_version = "2.0.0-beta2"
+
+  allow_unsafe = false
+
   deps = [ "//third_party/rust/icu_provider_baked/v2:lib" ]
 
   # Only for usage from third-party crates. Add the crate to
diff --git a/third_party/rust/icu_pattern/v0_4/BUILD.gn b/third_party/rust/icu_pattern/v0_4/BUILD.gn
index aa00be0..8813239 100644
--- a/third_party/rust/icu_pattern/v0_4/BUILD.gn
+++ b/third_party/rust/icu_pattern/v0_4/BUILD.gn
@@ -34,10 +34,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.4.0"
   cargo_pkg_authors = "The ICU4X Project Developers"
   cargo_pkg_name = "icu_pattern"
   cargo_pkg_description = "ICU pattern utilities"
+  cargo_pkg_version = "0.4.0"
+
+  allow_unsafe = false
+
   deps = [
     "//third_party/rust/displaydoc/v0_2:lib",
     "//third_party/rust/either/v1:lib",
diff --git a/third_party/rust/icu_plurals/v2/BUILD.gn b/third_party/rust/icu_plurals/v2/BUILD.gn
index 70cee29..de3fb2d 100644
--- a/third_party/rust/icu_plurals/v2/BUILD.gn
+++ b/third_party/rust/icu_plurals/v2/BUILD.gn
@@ -35,10 +35,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "2.0.0-beta2"
   cargo_pkg_authors = "The ICU4X Project Developers"
   cargo_pkg_name = "icu_plurals"
   cargo_pkg_description = "Unicode Plural Rules categorizer for numeric input"
+  cargo_pkg_version = "2.0.0-beta2"
+
+  allow_unsafe = false
+
   deps = [
     "//third_party/rust/displaydoc/v0_2:lib",
     "//third_party/rust/fixed_decimal/v0_7:lib",
diff --git a/third_party/rust/icu_plurals_data/v2/BUILD.gn b/third_party/rust/icu_plurals_data/v2/BUILD.gn
index ce02ca1f..caf9543 100644
--- a/third_party/rust/icu_plurals_data/v2/BUILD.gn
+++ b/third_party/rust/icu_plurals_data/v2/BUILD.gn
@@ -26,10 +26,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "2.0.0-beta2"
   cargo_pkg_authors = "The ICU4X Project Developers"
   cargo_pkg_name = "icu_plurals_data"
   cargo_pkg_description = "Data for the icu_plurals crate"
+  cargo_pkg_version = "2.0.0-beta2"
+
+  allow_unsafe = false
+
   deps = [
     "//third_party/rust/icu_locale/v2:lib",
     "//third_party/rust/icu_provider_baked/v2:lib",
diff --git a/third_party/rust/icu_properties/v2/BUILD.gn b/third_party/rust/icu_properties/v2/BUILD.gn
index f0353735..18af2bb 100644
--- a/third_party/rust/icu_properties/v2/BUILD.gn
+++ b/third_party/rust/icu_properties/v2/BUILD.gn
@@ -31,10 +31,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "2.0.0-beta2"
   cargo_pkg_authors = "The ICU4X Project Developers"
   cargo_pkg_name = "icu_properties"
   cargo_pkg_description = "Definitions for Unicode properties"
+  cargo_pkg_version = "2.0.0-beta2"
+
+  allow_unsafe = false
+
   deps = [
     "//third_party/rust/displaydoc/v0_2:lib",
     "//third_party/rust/icu_collections/v2:lib",
diff --git a/third_party/rust/icu_properties_data/v2/BUILD.gn b/third_party/rust/icu_properties_data/v2/BUILD.gn
index 8992cdd..c6fc8d0 100644
--- a/third_party/rust/icu_properties_data/v2/BUILD.gn
+++ b/third_party/rust/icu_properties_data/v2/BUILD.gn
@@ -254,10 +254,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "2.0.0-beta2"
   cargo_pkg_authors = "The ICU4X Project Developers"
   cargo_pkg_name = "icu_properties_data"
   cargo_pkg_description = "Data for the icu_properties crate"
+  cargo_pkg_version = "2.0.0-beta2"
+
+  allow_unsafe = false
+
   deps = [ "//third_party/rust/icu_provider_baked/v2:lib" ]
 
   # Only for usage from third-party crates. Add the crate to
diff --git a/third_party/rust/icu_provider/v2/BUILD.gn b/third_party/rust/icu_provider/v2/BUILD.gn
index 65720ea..e25a77d 100644
--- a/third_party/rust/icu_provider/v2/BUILD.gn
+++ b/third_party/rust/icu_provider/v2/BUILD.gn
@@ -36,11 +36,14 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "2.0.0-beta2"
   cargo_pkg_authors = "The ICU4X Project Developers"
   cargo_pkg_name = "icu_provider"
   cargo_pkg_description =
       "Trait and struct definitions for the ICU data provider"
+  cargo_pkg_version = "2.0.0-beta2"
+
+  allow_unsafe = true
+
   deps = [
     "//third_party/rust/displaydoc/v0_2:lib",
     "//third_party/rust/icu_locale_core/v2:lib",
diff --git a/third_party/rust/icu_provider_adapters/v2/BUILD.gn b/third_party/rust/icu_provider_adapters/v2/BUILD.gn
index 147ae23..cfcea752 100644
--- a/third_party/rust/icu_provider_adapters/v2/BUILD.gn
+++ b/third_party/rust/icu_provider_adapters/v2/BUILD.gn
@@ -29,11 +29,14 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "2.0.0-beta2"
   cargo_pkg_authors = "The ICU4X Project Developers"
   cargo_pkg_name = "icu_provider_adapters"
   cargo_pkg_description =
       "Adapters for composing and manipulating data providers."
+  cargo_pkg_version = "2.0.0-beta2"
+
+  allow_unsafe = false
+
   deps = [
     "//third_party/rust/icu_locale/v2:lib",
     "//third_party/rust/icu_provider/v2:lib",
diff --git a/third_party/rust/icu_provider_baked/v2/BUILD.gn b/third_party/rust/icu_provider_baked/v2/BUILD.gn
index d50a26c..8fee91f 100644
--- a/third_party/rust/icu_provider_baked/v2/BUILD.gn
+++ b/third_party/rust/icu_provider_baked/v2/BUILD.gn
@@ -23,10 +23,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "2.0.0-beta2"
   cargo_pkg_authors = "The ICU4X Project Developers"
   cargo_pkg_name = "icu_provider_baked"
   cargo_pkg_description = "Tooling for the ICU4X baked data provider"
+  cargo_pkg_version = "2.0.0-beta2"
+
+  allow_unsafe = true
+
   deps = [
     "//third_party/rust/icu_provider/v2:lib",
     "//third_party/rust/writeable/v0_6:lib",
diff --git a/third_party/rust/icu_time/v2/BUILD.gn b/third_party/rust/icu_time/v2/BUILD.gn
index ffdd29b7..0669abc 100644
--- a/third_party/rust/icu_time/v2/BUILD.gn
+++ b/third_party/rust/icu_time/v2/BUILD.gn
@@ -32,11 +32,14 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "2.0.0-beta2"
   cargo_pkg_authors = "The ICU4X Project Developers"
   cargo_pkg_name = "icu_time"
   cargo_pkg_description =
       "API for resolving and manipulating time zone information"
+  cargo_pkg_version = "2.0.0-beta2"
+
+  allow_unsafe = false
+
   deps = [
     "//third_party/rust/calendrical_calculations/v0_1:lib",
     "//third_party/rust/displaydoc/v0_2:lib",
diff --git a/third_party/rust/icu_time_data/v2/BUILD.gn b/third_party/rust/icu_time_data/v2/BUILD.gn
index d5e6d69..40bac5c 100644
--- a/third_party/rust/icu_time_data/v2/BUILD.gn
+++ b/third_party/rust/icu_time_data/v2/BUILD.gn
@@ -31,10 +31,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "2.0.0-beta2"
   cargo_pkg_authors = "The ICU4X Project Developers"
   cargo_pkg_name = "icu_time_data"
   cargo_pkg_description = "Data for the icu_time crate"
+  cargo_pkg_version = "2.0.0-beta2"
+
+  allow_unsafe = false
+
   deps = [ "//third_party/rust/icu_provider_baked/v2:lib" ]
 
   # Only for usage from third-party crates. Add the crate to
diff --git a/third_party/rust/indexmap/v2/BUILD.gn b/third_party/rust/indexmap/v2/BUILD.gn
index 3e8786b8..66eebda6 100644
--- a/third_party/rust/indexmap/v2/BUILD.gn
+++ b/third_party/rust/indexmap/v2/BUILD.gn
@@ -43,10 +43,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "2.9.0"
   cargo_pkg_name = "indexmap"
   cargo_pkg_description =
       "A hash table with consistent order and fast iteration."
+  cargo_pkg_version = "2.9.0"
+
+  allow_unsafe = true
+
   deps = [
     "//third_party/rust/equivalent/v1:lib",
     "//third_party/rust/hashbrown/v0_15:lib",
diff --git a/third_party/rust/itertools/v0_11/BUILD.gn b/third_party/rust/itertools/v0_11/BUILD.gn
index ab65a8b5..156e883d 100644
--- a/third_party/rust/itertools/v0_11/BUILD.gn
+++ b/third_party/rust/itertools/v0_11/BUILD.gn
@@ -68,11 +68,14 @@
 
   build_native_rust_unit_tests = false
   edition = "2018"
-  cargo_pkg_version = "0.11.0"
   cargo_pkg_authors = "bluss"
   cargo_pkg_name = "itertools"
   cargo_pkg_description =
       "Extra iterator adaptors, iterator methods, free functions, and macros."
+  cargo_pkg_version = "0.11.0"
+
+  allow_unsafe = true
+
   deps = [ "//third_party/rust/either/v1:lib" ]
   features = [
     "default",
diff --git a/third_party/rust/itoa/v1/BUILD.gn b/third_party/rust/itoa/v1/BUILD.gn
index 05674f0..5e030ac2 100644
--- a/third_party/rust/itoa/v1/BUILD.gn
+++ b/third_party/rust/itoa/v1/BUILD.gn
@@ -21,10 +21,12 @@
 
   build_native_rust_unit_tests = false
   edition = "2018"
-  cargo_pkg_version = "1.0.15"
   cargo_pkg_authors = "David Tolnay <dtolnay@gmail.com>"
   cargo_pkg_name = "itoa"
   cargo_pkg_description = "Fast integer primitive to string conversion"
+  cargo_pkg_version = "1.0.15"
+
+  allow_unsafe = true
 
   # Only for usage from third-party crates. Add the crate to
   # //third_party/rust/chromium_crates_io/Cargo.toml to use
diff --git a/third_party/rust/ixdtf/v0_4/BUILD.gn b/third_party/rust/ixdtf/v0_4/BUILD.gn
index 36ce295..ab73d5e 100644
--- a/third_party/rust/ixdtf/v0_4/BUILD.gn
+++ b/third_party/rust/ixdtf/v0_4/BUILD.gn
@@ -31,10 +31,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.4.0"
   cargo_pkg_authors = "The ICU4X Project Developers"
   cargo_pkg_name = "ixdtf"
   cargo_pkg_description = "Parser for Internet eXtended DateTime Format"
+  cargo_pkg_version = "0.4.0"
+
+  allow_unsafe = false
+
   deps = [
     "//third_party/rust/displaydoc/v0_2:lib",
     "//third_party/rust/utf8_iter/v1:lib",
diff --git a/third_party/rust/lazy_static/v1/BUILD.gn b/third_party/rust/lazy_static/v1/BUILD.gn
index 8b170a1c..18a1a98 100644
--- a/third_party/rust/lazy_static/v1/BUILD.gn
+++ b/third_party/rust/lazy_static/v1/BUILD.gn
@@ -23,11 +23,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2015"
-  cargo_pkg_version = "1.5.0"
   cargo_pkg_authors = "Marvin Löbel <loebel.marvin@gmail.com>"
   cargo_pkg_name = "lazy_static"
   cargo_pkg_description =
       "A macro for declaring lazily evaluated statics in Rust."
+  cargo_pkg_version = "1.5.0"
+
+  allow_unsafe = true
 
   #####################################################################
   # Tweaking which GN `config`s apply to this target.
diff --git a/third_party/rust/libc/v0_2/BUILD.gn b/third_party/rust/libc/v0_2/BUILD.gn
index e47906c..b36fbef 100644
--- a/third_party/rust/libc/v0_2/BUILD.gn
+++ b/third_party/rust/libc/v0_2/BUILD.gn
@@ -200,10 +200,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.2.172"
   cargo_pkg_authors = "The Rust Project Developers"
   cargo_pkg_name = "libc"
   cargo_pkg_description = "Raw FFI bindings to platform libraries like libc."
+  cargo_pkg_version = "0.2.172"
+
+  allow_unsafe = true
+
   features = [
     "default",
     "std",
diff --git a/third_party/rust/libm/v0_2/BUILD.gn b/third_party/rust/libm/v0_2/BUILD.gn
index fff310d..5e3171d 100644
--- a/third_party/rust/libm/v0_2/BUILD.gn
+++ b/third_party/rust/libm/v0_2/BUILD.gn
@@ -190,10 +190,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.2.13"
   cargo_pkg_authors = "Jorge Aparicio <jorge@japaric.io>"
   cargo_pkg_name = "libm"
   cargo_pkg_description = "libm in pure Rust"
+  cargo_pkg_version = "0.2.13"
+
+  allow_unsafe = true
+
   features = [
     "arch",
     "default",
diff --git a/third_party/rust/litemap/v0_7/BUILD.gn b/third_party/rust/litemap/v0_7/BUILD.gn
index f8ef067b..dc881b2 100644
--- a/third_party/rust/litemap/v0_7/BUILD.gn
+++ b/third_party/rust/litemap/v0_7/BUILD.gn
@@ -29,11 +29,14 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.7.5"
   cargo_pkg_authors = "The ICU4X Project Developers"
   cargo_pkg_name = "litemap"
   cargo_pkg_description =
       "A key-value Map implementation based on a flat, sorted Vec."
+  cargo_pkg_version = "0.7.5"
+
+  allow_unsafe = false
+
   features = [ "alloc" ]
 
   # Only for usage from third-party crates. Add the crate to
diff --git a/third_party/rust/llguidance/v0_7/BUILD.gn b/third_party/rust/llguidance/v0_7/BUILD.gn
index 1b2c2ce..6ffb278 100644
--- a/third_party/rust/llguidance/v0_7/BUILD.gn
+++ b/third_party/rust/llguidance/v0_7/BUILD.gn
@@ -65,9 +65,12 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.7.19"
   cargo_pkg_name = "llguidance"
   cargo_pkg_description = "Super-fast Structured Outputs"
+  cargo_pkg_version = "0.7.19"
+
+  allow_unsafe = true
+
   deps = [
     "//third_party/rust/anyhow/v1:lib",
     "//third_party/rust/derivre/v0_3:lib",
diff --git a/third_party/rust/log/v0_4/BUILD.gn b/third_party/rust/log/v0_4/BUILD.gn
index e91a1aa..8d4d70a 100644
--- a/third_party/rust/log/v0_4/BUILD.gn
+++ b/third_party/rust/log/v0_4/BUILD.gn
@@ -29,10 +29,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.4.27"
   cargo_pkg_authors = "The Rust Project Developers"
   cargo_pkg_name = "log"
   cargo_pkg_description = "A lightweight logging facade for Rust"
+  cargo_pkg_version = "0.4.27"
+
+  allow_unsafe = true
+
   features = [
     "release_max_level_info",
     "std",
diff --git a/third_party/rust/memchr/v2/BUILD.gn b/third_party/rust/memchr/v2/BUILD.gn
index 5a07e4b..64bb1abc 100644
--- a/third_party/rust/memchr/v2/BUILD.gn
+++ b/third_party/rust/memchr/v2/BUILD.gn
@@ -65,10 +65,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "2.7.4"
   cargo_pkg_authors = "Andrew Gallant <jamslam@gmail.com>, bluss"
   cargo_pkg_name = "memchr"
   cargo_pkg_description = "Provides extremely fast (uses SIMD on x86_64, aarch64 and wasm32) routines for 1, 2 or 3 byte search and single substring search."
+  cargo_pkg_version = "2.7.4"
+
+  allow_unsafe = true
+
   features = [
     "alloc",
     "default",
diff --git a/third_party/rust/miniz_oxide/v0_8/BUILD.gn b/third_party/rust/miniz_oxide/v0_8/BUILD.gn
index b90d9e9..c668cb6b 100644
--- a/third_party/rust/miniz_oxide/v0_8/BUILD.gn
+++ b/third_party/rust/miniz_oxide/v0_8/BUILD.gn
@@ -34,10 +34,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.8.8"
   cargo_pkg_authors = "Frommi <daniil.liferenko@gmail.com>, oyvindln <oyvindln@users.noreply.github.com>, Rich Geldreich richgel99@gmail.com"
   cargo_pkg_name = "miniz_oxide"
   cargo_pkg_description = "DEFLATE compression and decompression library rewritten in Rust based on miniz"
+  cargo_pkg_version = "0.8.8"
+
+  allow_unsafe = false
+
   deps = [
     "//third_party/rust/adler2/v2:lib",
     "//third_party/rust/simd_adler32/v0_3:lib",
diff --git a/third_party/rust/num_bigint/v0_4/BUILD.gn b/third_party/rust/num_bigint/v0_4/BUILD.gn
index cb1576f..48bf24a0 100644
--- a/third_party/rust/num_bigint/v0_4/BUILD.gn
+++ b/third_party/rust/num_bigint/v0_4/BUILD.gn
@@ -47,10 +47,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.4.6"
   cargo_pkg_authors = "The Rust Project Developers"
   cargo_pkg_name = "num-bigint"
   cargo_pkg_description = "Big integer implementation for Rust"
+  cargo_pkg_version = "0.4.6"
+
+  allow_unsafe = false
+
   deps = [
     "//third_party/rust/num_integer/v0_1:lib",
     "//third_party/rust/num_traits/v0_2:lib",
diff --git a/third_party/rust/num_integer/v0_1/BUILD.gn b/third_party/rust/num_integer/v0_1/BUILD.gn
index 158e26b9..f2ba05f9 100644
--- a/third_party/rust/num_integer/v0_1/BUILD.gn
+++ b/third_party/rust/num_integer/v0_1/BUILD.gn
@@ -23,10 +23,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2018"
-  cargo_pkg_version = "0.1.46"
   cargo_pkg_authors = "The Rust Project Developers"
   cargo_pkg_name = "num-integer"
   cargo_pkg_description = "Integer traits and functions"
+  cargo_pkg_version = "0.1.46"
+
+  allow_unsafe = false
+
   deps = [ "//third_party/rust/num_traits/v0_2:lib" ]
   features = [ "i128" ]
 
diff --git a/third_party/rust/num_rational/v0_4/BUILD.gn b/third_party/rust/num_rational/v0_4/BUILD.gn
index bba208c..2b762a9 100644
--- a/third_party/rust/num_rational/v0_4/BUILD.gn
+++ b/third_party/rust/num_rational/v0_4/BUILD.gn
@@ -21,10 +21,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.4.2"
   cargo_pkg_authors = "The Rust Project Developers"
   cargo_pkg_name = "num-rational"
   cargo_pkg_description = "Rational numbers implementation for Rust"
+  cargo_pkg_version = "0.4.2"
+
+  allow_unsafe = false
+
   deps = [
     "//third_party/rust/num_bigint/v0_4:lib",
     "//third_party/rust/num_integer/v0_1:lib",
diff --git a/third_party/rust/num_traits/v0_2/BUILD.gn b/third_party/rust/num_traits/v0_2/BUILD.gn
index 48827bc..dfa0de6 100644
--- a/third_party/rust/num_traits/v0_2/BUILD.gn
+++ b/third_party/rust/num_traits/v0_2/BUILD.gn
@@ -39,10 +39,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.2.19"
   cargo_pkg_authors = "The Rust Project Developers"
   cargo_pkg_name = "num-traits"
   cargo_pkg_description = "Numeric traits for generic mathematics"
+  cargo_pkg_version = "0.2.19"
+
+  allow_unsafe = true
+
   build_deps = [ "//third_party/rust/autocfg/v1:buildrs_support" ]
   features = [
     "default",
diff --git a/third_party/rust/png/v0_18/BUILD.gn b/third_party/rust/png/v0_18/BUILD.gn
index 6025473..fb9a398 100644
--- a/third_party/rust/png/v0_18/BUILD.gn
+++ b/third_party/rust/png/v0_18/BUILD.gn
@@ -39,10 +39,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.18.0-rc"
   cargo_pkg_authors = "The image-rs Developers"
   cargo_pkg_name = "png"
   cargo_pkg_description = "PNG decoding and encoding library in pure Rust"
+  cargo_pkg_version = "0.18.0-rc"
+
+  allow_unsafe = false
+
   deps = [
     "//third_party/rust/bitflags/v2:lib",
     "//third_party/rust/crc32fast/v1:lib",
diff --git a/third_party/rust/potential_utf/v0_1/BUILD.gn b/third_party/rust/potential_utf/v0_1/BUILD.gn
index 80b5ad38..abdf1f5 100644
--- a/third_party/rust/potential_utf/v0_1/BUILD.gn
+++ b/third_party/rust/potential_utf/v0_1/BUILD.gn
@@ -23,10 +23,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.1.2"
   cargo_pkg_authors = "The ICU4X Project Developers"
   cargo_pkg_name = "potential_utf"
   cargo_pkg_description = "Unvalidated string and character types"
+  cargo_pkg_version = "0.1.2"
+
+  allow_unsafe = true
+
   deps = [
     "//third_party/rust/writeable/v0_6:lib",
     "//third_party/rust/zerovec/v0_11:lib",
diff --git a/third_party/rust/proc_macro2/v1/BUILD.gn b/third_party/rust/proc_macro2/v1/BUILD.gn
index ffe7743..a94a8612 100644
--- a/third_party/rust/proc_macro2/v1/BUILD.gn
+++ b/third_party/rust/proc_macro2/v1/BUILD.gn
@@ -29,11 +29,14 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "1.0.95"
   cargo_pkg_authors =
       "David Tolnay <dtolnay@gmail.com>, Alex Crichton <alex@alexcrichton.com>"
   cargo_pkg_name = "proc-macro2"
   cargo_pkg_description = "A substitute implementation of the compiler's `proc_macro` API to decouple token-based libraries from the procedural macro use case."
+  cargo_pkg_version = "1.0.95"
+
+  allow_unsafe = true
+
   deps = [ "//third_party/rust/unicode_ident/v1:lib" ]
   features = [
     "default",
diff --git a/third_party/rust/prost/v0_13/BUILD.gn b/third_party/rust/prost/v0_13/BUILD.gn
index d7312db..5c64dbd4 100644
--- a/third_party/rust/prost/v0_13/BUILD.gn
+++ b/third_party/rust/prost/v0_13/BUILD.gn
@@ -31,11 +31,14 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.13.5"
   cargo_pkg_authors = "Dan Burkert <dan@danburkert.com>, Lucio Franco <luciofranco14@gmail.com>, Casper Meijn <casper@meijn.net>, Tokio Contributors <team@tokio.rs>"
   cargo_pkg_name = "prost"
   cargo_pkg_description =
       "A Protocol Buffers implementation for the Rust Language."
+  cargo_pkg_version = "0.13.5"
+
+  allow_unsafe = true
+
   deps = [
     "//third_party/rust/bytes/v1:lib",
     "//third_party/rust/prost_derive/v0_13:lib",
diff --git a/third_party/rust/prost_derive/v0_13/BUILD.gn b/third_party/rust/prost_derive/v0_13/BUILD.gn
index d5e2e3f..523eb2c0 100644
--- a/third_party/rust/prost_derive/v0_13/BUILD.gn
+++ b/third_party/rust/prost_derive/v0_13/BUILD.gn
@@ -26,10 +26,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.13.5"
   cargo_pkg_authors = "Dan Burkert <dan@danburkert.com>, Lucio Franco <luciofranco14@gmail.com>, Casper Meijn <casper@meijn.net>, Tokio Contributors <team@tokio.rs>"
   cargo_pkg_name = "prost-derive"
   cargo_pkg_description = "Generate encoding and decoding implementations for Prost annotated types."
+  cargo_pkg_version = "0.13.5"
+
+  allow_unsafe = false
+
   deps = [
     "//third_party/rust/anyhow/v1:lib",
     "//third_party/rust/itertools/v0_11:lib",
diff --git a/third_party/rust/qr_code/v2/BUILD.gn b/third_party/rust/qr_code/v2/BUILD.gn
index ec24dda..ca255e4 100644
--- a/third_party/rust/qr_code/v2/BUILD.gn
+++ b/third_party/rust/qr_code/v2/BUILD.gn
@@ -34,10 +34,12 @@
 
   build_native_rust_unit_tests = false
   edition = "2018"
-  cargo_pkg_version = "2.0.0"
   cargo_pkg_authors = "kennytm <kennytm@gmail.com>, Riccardo Casatta <riccardo.casatta@gmail.com>"
   cargo_pkg_name = "qr_code"
   cargo_pkg_description = "QR code encoder in Rust, support structured append (data in multiple qrcodes)"
+  cargo_pkg_version = "2.0.0"
+
+  allow_unsafe = false
 
   #####################################################################
   # Tweaking which GN `config`s apply to this target.
diff --git a/third_party/rust/quote/v1/BUILD.gn b/third_party/rust/quote/v1/BUILD.gn
index aa8b9be..55faea9 100644
--- a/third_party/rust/quote/v1/BUILD.gn
+++ b/third_party/rust/quote/v1/BUILD.gn
@@ -27,10 +27,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2018"
-  cargo_pkg_version = "1.0.40"
   cargo_pkg_authors = "David Tolnay <dtolnay@gmail.com>"
   cargo_pkg_name = "quote"
   cargo_pkg_description = "Quasi-quoting macro quote!(...)"
+  cargo_pkg_version = "1.0.40"
+
+  allow_unsafe = false
+
   deps = [ "//third_party/rust/proc_macro2/v1:lib" ]
   features = [
     "default",
diff --git a/third_party/rust/read_fonts/v0_27/BUILD.gn b/third_party/rust/read_fonts/v0_27/BUILD.gn
index ee543ba..e70ad98 100644
--- a/third_party/rust/read_fonts/v0_27/BUILD.gn
+++ b/third_party/rust/read_fonts/v0_27/BUILD.gn
@@ -166,9 +166,12 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.27.5"
   cargo_pkg_name = "read-fonts"
   cargo_pkg_description = "Reading OpenType font files."
+  cargo_pkg_version = "0.27.5"
+
+  allow_unsafe = false
+
   deps = [
     "//third_party/rust/bytemuck/v1:lib",
     "//third_party/rust/font_types/v0_8:lib",
diff --git a/third_party/rust/regex_automata/v0_4/BUILD.gn b/third_party/rust/regex_automata/v0_4/BUILD.gn
index 4d7fb53..f69a57b 100644
--- a/third_party/rust/regex_automata/v0_4/BUILD.gn
+++ b/third_party/rust/regex_automata/v0_4/BUILD.gn
@@ -89,12 +89,15 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.4.9"
   cargo_pkg_authors =
       "The Rust Project Developers, Andrew Gallant <jamslam@gmail.com>"
   cargo_pkg_name = "regex-automata"
   cargo_pkg_description =
       "Automata construction and matching using regular expressions."
+  cargo_pkg_version = "0.4.9"
+
+  allow_unsafe = false
+
   features = [ "dfa-search" ]
 
   # Only for usage from third-party crates. Add the crate to
diff --git a/third_party/rust/regex_syntax/v0_8/BUILD.gn b/third_party/rust/regex_syntax/v0_8/BUILD.gn
index cd92a4f..33bf228 100644
--- a/third_party/rust/regex_syntax/v0_8/BUILD.gn
+++ b/third_party/rust/regex_syntax/v0_8/BUILD.gn
@@ -52,11 +52,14 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.8.5"
   cargo_pkg_authors =
       "The Rust Project Developers, Andrew Gallant <jamslam@gmail.com>"
   cargo_pkg_name = "regex-syntax"
   cargo_pkg_description = "A regular expression parser."
+  cargo_pkg_version = "0.8.5"
+
+  allow_unsafe = false
+
   features = [
     "default",
     "std",
diff --git a/third_party/rust/rustc_demangle/v0_1/BUILD.gn b/third_party/rust/rustc_demangle/v0_1/BUILD.gn
index afa0177..8078e13 100644
--- a/third_party/rust/rustc_demangle/v0_1/BUILD.gn
+++ b/third_party/rust/rustc_demangle/v0_1/BUILD.gn
@@ -22,10 +22,12 @@
 
   build_native_rust_unit_tests = false
   edition = "2015"
-  cargo_pkg_version = "0.1.24"
   cargo_pkg_authors = "Alex Crichton <alex@alexcrichton.com>"
   cargo_pkg_name = "rustc-demangle"
   cargo_pkg_description = "Rust compiler symbol demangling."
+  cargo_pkg_version = "0.1.24"
+
+  allow_unsafe = false
 
   # Only for usage from third-party crates. Add the crate to
   # //third_party/rust/chromium_crates_io/Cargo.toml to use
diff --git a/third_party/rust/rustc_demangle_capi/v0_1/BUILD.gn b/third_party/rust/rustc_demangle_capi/v0_1/BUILD.gn
index 262716c..142c87017 100644
--- a/third_party/rust/rustc_demangle_capi/v0_1/BUILD.gn
+++ b/third_party/rust/rustc_demangle_capi/v0_1/BUILD.gn
@@ -18,10 +18,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2015"
-  cargo_pkg_version = "0.1.1"
   cargo_pkg_authors = "Torste Aikio <zokier@gmail.com>"
   cargo_pkg_name = "rustc-demangle-capi"
   cargo_pkg_description = "C API for the `rustc-demangle` crate"
+  cargo_pkg_version = "0.1.1"
+
+  allow_unsafe = true
+
   deps = [ "//third_party/rust/rustc_demangle/v0_1:lib" ]
 
   #####################################################################
diff --git a/third_party/rust/rustversion/v1/BUILD.gn b/third_party/rust/rustversion/v1/BUILD.gn
index 783b7fe8..cb13e98f 100644
--- a/third_party/rust/rustversion/v1/BUILD.gn
+++ b/third_party/rust/rustversion/v1/BUILD.gn
@@ -33,11 +33,14 @@
 
   build_native_rust_unit_tests = false
   edition = "2018"
-  cargo_pkg_version = "1.0.20"
   cargo_pkg_authors = "David Tolnay <dtolnay@gmail.com>"
   cargo_pkg_name = "rustversion"
   cargo_pkg_description =
       "Conditional compilation according to rustc compiler version"
+  cargo_pkg_version = "1.0.20"
+
+  allow_unsafe = false
+
   build_root = "//third_party/rust/chromium_crates_io/vendor/rustversion-v1/build/build.rs"
   build_sources = [ "//third_party/rust/chromium_crates_io/vendor/rustversion-v1/build/build.rs" ]
   build_script_inputs = [ "//third_party/rust/chromium_crates_io/vendor/rustversion-v1/src/../build/rustc.rs" ]
diff --git a/third_party/rust/ryu/v1/BUILD.gn b/third_party/rust/ryu/v1/BUILD.gn
index 4de6db2..db25722 100644
--- a/third_party/rust/ryu/v1/BUILD.gn
+++ b/third_party/rust/ryu/v1/BUILD.gn
@@ -35,10 +35,12 @@
 
   build_native_rust_unit_tests = false
   edition = "2018"
-  cargo_pkg_version = "1.0.20"
   cargo_pkg_authors = "David Tolnay <dtolnay@gmail.com>"
   cargo_pkg_name = "ryu"
   cargo_pkg_description = "Fast floating point to string conversion"
+  cargo_pkg_version = "1.0.20"
+
+  allow_unsafe = true
 
   # Only for usage from third-party crates. Add the crate to
   # //third_party/rust/chromium_crates_io/Cargo.toml to use
diff --git a/third_party/rust/serde/v1/BUILD.gn b/third_party/rust/serde/v1/BUILD.gn
index 7c50aa93..153dc1d 100644
--- a/third_party/rust/serde/v1/BUILD.gn
+++ b/third_party/rust/serde/v1/BUILD.gn
@@ -39,10 +39,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2018"
-  cargo_pkg_version = "1.0.219"
   cargo_pkg_authors = "Erick Tryzelaar <erick.tryzelaar@gmail.com>, David Tolnay <dtolnay@gmail.com>"
   cargo_pkg_name = "serde"
   cargo_pkg_description = "A generic serialization/deserialization framework"
+  cargo_pkg_version = "1.0.219"
+
+  allow_unsafe = true
+
   deps = [ "//third_party/rust/serde_derive/v1:lib" ]
   features = [
     "alloc",
diff --git a/third_party/rust/serde_derive/v1/BUILD.gn b/third_party/rust/serde_derive/v1/BUILD.gn
index 405063b..958a573 100644
--- a/third_party/rust/serde_derive/v1/BUILD.gn
+++ b/third_party/rust/serde_derive/v1/BUILD.gn
@@ -38,11 +38,14 @@
 
   build_native_rust_unit_tests = false
   edition = "2015"
-  cargo_pkg_version = "1.0.219"
   cargo_pkg_authors = "Erick Tryzelaar <erick.tryzelaar@gmail.com>, David Tolnay <dtolnay@gmail.com>"
   cargo_pkg_name = "serde_derive"
   cargo_pkg_description =
       "Macros 1.1 implementation of #[derive(Serialize, Deserialize)]"
+  cargo_pkg_version = "1.0.219"
+
+  allow_unsafe = false
+
   deps = [
     "//third_party/rust/proc_macro2/v1:lib",
     "//third_party/rust/quote/v1:lib",
diff --git a/third_party/rust/serde_json/v1/BUILD.gn b/third_party/rust/serde_json/v1/BUILD.gn
index 27abf18..94d2e43 100644
--- a/third_party/rust/serde_json/v1/BUILD.gn
+++ b/third_party/rust/serde_json/v1/BUILD.gn
@@ -57,10 +57,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "1.0.140"
   cargo_pkg_authors = "Erick Tryzelaar <erick.tryzelaar@gmail.com>, David Tolnay <dtolnay@gmail.com>"
   cargo_pkg_name = "serde_json"
   cargo_pkg_description = "A JSON serialization file format"
+  cargo_pkg_version = "1.0.140"
+
+  allow_unsafe = true
+
   deps = [
     "//third_party/rust/indexmap/v2:lib",
     "//third_party/rust/itoa/v1:lib",
diff --git a/third_party/rust/serde_json_lenient/v0_2/BUILD.gn b/third_party/rust/serde_json_lenient/v0_2/BUILD.gn
index a601bf4..a6a9f3d 100644
--- a/third_party/rust/serde_json_lenient/v0_2/BUILD.gn
+++ b/third_party/rust/serde_json_lenient/v0_2/BUILD.gn
@@ -56,10 +56,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.2.4"
   cargo_pkg_authors = "Adrian Taylor <adetaylor@chromium.org>, Michael Bolin <bolinfest@gmail.com>, Erick Tryzelaar <erick.tryzelaar@gmail.com>, David Tolnay <dtolnay@gmail.com>"
   cargo_pkg_name = "serde_json_lenient"
   cargo_pkg_description = "A lenient JSON serialization file format"
+  cargo_pkg_version = "0.2.4"
+
+  allow_unsafe = true
+
   deps = [
     "//third_party/rust/itoa/v1:lib",
     "//third_party/rust/memchr/v2:lib",
diff --git a/third_party/rust/simd_adler32/v0_3/BUILD.gn b/third_party/rust/simd_adler32/v0_3/BUILD.gn
index 6efd8aac..72c6c71f 100644
--- a/third_party/rust/simd_adler32/v0_3/BUILD.gn
+++ b/third_party/rust/simd_adler32/v0_3/BUILD.gn
@@ -29,11 +29,14 @@
 
   build_native_rust_unit_tests = false
   edition = "2018"
-  cargo_pkg_version = "0.3.7"
   cargo_pkg_authors = "Marvin Countryman <me@maar.vin>"
   cargo_pkg_name = "simd-adler32"
   cargo_pkg_description =
       "A SIMD-accelerated Adler-32 hash algorithm implementation."
+  cargo_pkg_version = "0.3.7"
+
+  allow_unsafe = true
+
   features = [
     "const-generics",
     "default",
diff --git a/third_party/rust/skrifa/v0_29/BUILD.gn b/third_party/rust/skrifa/v0_29/BUILD.gn
index 838769b..41f107d6 100644
--- a/third_party/rust/skrifa/v0_29/BUILD.gn
+++ b/third_party/rust/skrifa/v0_29/BUILD.gn
@@ -100,9 +100,12 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.29.2"
   cargo_pkg_name = "skrifa"
   cargo_pkg_description = "Metadata reader and glyph scaler for OpenType fonts."
+  cargo_pkg_version = "0.29.2"
+
+  allow_unsafe = false
+
   deps = [
     "//third_party/rust/bytemuck/v1:lib",
     "//third_party/rust/read_fonts/v0_27:lib",
diff --git a/third_party/rust/small_ctor/v0_1/BUILD.gn b/third_party/rust/small_ctor/v0_1/BUILD.gn
index e4e9618..bba1820 100644
--- a/third_party/rust/small_ctor/v0_1/BUILD.gn
+++ b/third_party/rust/small_ctor/v0_1/BUILD.gn
@@ -21,10 +21,12 @@
 
   build_native_rust_unit_tests = false
   edition = "2018"
-  cargo_pkg_version = "0.1.2"
   cargo_pkg_authors = "Armin Ronacher <armin.ronacher@active-4.com>"
   cargo_pkg_name = "small_ctor"
   cargo_pkg_description = "A minimal, dependency free version of the ctor crate"
+  cargo_pkg_version = "0.1.2"
+
+  allow_unsafe = false
 
   testonly = true
 
diff --git a/third_party/rust/smallvec/v1/BUILD.gn b/third_party/rust/smallvec/v1/BUILD.gn
index a3a061a..3c1351a 100644
--- a/third_party/rust/smallvec/v1/BUILD.gn
+++ b/third_party/rust/smallvec/v1/BUILD.gn
@@ -24,10 +24,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2018"
-  cargo_pkg_version = "1.15.0"
   cargo_pkg_authors = "The Servo Project Developers"
   cargo_pkg_name = "smallvec"
   cargo_pkg_description = "'Small vector' optimization: store up to a small number of items on the stack"
+  cargo_pkg_version = "1.15.0"
+
+  allow_unsafe = true
+
   features = [ "const_generics" ]
 
   # Only for usage from third-party crates. Add the crate to
diff --git a/third_party/rust/stable_deref_trait/v1/BUILD.gn b/third_party/rust/stable_deref_trait/v1/BUILD.gn
index 49914331..cb86545 100644
--- a/third_party/rust/stable_deref_trait/v1/BUILD.gn
+++ b/third_party/rust/stable_deref_trait/v1/BUILD.gn
@@ -18,10 +18,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2015"
-  cargo_pkg_version = "1.2.0"
   cargo_pkg_authors = "Robert Grosse <n210241048576@gmail.com>"
   cargo_pkg_name = "stable_deref_trait"
   cargo_pkg_description = "An unsafe marker trait for types like Box and Rc that dereference to a stable address even when moved, and hence can be used with libraries such as owning_ref and rental."
+  cargo_pkg_version = "1.2.0"
+
+  allow_unsafe = true
+
   features = [ "alloc" ]
 
   # Only for usage from third-party crates. Add the crate to
diff --git a/third_party/rust/static_assertions/v1/BUILD.gn b/third_party/rust/static_assertions/v1/BUILD.gn
index e065e53c3..a8875b64 100644
--- a/third_party/rust/static_assertions/v1/BUILD.gn
+++ b/third_party/rust/static_assertions/v1/BUILD.gn
@@ -29,11 +29,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2015"
-  cargo_pkg_version = "1.1.0"
   cargo_pkg_authors = "Nikolai Vazquez"
   cargo_pkg_name = "static_assertions"
   cargo_pkg_description =
       "Compile-time assertions to ensure that invariants are met."
+  cargo_pkg_version = "1.1.0"
+
+  allow_unsafe = false
 
   #####################################################################
   # Tweaking which GN `config`s apply to this target.
diff --git a/third_party/rust/strck/v1/BUILD.gn b/third_party/rust/strck/v1/BUILD.gn
index 4e57ab9..9a82cb4 100644
--- a/third_party/rust/strck/v1/BUILD.gn
+++ b/third_party/rust/strck/v1/BUILD.gn
@@ -26,10 +26,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "1.0.0"
   cargo_pkg_authors = "Quinn Okabayashi <quinnokabayashi@gmail.com>"
   cargo_pkg_name = "strck"
   cargo_pkg_description = "Checked owned and borrowed strings"
+  cargo_pkg_version = "1.0.0"
+
+  allow_unsafe = true
+
   deps = [ "//third_party/rust/unicode_ident/v1:lib" ]
   features = [ "ident" ]
 
diff --git a/third_party/rust/strsim/v0_11/BUILD.gn b/third_party/rust/strsim/v0_11/BUILD.gn
index 35bbfa0..4ad9cf3 100644
--- a/third_party/rust/strsim/v0_11/BUILD.gn
+++ b/third_party/rust/strsim/v0_11/BUILD.gn
@@ -20,11 +20,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2015"
-  cargo_pkg_version = "0.11.1"
   cargo_pkg_authors =
       "Danny Guo <danny@dannyguo.com>, maxbachmann <oss@maxbachmann.de>"
   cargo_pkg_name = "strsim"
   cargo_pkg_description = "Implementations of string similarity metrics. Includes Hamming, Levenshtein, OSA, Damerau-Levenshtein, Jaro, Jaro-Winkler, and Sørensen-Dice."
+  cargo_pkg_version = "0.11.1"
+
+  allow_unsafe = false
 
   # Only for usage from third-party crates. Add the crate to
   # //third_party/rust/chromium_crates_io/Cargo.toml to use
diff --git a/third_party/rust/strum/v0_27/BUILD.gn b/third_party/rust/strum/v0_27/BUILD.gn
index 0225890..1170459 100644
--- a/third_party/rust/strum/v0_27/BUILD.gn
+++ b/third_party/rust/strum/v0_27/BUILD.gn
@@ -22,10 +22,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.27.1"
   cargo_pkg_authors = "Peter Glotfelty <peter.glotfelty@microsoft.com>"
   cargo_pkg_name = "strum"
   cargo_pkg_description = "Helpful macros for working with enums and strings"
+  cargo_pkg_version = "0.27.1"
+
+  allow_unsafe = false
+
   deps = [ "//third_party/rust/strum_macros/v0_27:lib" ]
   features = [
     "default",
diff --git a/third_party/rust/strum_macros/v0_27/BUILD.gn b/third_party/rust/strum_macros/v0_27/BUILD.gn
index efc1271..74f7ea7 100644
--- a/third_party/rust/strum_macros/v0_27/BUILD.gn
+++ b/third_party/rust/strum_macros/v0_27/BUILD.gn
@@ -43,10 +43,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.27.1"
   cargo_pkg_authors = "Peter Glotfelty <peter.glotfelty@microsoft.com>"
   cargo_pkg_name = "strum_macros"
   cargo_pkg_description = "Helpful macros for working with enums and strings"
+  cargo_pkg_version = "0.27.1"
+
+  allow_unsafe = false
+
   deps = [
     "//third_party/rust/heck/v0_5:lib",
     "//third_party/rust/proc_macro2/v1:lib",
diff --git a/third_party/rust/subtle/v2/BUILD.gn b/third_party/rust/subtle/v2/BUILD.gn
index 5e7b8d775..0ea513f 100644
--- a/third_party/rust/subtle/v2/BUILD.gn
+++ b/third_party/rust/subtle/v2/BUILD.gn
@@ -20,10 +20,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2018"
-  cargo_pkg_version = "2.6.1"
   cargo_pkg_authors = "Isis Lovecruft <isis@patternsinthevoid.net>, Henry de Valence <hdevalence@hdevalence.ca>"
   cargo_pkg_name = "subtle"
   cargo_pkg_description = "Pure-Rust traits and utilities for constant-time cryptographic implementations."
+  cargo_pkg_version = "2.6.1"
+
+  allow_unsafe = false
+
   features = [
     "core_hint_black_box",
     "default",
diff --git a/third_party/rust/syn/v2/BUILD.gn b/third_party/rust/syn/v2/BUILD.gn
index 98adf97..7e049a8 100644
--- a/third_party/rust/syn/v2/BUILD.gn
+++ b/third_party/rust/syn/v2/BUILD.gn
@@ -74,10 +74,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "2.0.101"
   cargo_pkg_authors = "David Tolnay <dtolnay@gmail.com>"
   cargo_pkg_name = "syn"
   cargo_pkg_description = "Parser for Rust source code"
+  cargo_pkg_version = "2.0.101"
+
+  allow_unsafe = true
+
   deps = [
     "//third_party/rust/proc_macro2/v1:lib",
     "//third_party/rust/quote/v1:lib",
diff --git a/third_party/rust/synstructure/v0_13/BUILD.gn b/third_party/rust/synstructure/v0_13/BUILD.gn
index 41a789b..6b6e3e7 100644
--- a/third_party/rust/synstructure/v0_13/BUILD.gn
+++ b/third_party/rust/synstructure/v0_13/BUILD.gn
@@ -21,10 +21,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2018"
-  cargo_pkg_version = "0.13.1"
   cargo_pkg_authors = "Nika Layzell <nika@thelayzells.com>"
   cargo_pkg_name = "synstructure"
   cargo_pkg_description = "Helper methods and macros for custom derives"
+  cargo_pkg_version = "0.13.1"
+
+  allow_unsafe = false
+
   deps = [
     "//third_party/rust/proc_macro2/v1:lib",
     "//third_party/rust/quote/v1:lib",
diff --git a/third_party/rust/temporal_capi/v0_0/BUILD.gn b/third_party/rust/temporal_capi/v0_0/BUILD.gn
index 1cd93e2..6aa48ee 100644
--- a/third_party/rust/temporal_capi/v0_0/BUILD.gn
+++ b/third_party/rust/temporal_capi/v0_0/BUILD.gn
@@ -31,10 +31,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.0.6"
   cargo_pkg_authors = "boa-dev"
   cargo_pkg_name = "temporal_capi"
   cargo_pkg_description = "C interface to temporal_rs"
+  cargo_pkg_version = "0.0.6"
+
+  allow_unsafe = false
+
   deps = [
     "//third_party/rust/diplomat/v0_10:lib",
     "//third_party/rust/diplomat_runtime/v0_10:lib",
diff --git a/third_party/rust/temporal_rs/v0_0/BUILD.gn b/third_party/rust/temporal_rs/v0_0/BUILD.gn
index 24e8982..8fee977 100644
--- a/third_party/rust/temporal_rs/v0_0/BUILD.gn
+++ b/third_party/rust/temporal_rs/v0_0/BUILD.gn
@@ -62,10 +62,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.0.6"
   cargo_pkg_authors = "boa-dev"
   cargo_pkg_name = "temporal_rs"
   cargo_pkg_description = "Temporal in Rust is an implementation of the TC39 Temporal Builtin Proposal in Rust."
+  cargo_pkg_version = "0.0.6"
+
+  allow_unsafe = true
+
   deps = [
     "//third_party/rust/iana_time_zone/v0_1:lib",
     "//third_party/rust/icu_calendar/v2:lib",
diff --git a/third_party/rust/termcolor/v1/BUILD.gn b/third_party/rust/termcolor/v1/BUILD.gn
index 0904ae9..d57e340 100644
--- a/third_party/rust/termcolor/v1/BUILD.gn
+++ b/third_party/rust/termcolor/v1/BUILD.gn
@@ -20,11 +20,14 @@
 
   build_native_rust_unit_tests = false
   edition = "2018"
-  cargo_pkg_version = "1.4.1"
   cargo_pkg_authors = "Andrew Gallant <jamslam@gmail.com>"
   cargo_pkg_name = "termcolor"
   cargo_pkg_description =
       "A simple cross platform library for writing colored text to a terminal."
+  cargo_pkg_version = "1.4.1"
+
+  allow_unsafe = false
+
   deps = []
   if (is_win) {
     deps += [ "//third_party/rust/winapi_util/v0_1:lib" ]
diff --git a/third_party/rust/tinystr/v0_8/BUILD.gn b/third_party/rust/tinystr/v0_8/BUILD.gn
index 39975fa..5831852 100644
--- a/third_party/rust/tinystr/v0_8/BUILD.gn
+++ b/third_party/rust/tinystr/v0_8/BUILD.gn
@@ -30,11 +30,14 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.8.1"
   cargo_pkg_authors = "The ICU4X Project Developers"
   cargo_pkg_name = "tinystr"
   cargo_pkg_description =
       "A small ASCII-only bounded length string representation."
+  cargo_pkg_version = "0.8.1"
+
+  allow_unsafe = true
+
   deps = [
     "//third_party/rust/displaydoc/v0_2:lib",
     "//third_party/rust/zerovec/v0_11:lib",
diff --git a/third_party/rust/toktrie/v0_7/BUILD.gn b/third_party/rust/toktrie/v0_7/BUILD.gn
index e190b6e5..8bf677e 100644
--- a/third_party/rust/toktrie/v0_7/BUILD.gn
+++ b/third_party/rust/toktrie/v0_7/BUILD.gn
@@ -27,9 +27,12 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.7.19"
   cargo_pkg_name = "toktrie"
   cargo_pkg_description = "LLM Token Trie library"
+  cargo_pkg_version = "0.7.19"
+
+  allow_unsafe = false
+
   deps = [
     "//third_party/rust/anyhow/v1:lib",
     "//third_party/rust/bytemuck/v1:lib",
diff --git a/third_party/rust/unicode_ident/v1/BUILD.gn b/third_party/rust/unicode_ident/v1/BUILD.gn
index e298dbf7..9dd44f5 100644
--- a/third_party/rust/unicode_ident/v1/BUILD.gn
+++ b/third_party/rust/unicode_ident/v1/BUILD.gn
@@ -22,10 +22,12 @@
 
   build_native_rust_unit_tests = false
   edition = "2018"
-  cargo_pkg_version = "1.0.18"
   cargo_pkg_authors = "David Tolnay <dtolnay@gmail.com>"
   cargo_pkg_name = "unicode-ident"
   cargo_pkg_description = "Determine whether characters have the XID_Start or XID_Continue properties according to Unicode Standard Annex #31"
+  cargo_pkg_version = "1.0.18"
+
+  allow_unsafe = true
 
   # Only for usage from third-party crates. Add the crate to
   # //third_party/rust/chromium_crates_io/Cargo.toml to use
diff --git a/third_party/rust/unicode_width/v0_1/BUILD.gn b/third_party/rust/unicode_width/v0_1/BUILD.gn
index ee392039..1d68e9763 100644
--- a/third_party/rust/unicode_width/v0_1/BUILD.gn
+++ b/third_party/rust/unicode_width/v0_1/BUILD.gn
@@ -21,11 +21,14 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.1.14"
   cargo_pkg_authors =
       "kwantam <kwantam@gmail.com>, Manish Goregaokar <manishsmail@gmail.com>"
   cargo_pkg_name = "unicode-width"
   cargo_pkg_description = "Determine displayed width of `char` and `str` types according to Unicode Standard Annex #11 rules."
+  cargo_pkg_version = "0.1.14"
+
+  allow_unsafe = false
+
   features = [
     "cjk",
     "default",
diff --git a/third_party/rust/utf8_iter/v1/BUILD.gn b/third_party/rust/utf8_iter/v1/BUILD.gn
index fa70169..5361524 100644
--- a/third_party/rust/utf8_iter/v1/BUILD.gn
+++ b/third_party/rust/utf8_iter/v1/BUILD.gn
@@ -23,11 +23,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "1.0.4"
   cargo_pkg_authors = "Henri Sivonen <hsivonen@hsivonen.fi>"
   cargo_pkg_name = "utf8_iter"
   cargo_pkg_description =
       "Iterator by char over potentially-invalid UTF-8 in &[u8]"
+  cargo_pkg_version = "1.0.4"
+
+  allow_unsafe = true
 
   # Only for usage from third-party crates. Add the crate to
   # //third_party/rust/chromium_crates_io/Cargo.toml to use
diff --git a/third_party/rust/winapi_util/v0_1/BUILD.gn b/third_party/rust/winapi_util/v0_1/BUILD.gn
index 3169441..048a35a 100644
--- a/third_party/rust/winapi_util/v0_1/BUILD.gn
+++ b/third_party/rust/winapi_util/v0_1/BUILD.gn
@@ -26,11 +26,14 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.1.9"
   cargo_pkg_authors = "Andrew Gallant <jamslam@gmail.com>"
   cargo_pkg_name = "winapi-util"
   cargo_pkg_description =
       "A dumping ground for high level safe wrappers over windows-sys."
+  cargo_pkg_version = "0.1.9"
+
+  allow_unsafe = true
+
   deps = []
   if (is_win) {
     deps += [ "//third_party/rust/windows_sys/v0_52:lib" ]
diff --git a/third_party/rust/windows_aarch64_msvc/v0_52/BUILD.gn b/third_party/rust/windows_aarch64_msvc/v0_52/BUILD.gn
index b03eaab..aa8b4e8 100644
--- a/third_party/rust/windows_aarch64_msvc/v0_52/BUILD.gn
+++ b/third_party/rust/windows_aarch64_msvc/v0_52/BUILD.gn
@@ -19,10 +19,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.52.6"
   cargo_pkg_authors = "Microsoft"
   cargo_pkg_name = "windows_aarch64_msvc"
   cargo_pkg_description = "Import lib for Windows"
+  cargo_pkg_version = "0.52.6"
+
+  allow_unsafe = false
+
   build_root = "//third_party/rust/chromium_crates_io/vendor/windows_aarch64_msvc-v0_52/build.rs"
   build_sources = [ "//third_party/rust/chromium_crates_io/vendor/windows_aarch64_msvc-v0_52/build.rs" ]
   native_libs = [ "//third_party/rust/chromium_crates_io/vendor/windows_aarch64_msvc-v0_52/src/../lib/windows.0.52.0.lib" ]
diff --git a/third_party/rust/windows_core/v0_61/BUILD.gn b/third_party/rust/windows_core/v0_61/BUILD.gn
index 141a671..bceac02 100644
--- a/third_party/rust/windows_core/v0_61/BUILD.gn
+++ b/third_party/rust/windows_core/v0_61/BUILD.gn
@@ -53,10 +53,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.61.0"
   cargo_pkg_authors = "Microsoft"
   cargo_pkg_name = "windows-core"
   cargo_pkg_description = "Core type support for COM and Windows"
+  cargo_pkg_version = "0.61.0"
+
+  allow_unsafe = false
+
   deps = []
   if (is_win) {
     deps += [
diff --git a/third_party/rust/windows_i686_msvc/v0_52/BUILD.gn b/third_party/rust/windows_i686_msvc/v0_52/BUILD.gn
index cbc51dc12..66364fa 100644
--- a/third_party/rust/windows_i686_msvc/v0_52/BUILD.gn
+++ b/third_party/rust/windows_i686_msvc/v0_52/BUILD.gn
@@ -19,10 +19,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.52.6"
   cargo_pkg_authors = "Microsoft"
   cargo_pkg_name = "windows_i686_msvc"
   cargo_pkg_description = "Import lib for Windows"
+  cargo_pkg_version = "0.52.6"
+
+  allow_unsafe = false
+
   build_root = "//third_party/rust/chromium_crates_io/vendor/windows_i686_msvc-v0_52/build.rs"
   build_sources = [ "//third_party/rust/chromium_crates_io/vendor/windows_i686_msvc-v0_52/build.rs" ]
   native_libs = [ "//third_party/rust/chromium_crates_io/vendor/windows_i686_msvc-v0_52/src/../lib/windows.0.52.0.lib" ]
diff --git a/third_party/rust/windows_implement/v0_60/BUILD.gn b/third_party/rust/windows_implement/v0_60/BUILD.gn
index 6fa61df..ed3fa9c 100644
--- a/third_party/rust/windows_implement/v0_60/BUILD.gn
+++ b/third_party/rust/windows_implement/v0_60/BUILD.gn
@@ -23,10 +23,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.60.0"
   cargo_pkg_authors = "Microsoft"
   cargo_pkg_name = "windows-implement"
   cargo_pkg_description = "The implement macro for the windows crate"
+  cargo_pkg_version = "0.60.0"
+
+  allow_unsafe = false
+
   deps = []
   if (is_win) {
     deps += [
diff --git a/third_party/rust/windows_interface/v0_59/BUILD.gn b/third_party/rust/windows_interface/v0_59/BUILD.gn
index 8ad2c84..65b0de7 100644
--- a/third_party/rust/windows_interface/v0_59/BUILD.gn
+++ b/third_party/rust/windows_interface/v0_59/BUILD.gn
@@ -19,10 +19,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.59.1"
   cargo_pkg_authors = "Microsoft"
   cargo_pkg_name = "windows-interface"
   cargo_pkg_description = "The interface macro for the windows crate"
+  cargo_pkg_version = "0.59.1"
+
+  allow_unsafe = false
+
   deps = []
   if (is_win) {
     deps += [
diff --git a/third_party/rust/windows_link/v0_1/BUILD.gn b/third_party/rust/windows_link/v0_1/BUILD.gn
index 52ba5d2..32ac3287 100644
--- a/third_party/rust/windows_link/v0_1/BUILD.gn
+++ b/third_party/rust/windows_link/v0_1/BUILD.gn
@@ -21,10 +21,12 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.1.1"
   cargo_pkg_authors = "Microsoft"
   cargo_pkg_name = "windows-link"
   cargo_pkg_description = "Linking for Windows"
+  cargo_pkg_version = "0.1.1"
+
+  allow_unsafe = false
 
   # Only for usage from third-party crates. Add the crate to
   # //third_party/rust/chromium_crates_io/Cargo.toml to use
diff --git a/third_party/rust/windows_result/v0_3/BUILD.gn b/third_party/rust/windows_result/v0_3/BUILD.gn
index 9b66617..fec3c5e 100644
--- a/third_party/rust/windows_result/v0_3/BUILD.gn
+++ b/third_party/rust/windows_result/v0_3/BUILD.gn
@@ -28,10 +28,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.3.2"
   cargo_pkg_authors = "Microsoft"
   cargo_pkg_name = "windows-result"
   cargo_pkg_description = "Windows error handling"
+  cargo_pkg_version = "0.3.2"
+
+  allow_unsafe = false
+
   deps = []
   if (is_win) {
     deps += [ "//third_party/rust/windows_link/v0_1:lib" ]
diff --git a/third_party/rust/windows_strings/v0_4/BUILD.gn b/third_party/rust/windows_strings/v0_4/BUILD.gn
index 44928971..8ae99d71 100644
--- a/third_party/rust/windows_strings/v0_4/BUILD.gn
+++ b/third_party/rust/windows_strings/v0_4/BUILD.gn
@@ -33,10 +33,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.4.0"
   cargo_pkg_authors = "Microsoft"
   cargo_pkg_name = "windows-strings"
   cargo_pkg_description = "Windows string types"
+  cargo_pkg_version = "0.4.0"
+
+  allow_unsafe = false
+
   deps = []
   if (is_win) {
     deps += [ "//third_party/rust/windows_link/v0_1:lib" ]
diff --git a/third_party/rust/windows_sys/v0_52/BUILD.gn b/third_party/rust/windows_sys/v0_52/BUILD.gn
index 80797a7..551ab88 100644
--- a/third_party/rust/windows_sys/v0_52/BUILD.gn
+++ b/third_party/rust/windows_sys/v0_52/BUILD.gn
@@ -255,10 +255,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.52.0"
   cargo_pkg_authors = "Microsoft"
   cargo_pkg_name = "windows-sys"
   cargo_pkg_description = "Rust for Windows"
+  cargo_pkg_version = "0.52.0"
+
+  allow_unsafe = false
+
   deps = []
   if (is_win) {
     deps += [ "//third_party/rust/windows_targets/v0_52:lib" ]
diff --git a/third_party/rust/windows_targets/v0_52/BUILD.gn b/third_party/rust/windows_targets/v0_52/BUILD.gn
index 152e283..2820e09 100644
--- a/third_party/rust/windows_targets/v0_52/BUILD.gn
+++ b/third_party/rust/windows_targets/v0_52/BUILD.gn
@@ -19,10 +19,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.52.6"
   cargo_pkg_authors = "Microsoft"
   cargo_pkg_name = "windows-targets"
   cargo_pkg_description = "Import libs for Windows"
+  cargo_pkg_version = "0.52.6"
+
+  allow_unsafe = false
+
   deps = []
   if (is_win && current_cpu == "arm64") {
     deps += [ "//third_party/rust/windows_aarch64_msvc/v0_52:lib" ]
diff --git a/third_party/rust/windows_x86_64_msvc/v0_52/BUILD.gn b/third_party/rust/windows_x86_64_msvc/v0_52/BUILD.gn
index 1acd990b..18c29fe2 100644
--- a/third_party/rust/windows_x86_64_msvc/v0_52/BUILD.gn
+++ b/third_party/rust/windows_x86_64_msvc/v0_52/BUILD.gn
@@ -19,10 +19,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.52.6"
   cargo_pkg_authors = "Microsoft"
   cargo_pkg_name = "windows_x86_64_msvc"
   cargo_pkg_description = "Import lib for Windows"
+  cargo_pkg_version = "0.52.6"
+
+  allow_unsafe = false
+
   build_root = "//third_party/rust/chromium_crates_io/vendor/windows_x86_64_msvc-v0_52/build.rs"
   build_sources = [ "//third_party/rust/chromium_crates_io/vendor/windows_x86_64_msvc-v0_52/build.rs" ]
   native_libs = [ "//third_party/rust/chromium_crates_io/vendor/windows_x86_64_msvc-v0_52/src/../lib/windows.0.52.0.lib" ]
diff --git a/third_party/rust/writeable/v0_6/BUILD.gn b/third_party/rust/writeable/v0_6/BUILD.gn
index b6a9b8f..918eac9 100644
--- a/third_party/rust/writeable/v0_6/BUILD.gn
+++ b/third_party/rust/writeable/v0_6/BUILD.gn
@@ -29,10 +29,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.6.1"
   cargo_pkg_authors = "The ICU4X Project Developers"
   cargo_pkg_name = "writeable"
   cargo_pkg_description = "A more efficient alternative to fmt::Display"
+  cargo_pkg_version = "0.6.1"
+
+  allow_unsafe = true
+
   deps = [ "//third_party/rust/either/v1:lib" ]
   features = [ "either" ]
 
diff --git a/third_party/rust/yoke/v0_8/BUILD.gn b/third_party/rust/yoke/v0_8/BUILD.gn
index df6e397..d4da33d 100644
--- a/third_party/rust/yoke/v0_8/BUILD.gn
+++ b/third_party/rust/yoke/v0_8/BUILD.gn
@@ -29,10 +29,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.8.0"
   cargo_pkg_authors = "Manish Goregaokar <manishsmail@gmail.com>"
   cargo_pkg_name = "yoke"
   cargo_pkg_description = "Abstraction allowing borrowed data to be carried along with the backing data it borrows from"
+  cargo_pkg_version = "0.8.0"
+
+  allow_unsafe = true
+
   deps = [
     "//third_party/rust/stable_deref_trait/v1:lib",
     "//third_party/rust/yoke_derive/v0_8:lib",
diff --git a/third_party/rust/yoke_derive/v0_8/BUILD.gn b/third_party/rust/yoke_derive/v0_8/BUILD.gn
index 10a5d29..2d472ed 100644
--- a/third_party/rust/yoke_derive/v0_8/BUILD.gn
+++ b/third_party/rust/yoke_derive/v0_8/BUILD.gn
@@ -22,10 +22,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.8.0"
   cargo_pkg_authors = "Manish Goregaokar <manishsmail@gmail.com>"
   cargo_pkg_name = "yoke-derive"
   cargo_pkg_description = "Custom derive for the yoke crate"
+  cargo_pkg_version = "0.8.0"
+
+  allow_unsafe = false
+
   deps = [
     "//third_party/rust/proc_macro2/v1:lib",
     "//third_party/rust/quote/v1:lib",
diff --git a/third_party/rust/zerofrom/v0_1/BUILD.gn b/third_party/rust/zerofrom/v0_1/BUILD.gn
index 7b0bbae..5aecdc1 100644
--- a/third_party/rust/zerofrom/v0_1/BUILD.gn
+++ b/third_party/rust/zerofrom/v0_1/BUILD.gn
@@ -23,10 +23,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.1.6"
   cargo_pkg_authors = "Manish Goregaokar <manishsmail@gmail.com>"
   cargo_pkg_name = "zerofrom"
   cargo_pkg_description = "ZeroFrom trait for constructing"
+  cargo_pkg_version = "0.1.6"
+
+  allow_unsafe = false
+
   deps = [ "//third_party/rust/zerofrom_derive/v0_1:lib" ]
   features = [
     "alloc",
diff --git a/third_party/rust/zerofrom_derive/v0_1/BUILD.gn b/third_party/rust/zerofrom_derive/v0_1/BUILD.gn
index 7094e73..69c733e 100644
--- a/third_party/rust/zerofrom_derive/v0_1/BUILD.gn
+++ b/third_party/rust/zerofrom_derive/v0_1/BUILD.gn
@@ -21,10 +21,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.1.6"
   cargo_pkg_authors = "Manish Goregaokar <manishsmail@gmail.com>"
   cargo_pkg_name = "zerofrom-derive"
   cargo_pkg_description = "Custom derive for the zerofrom crate"
+  cargo_pkg_version = "0.1.6"
+
+  allow_unsafe = false
+
   deps = [
     "//third_party/rust/proc_macro2/v1:lib",
     "//third_party/rust/quote/v1:lib",
diff --git a/third_party/rust/zerotrie/v0_2/BUILD.gn b/third_party/rust/zerotrie/v0_2/BUILD.gn
index e32eab3..3170053 100644
--- a/third_party/rust/zerotrie/v0_2/BUILD.gn
+++ b/third_party/rust/zerotrie/v0_2/BUILD.gn
@@ -42,11 +42,14 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.2.1"
   cargo_pkg_authors = "The ICU4X Project Developers"
   cargo_pkg_name = "zerotrie"
   cargo_pkg_description =
       "A data structure that efficiently maps strings to integers"
+  cargo_pkg_version = "0.2.1"
+
+  allow_unsafe = true
+
   deps = [
     "//third_party/rust/displaydoc/v0_2:lib",
     "//third_party/rust/yoke/v0_8:lib",
diff --git a/third_party/rust/zerovec/v0_11/BUILD.gn b/third_party/rust/zerovec/v0_11/BUILD.gn
index e851ce3c..dc7ead2 100644
--- a/third_party/rust/zerovec/v0_11/BUILD.gn
+++ b/third_party/rust/zerovec/v0_11/BUILD.gn
@@ -69,10 +69,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.11.1"
   cargo_pkg_authors = "The ICU4X Project Developers"
   cargo_pkg_name = "zerovec"
   cargo_pkg_description = "Zero-copy vector backed by a byte array"
+  cargo_pkg_version = "0.11.1"
+
+  allow_unsafe = true
+
   deps = [
     "//third_party/rust/yoke/v0_8:lib",
     "//third_party/rust/zerofrom/v0_1:lib",
diff --git a/third_party/rust/zerovec_derive/v0_11/BUILD.gn b/third_party/rust/zerovec_derive/v0_11/BUILD.gn
index 9a8c4c1..a1d1452c 100644
--- a/third_party/rust/zerovec_derive/v0_11/BUILD.gn
+++ b/third_party/rust/zerovec_derive/v0_11/BUILD.gn
@@ -25,10 +25,13 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.11.1"
   cargo_pkg_authors = "Manish Goregaokar <manishsmail@gmail.com>"
   cargo_pkg_name = "zerovec-derive"
   cargo_pkg_description = "Custom derive for the zerovec crate"
+  cargo_pkg_version = "0.11.1"
+
+  allow_unsafe = false
+
   deps = [
     "//third_party/rust/proc_macro2/v1:lib",
     "//third_party/rust/quote/v1:lib",
diff --git a/third_party/rust/zip/v2/BUILD.gn b/third_party/rust/zip/v2/BUILD.gn
deleted file mode 100644
index b1142b0..0000000
--- a/third_party/rust/zip/v2/BUILD.gn
+++ /dev/null
@@ -1,98 +0,0 @@
-# Copyright 2023 The Chromium Authors
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-# @generated from third_party/rust/chromium_crates_io/BUILD.gn.hbs by
-# tools/crates/gnrt.
-# Do not edit!
-
-import("//build/rust/cargo_crate.gni")
-
-cargo_crate("lib") {
-  crate_name = "zip"
-  epoch = "2"
-  crate_type = "rlib"
-  crate_root = "//third_party/rust/chromium_crates_io/vendor/zip-v2/src/lib.rs"
-  sources = [
-    "//third_party/rust/chromium_crates_io/vendor/zip-v2/src/aes.rs",
-    "//third_party/rust/chromium_crates_io/vendor/zip-v2/src/aes_ctr.rs",
-    "//third_party/rust/chromium_crates_io/vendor/zip-v2/src/build.rs",
-    "//third_party/rust/chromium_crates_io/vendor/zip-v2/src/compression.rs",
-    "//third_party/rust/chromium_crates_io/vendor/zip-v2/src/cp437.rs",
-    "//third_party/rust/chromium_crates_io/vendor/zip-v2/src/crc32.rs",
-    "//third_party/rust/chromium_crates_io/vendor/zip-v2/src/extra_fields/extended_timestamp.rs",
-    "//third_party/rust/chromium_crates_io/vendor/zip-v2/src/extra_fields/mod.rs",
-    "//third_party/rust/chromium_crates_io/vendor/zip-v2/src/extra_fields/ntfs.rs",
-    "//third_party/rust/chromium_crates_io/vendor/zip-v2/src/extra_fields/zipinfo_utf8.rs",
-    "//third_party/rust/chromium_crates_io/vendor/zip-v2/src/lib.rs",
-    "//third_party/rust/chromium_crates_io/vendor/zip-v2/src/path.rs",
-    "//third_party/rust/chromium_crates_io/vendor/zip-v2/src/read.rs",
-    "//third_party/rust/chromium_crates_io/vendor/zip-v2/src/read/config.rs",
-    "//third_party/rust/chromium_crates_io/vendor/zip-v2/src/read/lzma.rs",
-    "//third_party/rust/chromium_crates_io/vendor/zip-v2/src/read/magic_finder.rs",
-    "//third_party/rust/chromium_crates_io/vendor/zip-v2/src/read/stream.rs",
-    "//third_party/rust/chromium_crates_io/vendor/zip-v2/src/result.rs",
-    "//third_party/rust/chromium_crates_io/vendor/zip-v2/src/spec.rs",
-    "//third_party/rust/chromium_crates_io/vendor/zip-v2/src/types.rs",
-    "//third_party/rust/chromium_crates_io/vendor/zip-v2/src/unstable.rs",
-    "//third_party/rust/chromium_crates_io/vendor/zip-v2/src/write.rs",
-    "//third_party/rust/chromium_crates_io/vendor/zip-v2/src/zipcrypto.rs",
-  ]
-  inputs = []
-
-  build_native_rust_unit_tests = false
-  edition = "2021"
-  cargo_pkg_version = "2.6.1"
-  cargo_pkg_authors = "Mathijs van de Nes <git@mathijs.vd-nes.nl>, Marli Frost <marli@frost.red>, Ryan Levick <ryan.levick@gmail.com>, Chris Hennick <hennickc@amazon.com>"
-  cargo_pkg_name = "zip"
-  cargo_pkg_description =
-      "Library to support the reading and writing of zip files."
-  deps = [
-    "//third_party/rust/crc32fast/v1:lib",
-    "//third_party/rust/flate2/v1:lib",
-    "//third_party/rust/indexmap/v2:lib",
-    "//third_party/rust/memchr/v2:lib",
-  ]
-  if (current_cpu == "arm") {
-    deps += [ "//third_party/rust/crossbeam_utils/v0_8:lib" ]
-  }
-  features = [
-    "_deflate-any",
-    "deflate-flate2",
-    "flate2",
-  ]
-  build_root =
-      "//third_party/rust/chromium_crates_io/vendor/zip-v2/src/build.rs"
-  build_sources =
-      [ "//third_party/rust/chromium_crates_io/vendor/zip-v2/src/build.rs" ]
-
-  #####################################################################
-  # Tweaking which GN `config`s apply to this target.
-
-  # Config changes that apply to all `//third_party/rust` crates.
-  _configs_to_remove = [
-    # We don't need code coverage data for any `chromium_crates_io` crates.
-    "//build/config/coverage:default_coverage",
-
-    # This is third-party code, so remove `chromium_code` config.  We are not
-    # at the same time adding `//build/config/compiler:no_chromium_code`,
-    # because 1) we don't want to pull how warnings are handled by that config
-    # and 2) that config doesn't have any non-warnings-related stuff.
-    "//build/config/compiler:chromium_code",
-  ]
-  _configs_to_add = []
-
-  # Changing (if needed) which configs apply to this specific crate (based on
-  # `extra_kv.configs_to_remove` and `extra_kv.configs_to_add` from
-  # `gnrt_config.toml`).
-  _configs_to_remove += []
-  _configs_to_add += []
-
-  # Applying config changes.
-  library_configs -= _configs_to_remove
-  library_configs += _configs_to_add
-  executable_configs -= _configs_to_remove
-  executable_configs += _configs_to_add
-  proc_macro_configs -= _configs_to_remove
-  proc_macro_configs += _configs_to_add
-}
diff --git a/third_party/rust/zip/v3/BUILD.gn b/third_party/rust/zip/v3/BUILD.gn
new file mode 100644
index 0000000..fe52247
--- /dev/null
+++ b/third_party/rust/zip/v3/BUILD.gn
@@ -0,0 +1,92 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# @generated from third_party/rust/chromium_crates_io/BUILD.gn.hbs by
+# tools/crates/gnrt.
+# Do not edit!
+
+import("//build/rust/cargo_crate.gni")
+
+cargo_crate("lib") {
+  crate_name = "zip"
+  epoch = "3"
+  crate_type = "rlib"
+  crate_root = "//third_party/rust/chromium_crates_io/vendor/zip-v3/src/lib.rs"
+  sources = [
+    "//third_party/rust/chromium_crates_io/vendor/zip-v3/src/aes.rs",
+    "//third_party/rust/chromium_crates_io/vendor/zip-v3/src/aes_ctr.rs",
+    "//third_party/rust/chromium_crates_io/vendor/zip-v3/src/compression.rs",
+    "//third_party/rust/chromium_crates_io/vendor/zip-v3/src/cp437.rs",
+    "//third_party/rust/chromium_crates_io/vendor/zip-v3/src/crc32.rs",
+    "//third_party/rust/chromium_crates_io/vendor/zip-v3/src/extra_fields/extended_timestamp.rs",
+    "//third_party/rust/chromium_crates_io/vendor/zip-v3/src/extra_fields/mod.rs",
+    "//third_party/rust/chromium_crates_io/vendor/zip-v3/src/extra_fields/ntfs.rs",
+    "//third_party/rust/chromium_crates_io/vendor/zip-v3/src/extra_fields/zipinfo_utf8.rs",
+    "//third_party/rust/chromium_crates_io/vendor/zip-v3/src/lib.rs",
+    "//third_party/rust/chromium_crates_io/vendor/zip-v3/src/path.rs",
+    "//third_party/rust/chromium_crates_io/vendor/zip-v3/src/read.rs",
+    "//third_party/rust/chromium_crates_io/vendor/zip-v3/src/read/config.rs",
+    "//third_party/rust/chromium_crates_io/vendor/zip-v3/src/read/lzma.rs",
+    "//third_party/rust/chromium_crates_io/vendor/zip-v3/src/read/magic_finder.rs",
+    "//third_party/rust/chromium_crates_io/vendor/zip-v3/src/read/stream.rs",
+    "//third_party/rust/chromium_crates_io/vendor/zip-v3/src/result.rs",
+    "//third_party/rust/chromium_crates_io/vendor/zip-v3/src/spec.rs",
+    "//third_party/rust/chromium_crates_io/vendor/zip-v3/src/types.rs",
+    "//third_party/rust/chromium_crates_io/vendor/zip-v3/src/unstable.rs",
+    "//third_party/rust/chromium_crates_io/vendor/zip-v3/src/write.rs",
+    "//third_party/rust/chromium_crates_io/vendor/zip-v3/src/zipcrypto.rs",
+  ]
+  inputs = []
+
+  build_native_rust_unit_tests = false
+  edition = "2021"
+  cargo_pkg_authors = "Mathijs van de Nes <git@mathijs.vd-nes.nl>, Marli Frost <marli@frost.red>, Ryan Levick <ryan.levick@gmail.com>, Chris Hennick <hennickc@amazon.com>"
+  cargo_pkg_name = "zip"
+  cargo_pkg_description =
+      "Library to support the reading and writing of zip files."
+  cargo_pkg_version = "3.0.0"
+
+  allow_unsafe = false
+
+  deps = [
+    "//third_party/rust/crc32fast/v1:lib",
+    "//third_party/rust/flate2/v1:lib",
+    "//third_party/rust/indexmap/v2:lib",
+    "//third_party/rust/memchr/v2:lib",
+  ]
+  features = [
+    "_deflate-any",
+    "deflate-flate2",
+  ]
+
+  #####################################################################
+  # Tweaking which GN `config`s apply to this target.
+
+  # Config changes that apply to all `//third_party/rust` crates.
+  _configs_to_remove = [
+    # We don't need code coverage data for any `chromium_crates_io` crates.
+    "//build/config/coverage:default_coverage",
+
+    # This is third-party code, so remove `chromium_code` config.  We are not
+    # at the same time adding `//build/config/compiler:no_chromium_code`,
+    # because 1) we don't want to pull how warnings are handled by that config
+    # and 2) that config doesn't have any non-warnings-related stuff.
+    "//build/config/compiler:chromium_code",
+  ]
+  _configs_to_add = []
+
+  # Changing (if needed) which configs apply to this specific crate (based on
+  # `extra_kv.configs_to_remove` and `extra_kv.configs_to_add` from
+  # `gnrt_config.toml`).
+  _configs_to_remove += []
+  _configs_to_add += []
+
+  # Applying config changes.
+  library_configs -= _configs_to_remove
+  library_configs += _configs_to_add
+  executable_configs -= _configs_to_remove
+  executable_configs += _configs_to_add
+  proc_macro_configs -= _configs_to_remove
+  proc_macro_configs += _configs_to_add
+}
diff --git a/third_party/rust/zip/v2/README.chromium b/third_party/rust/zip/v3/README.chromium
similarity index 75%
rename from third_party/rust/zip/v2/README.chromium
rename to third_party/rust/zip/v3/README.chromium
index df1b20ea..4fdbf35 100644
--- a/third_party/rust/zip/v2/README.chromium
+++ b/third_party/rust/zip/v3/README.chromium
@@ -1,9 +1,9 @@
 Name: zip
 URL: https://crates.io/crates/zip
-Version: 2.6.1
-Revision: 1d42731efcad72e9f653a5ca46ed8306f4a3ca72
+Version: 3.0.0
+Revision: 8c01cb7fec6c4f696f8306f1942044dcf1a8f5b2
 License: MIT
-License File: //third_party/rust/chromium_crates_io/vendor/zip-v2/LICENSE
+License File: //third_party/rust/chromium_crates_io/vendor/zip-v3/LICENSE
 Shipped: yes
 Security Critical: yes
 
diff --git a/third_party/vulkan-deps b/third_party/vulkan-deps
index 8f94220..bb4c9a8 160000
--- a/third_party/vulkan-deps
+++ b/third_party/vulkan-deps
@@ -1 +1 @@
-Subproject commit 8f94220fd61f543da56fd22b6e1737d9dddd223b
+Subproject commit bb4c9a8250cf96a68bd4c3ba51ef231a59b6c502
diff --git a/third_party/vulkan-loader/src b/third_party/vulkan-loader/src
index 0f4bd66..c913466 160000
--- a/third_party/vulkan-loader/src
+++ b/third_party/vulkan-loader/src
@@ -1 +1 @@
-Subproject commit 0f4bd6649d19b5df4e76381725ab6f5a2eeb9f30
+Subproject commit c913466fdc5004584890f89ff91121bdb2ffd4ba
diff --git a/third_party/webrtc b/third_party/webrtc
index 41eb9cd..3d059dd 160000
--- a/third_party/webrtc
+++ b/third_party/webrtc
@@ -1 +1 @@
-Subproject commit 41eb9cd3c7ec1ca38cc9e4037cc2256125d560bd
+Subproject commit 3d059dd3ca2653d372ea4463dadc53a40baf7fa8
diff --git a/third_party/zlib/contrib/minizip/unzip.c b/third_party/zlib/contrib/minizip/unzip.c
index 2c8a73e..607abc8 100644
--- a/third_party/zlib/contrib/minizip/unzip.c
+++ b/third_party/zlib/contrib/minizip/unzip.c
@@ -489,8 +489,10 @@
     buf = (unsigned char*)ALLOC(BUFSIZE);
     if (buf==NULL)
         return CENTRALDIRINVALID;
+    if (relativeOffset > uPosFound)
+        return CENTRALDIRINVALID;
     // Zip64 EOCDR is at least 48 bytes long.
-    while (relativeOffset <= uPosFound - 48) {
+    while (uPosFound - relativeOffset >= 48) {
         int found = 0;
         uLong uReadSize = uPosFound - relativeOffset;
         if (uReadSize > BUFSIZE) {
@@ -1043,6 +1045,8 @@
                     {
                         uLong uSizeRead;
 
+                        file_info.size_filename = fileNameSize;
+
                         if (fileNameSize < fileNameBufferSize)
                         {
                              *(szFileName + fileNameSize) = '\0';
diff --git a/third_party/zlib/google/zip_reader.cc b/third_party/zlib/google/zip_reader.cc
index f64eedd..f71fcf51 100644
--- a/third_party/zlib/google/zip_reader.cc
+++ b/third_party/zlib/google/zip_reader.cc
@@ -219,6 +219,7 @@
     return false;
   }
 
+  DCHECK(path_in_zip[info.size_filename] == '\0');
   entry_.path_in_original_encoding = path_in_zip.data();
 
   // Convert path from original encoding to Unicode.
diff --git a/third_party/zlib/google/zip_reader_unittest.cc b/third_party/zlib/google/zip_reader_unittest.cc
index 46c0beb1..d77838c 100644
--- a/third_party/zlib/google/zip_reader_unittest.cc
+++ b/third_party/zlib/google/zip_reader_unittest.cc
@@ -887,6 +887,31 @@
   EXPECT_EQ("This file has been changed after its CRC was computed.", contents);
 }
 
+// The Unicode Path Extra field overrides the regular filename. Make sure the
+// size_filename field is also correctly updated (ZipReader has a DCHECK for
+// this).
+TEST_F(ZipReaderTest, WrongFilenameLength) {
+  static const char test_data[] = {
+      0x50, 0x4b, 0x03, 0x04, 0x0a, 0x03, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x71,
+      0x91, 0x4e, 0xbc, 0x2c, 0x7f, 0x06, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00,
+      0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0xc8, 0xfe, 0x20, 0xc8, 0xfe, 0xc8,
+      0xfe, 0x2e, 0x74, 0x78, 0x74, 0xed, 0x95, 0x9c, 0xea, 0xb5, 0xad, 0xeb,
+      0xa7, 0x90, 0x50, 0x4b, 0x01, 0x02, 0x3f, 0x03, 0x0a, 0x03, 0x00, 0x00,
+      0x00, 0x00, 0xd0, 0x71, 0x91, 0x4e, 0xbc, 0x2c, 0x7f, 0x06, 0x09, 0x00,
+      0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x17, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x20, 0x80, 0xc9, 0x81, 0x00, 0x00, 0x00, 0x00,
+      0xc8, 0xfe, 0x20, 0xc8, 0xfe, 0xc8, 0xfe, 0x2e, 0x74, 0x78, 0x74, 0x75,
+      0x70, 0x13, 0x00, 0x01, 0xe9, 0x73, 0xbf, 0xc8, 0xec, 0x83, 0x88, 0x20,
+      0xeb, 0xac, 0xb8, 0xec, 0x84, 0x9c, 0x2e, 0x74, 0x78, 0x74, 0x50, 0x4b,
+      0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x50, 0x00,
+      0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+  std::string test_string(test_data, sizeof(test_data));
+  ZipReader reader;
+  ASSERT_TRUE(reader.OpenFromString(test_string));
+  reader.Next();
+}
+
 class FileWriterDelegateTest : public ::testing::Test {
  protected:
   void SetUp() override {
diff --git a/third_party/zlib/patches/0016-minizip-parse-unicode-path-extra-field.patch b/third_party/zlib/patches/0016-minizip-parse-unicode-path-extra-field.patch
index 9a99a8a..73ea055 100644
--- a/third_party/zlib/patches/0016-minizip-parse-unicode-path-extra-field.patch
+++ b/third_party/zlib/patches/0016-minizip-parse-unicode-path-extra-field.patch
@@ -21,6 +21,20 @@
     Commit-Queue: Alex Danilo <adanilo@chromium.org>
     Cr-Commit-Position: refs/heads/main@{#1002476}
 
+commit 5a56f75b0da23a10f2c6d9068029a00e53425ddf
+Author: Hans Wennborg <hans@chromium.org>
+Date:   Fri May 16 15:48:19 2025 +0200
+
+    [minizip] Set filename length when using the Unicode Path Extra field
+
+    This is a follow-up to crrev.com/1002476 which added support for parsing
+    the Unicode Path Extra field, which overrides the regular filename. It
+    also needs to update the filename length.
+
+    Bug: 40623474
+    Change-Id: Ifab65f470736b45b1b51a1cc130a5753a2b20583
+    Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6553931
+
 diff --git a/third_party/zlib/contrib/minizip/unzip.c b/third_party/zlib/contrib/minizip/unzip.c
 index c8a01b23efd42..42677cff82c96 100644
 --- a/third_party/zlib/contrib/minizip/unzip.c
@@ -93,6 +107,8 @@
 +                    {
 +                        uLong uSizeRead;
 +
++                        file_info.size_filename = fileNameSize;
++
 +                        if (fileNameSize < fileNameBufferSize)
 +                        {
 +                             *(szFileName + fileNameSize) = '\0';
diff --git a/third_party/zlib/patches/0018-support-prefixed-zip64.patch b/third_party/zlib/patches/0018-support-prefixed-zip64.patch
index b6f15689..9e1b473 100644
--- a/third_party/zlib/patches/0018-support-prefixed-zip64.patch
+++ b/third_party/zlib/patches/0018-support-prefixed-zip64.patch
@@ -1,3 +1,34 @@
+commit 410a23e8a442e3de4f8df29339946a7d826120d0
+Author: Joshua Pawlicki <waffles@chromium.org>
+Date:   Wed May 14 10:58:18 2025 -0700
+
+    [zip]: Allow zip64 files to have prepended content.
+
+    This was already supported for non-zip64 files.
+
+    Fixed: 414848094
+    Change-Id: I3bd79f4b5175d8abca8d8e81f26373037868dcf8
+    Cq-Include-Trybots: luci.chromium.try:ios-blink-dbg-fyi
+    Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6533087
+    Commit-Queue: Joshua Pawlicki <waffles@chromium.org>
+    Auto-Submit: Joshua Pawlicki <waffles@chromium.org>
+    Reviewed-by: Hans Wennborg <hans@chromium.org>
+    Cr-Commit-Position: refs/heads/main@{#1460236}
+
+commit 3daf10f4a4cd6dbfaaaef6a90091f7c6c3148a24
+Author: Hans Wennborg <hans@chromium.org>
+Date:   Fri May 16 15:35:36 2025 +0200
+
+    [minizip] Fix possible infinite loop in unz64local_SearchCentralDir64
+
+    This is a follow-up to crrev.com/1460236, where `uPosFound - 48` could
+    overflow, resulting in a possible readSize of 3, which meant the loop
+    did not make progress.
+
+    Bug: 414848094
+    Change-Id: Icda653b2c9ac59161fb2aba9ac287fe510ee8653
+    Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6554217
+
 diff --git a/third_party/zlib/contrib/minizip/unzip.c b/third_party/zlib/contrib/minizip/unzip.c
 index a39e1752f6d2e..2c8a73e87e011 100644
 --- a/third_party/zlib/contrib/minizip/unzip.c
@@ -13,8 +44,10 @@
 +    buf = (unsigned char*)ALLOC(BUFSIZE);
 +    if (buf==NULL)
 +        return CENTRALDIRINVALID;
++    if (relativeOffset > uPosFound)
++        return CENTRALDIRINVALID;
 +    // Zip64 EOCDR is at least 48 bytes long.
-+    while (relativeOffset <= uPosFound - 48) {
++    while (relativeOffset + 48 <= uPosFound) {
 +        int found = 0;
 +        uLong uReadSize = uPosFound - relativeOffset;
 +        if (uReadSize > BUFSIZE) {
diff --git a/tools/autotest.py b/tools/autotest.py
index 553732c7..9813567 100755
--- a/tools/autotest.py
+++ b/tools/autotest.py
@@ -67,7 +67,7 @@
 ]
 
 _TEST_TARGET_REGEX = re.compile(
-    r'(_browsertests|_perftests|_wpr_tests|_unittests)$')
+    r'(_browsertests|_perftests|_wpr_tests|_unittests|_tests?)$')
 
 _PREF_MAPPING_FILE_PATTERN = re.escape(
     str(Path('components') / 'policy' / 'test' / 'data' / 'pref_mapping') +
diff --git a/tools/crates/gnrt/gen.rs b/tools/crates/gnrt/gen.rs
index b0da22b..6c90c12 100644
--- a/tools/crates/gnrt/gen.rs
+++ b/tools/crates/gnrt/gen.rs
@@ -317,13 +317,15 @@
 /// Runs `gn format` command to format a `BUILD.gn` file at the given path.
 fn format_build_file(path_to_build_gn_file: &Path) -> Result<()> {
     let cmd_name = "gn format";
-    let child = check_spawn(
+    check_spawn(
         Command::new(if cfg!(windows) { "gn.bat" } else { "gn" })
             .arg("format")
             .arg(path_to_build_gn_file)
             // Discard `Wrote formatted to '//.../BUILD>gn'` messages.
             .stdout(Stdio::null()),
         cmd_name,
-    )?;
-    check_exit_ok(&check_wait_with_output(child, cmd_name)?, cmd_name)
+    )
+    .and_then(|child| check_wait_with_output(child, cmd_name))
+    .and_then(|output| check_exit_ok(&output, cmd_name))
+    .with_context(|| format!("Error formatting `{}`", path_to_build_gn_file.display()))
 }
diff --git a/tools/crates/gnrt/util.rs b/tools/crates/gnrt/util.rs
index 51a2969..7621748 100644
--- a/tools/crates/gnrt/util.rs
+++ b/tools/crates/gnrt/util.rs
@@ -5,7 +5,7 @@
 use crate::paths::ChromiumPaths;
 
 use anyhow::{ensure, format_err, Context, Result};
-use handlebars::handlebars_helper;
+use handlebars::{handlebars_helper, Renderable};
 use serde::Serialize;
 use std::{
     borrow::Cow,
@@ -50,6 +50,7 @@
             Some(code) => write!(msg, "{code}.").unwrap(),
             None => write!(msg, "no code.").unwrap(),
         };
+        write!(msg, " stdout:\n\n{}", String::from_utf8_lossy(&output.stdout)).unwrap();
         write!(msg, " stderr:\n\n{}", String::from_utf8_lossy(&output.stderr)).unwrap();
 
         Err(format_err!(msg))
@@ -159,6 +160,29 @@
     Ok(())
 }
 
+struct IfKeyPresentHelper();
+
+impl handlebars::HelperDef for IfKeyPresentHelper {
+    fn call<'reg: 'rc, 'rc>(
+        &self,
+        h: &handlebars::Helper<'rc>,
+        r: &'reg handlebars::Handlebars<'reg>,
+        ctx: &'rc handlebars::Context,
+        rc: &mut handlebars::RenderContext<'reg, 'rc>,
+        out: &mut dyn handlebars::Output,
+    ) -> handlebars::HelperResult {
+        let param_not_found =
+            |idx| handlebars::RenderErrorReason::ParamNotFoundForIndex("is_key_present", idx);
+        let key = h.param(0).and_then(|v| v.value().as_str()).ok_or(param_not_found(0))?;
+        let dict = h.param(1).and_then(|v| v.value().as_object()).ok_or(param_not_found(1))?;
+        let template = if dict.contains_key(key) { h.template() } else { h.inverse() };
+        match template {
+            None => Ok(()),
+            Some(t) => t.render(r, ctx, rc, out),
+        }
+    }
+}
+
 pub fn init_handlebars<'reg>() -> handlebars::Handlebars<'reg> {
     let mut handlebars = handlebars::Handlebars::new();
     handlebars.set_strict_mode(true);
@@ -171,6 +195,11 @@
     handlebars_helper!(gn_escape: |x: String| escape_for_handlebars(&x));
     handlebars.register_helper("gn_escape", Box::new(gn_escape));
 
+    // Install helper to detect presence of dictionary keys (which works even if
+    // the corresponding value is "false-y / non-truth-y" - i.e. the helper
+    // distinguishes "missing" / "none" VS `false` / `0` / empty-string, etc.).
+    handlebars.register_helper("if_key_present", Box::new(IfKeyPresentHelper()));
+
     handlebars
 }
 
@@ -259,10 +288,32 @@
     use super::*;
 
     #[test]
-    fn string_excaping() {
+    fn test_string_excaping() {
         assert_eq!("foo bar", format!("{}", escape_for_handlebars("foo bar")));
         assert_eq!("foo bar ", format!("{}", escape_for_handlebars("foo\nbar\n")));
         assert_eq!(r#"foo \"bar\""#, format!("{}", escape_for_handlebars(r#"foo "bar""#)));
         assert_eq!("foo 'bar'", format!("{}", escape_for_handlebars("foo 'bar'")));
     }
+
+    #[test]
+    fn test_handlebars_helper_is_key_present() {
+        fn render(data: serde_json::Value) -> String {
+            let mut h = init_handlebars();
+            h.register_template_string(
+                "template",
+                r#"
+                    {{#if_key_present "foo" dict}}
+                        true
+                    {{else}}
+                        false
+                    {{/if_key_present}}
+                "#,
+            )
+            .unwrap();
+            h.render("template", &data).unwrap().trim().to_string()
+        }
+
+        assert_eq!("true", render(serde_json::json!({ "dict": { "foo": 456 }})));
+        assert_eq!("false", render(serde_json::json!({ "dict": { "bar": 456 }})));
+    }
 }
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 8bfc9f80..14fb202 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -27708,6 +27708,50 @@
   </description>
 </action>
 
+<action name="MobileToolbarTabGroupMenu.DeleteSharedGroup">
+  <owner>zheliooo@google.com</owner>
+  <owner>clank-large-form-factors@google.com</owner>
+  <description>
+    User deleted a shared tab group via tab group menu on tab strip on Android.
+  </description>
+</action>
+
+<action name="MobileToolbarTabGroupMenu.LeaveSharedGroup">
+  <owner>zheliooo@google.com</owner>
+  <owner>clank-large-form-factors@google.com</owner>
+  <description>
+    User left a shared tab group via tab group menu on tab strip on Android.
+  </description>
+</action>
+
+<action name="MobileToolbarTabGroupMenu.ManageSharing">
+  <owner>zheliooo@google.com</owner>
+  <owner>clank-large-form-factors@google.com</owner>
+  <description>
+    User managed a shared tab group via tab group menu on tab strip on Android.
+  </description>
+</action>
+
+<action name="MobileToolbarTabGroupMenu.MoveGroupToAnotherWindow">
+  <owner>nemco@google.com</owner>
+  <owner>zheliooo@google.com</owner>
+  <owner>clank-large-form-factors@google.com</owner>
+  <description>
+    User moved the tab group to a another existing window via tab group menu on
+    tab strip on Android.
+  </description>
+</action>
+
+<action name="MobileToolbarTabGroupMenu.MoveGroupToNewWindow">
+  <owner>nemco@google.com</owner>
+  <owner>zheliooo@google.com</owner>
+  <owner>clank-large-form-factors@google.com</owner>
+  <description>
+    User moved the tab group to a new window via tab group menu on tab strip on
+    Android.
+  </description>
+</action>
+
 <action name="MobileToolbarTabGroupMenu.NewTabInGroup">
   <owner>zheliooo@google.com</owner>
   <owner>clank-large-form-factors@google.com</owner>
@@ -27716,6 +27760,23 @@
   </description>
 </action>
 
+<action name="MobileToolbarTabGroupMenu.RecentActivity">
+  <owner>zheliooo@google.com</owner>
+  <owner>clank-large-form-factors@google.com</owner>
+  <description>
+    User viewed recent shared tab group activity via tab group menu on tab strip
+    on Android.
+  </description>
+</action>
+
+<action name="MobileToolbarTabGroupMenu.ShareGroup">
+  <owner>zheliooo@google.com</owner>
+  <owner>clank-large-form-factors@google.com</owner>
+  <description>
+    User shared a tab group via tab group menu on tab strip on Android.
+  </description>
+</action>
+
 <action name="MobileToolbarTabGroupMenu.Shown">
   <owner>zheliooo@google.com</owner>
   <owner>clank-large-form-factors@google.com</owner>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 358475d7..24307d5 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -15795,6 +15795,8 @@
   <int value="661020875" label="AutofillSaveCardShowNoThanks:disabled"/>
   <int value="661537613" label="WebNotesPublish:enabled"/>
   <int value="661566521" label="MediaAppImageMantisRemoveBackground:enabled"/>
+  <int value="661979157"
+      label="EnableSiteSearchAllowUserOverridePolicy:disabled"/>
   <int value="662001716" label="ToolbarIphAndroid:disabled"/>
   <int value="662331859" label="ConversionMeasurement:enabled"/>
   <int value="662707634" label="DynamicColorAndroid:enabled"/>
@@ -19252,6 +19254,8 @@
   <int value="1938279796" label="PromosOnLocalNtp:disabled"/>
   <int value="1938772705" label="PrivacySandboxAdTopicsContentParity:enabled"/>
   <int value="1939413645" label="enable-invalid-cert-collection"/>
+  <int value="1939639779"
+      label="EnableSiteSearchAllowUserOverridePolicy:enabled"/>
   <int value="1939884866" label="web-otp-backend"/>
   <int value="1940625534" label="NewOverviewUi:disabled"/>
   <int value="1941013139" label="FedCmUseOtherAccount:disabled"/>
diff --git a/tools/metrics/histograms/metadata/accessibility/histograms.xml b/tools/metrics/histograms/metadata/accessibility/histograms.xml
index cd4d888..e255110d2 100644
--- a/tools/metrics/histograms/metadata/accessibility/histograms.xml
+++ b/tools/metrics/histograms/metadata/accessibility/histograms.xml
@@ -829,7 +829,7 @@
 </histogram>
 
 <histogram name="Accessibility.CaptionSettingsLoadedFromPrefs"
-    enum="BooleanEnabled" expires_after="2025-06-27">
+    enum="BooleanEnabled" expires_after="2026-05-19">
   <owner>evliu@google.com</owner>
   <owner>chrome-media-ux@google.com</owner>
   <summary>
@@ -839,7 +839,7 @@
 </histogram>
 
 <histogram name="Accessibility.CaptionSettingsLoadedFromSystemSettings"
-    enum="BooleanEnabled" expires_after="2025-06-27">
+    enum="BooleanEnabled" expires_after="2026-05-19">
   <owner>evliu@google.com</owner>
   <owner>chrome-media-ux@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/android/histograms.xml b/tools/metrics/histograms/metadata/android/histograms.xml
index 081deae..e698ced 100644
--- a/tools/metrics/histograms/metadata/android/histograms.xml
+++ b/tools/metrics/histograms/metadata/android/histograms.xml
@@ -6716,7 +6716,7 @@
 </histogram>
 
 <histogram name="Android.WebView.GeolocationRetained.Granted"
-    enum="BooleanGranted" expires_after="2025-06-30">
+    enum="BooleanGranted" expires_after="2026-05-01">
   <owner>pbirk@chromium.org</owner>
   <owner>src/android_webview/OWNERS</owner>
   <summary>
@@ -6729,7 +6729,7 @@
 </histogram>
 
 <histogram name="Android.WebView.GeolocationRetained.ResponseTime" units="ms"
-    expires_after="2025-06-30">
+    expires_after="2026-05-01">
   <owner>pbirk@chromium.org</owner>
   <owner>src/android_webview/OWNERS</owner>
   <summary>
@@ -7341,7 +7341,7 @@
 </histogram>
 
 <histogram name="Android.WebView.OnGeolocationPermissionsShowPrompt.Granted"
-    enum="BooleanGranted" expires_after="2025-06-30">
+    enum="BooleanGranted" expires_after="2026-05-01">
   <owner>pbirk@chromium.org</owner>
   <owner>src/android_webview/OWNERS</owner>
   <summary>
@@ -7356,7 +7356,7 @@
 
 <histogram
     name="Android.WebView.OnGeolocationPermissionsShowPrompt.ResponseTime"
-    units="ms" expires_after="2025-06-30">
+    units="ms" expires_after="2026-05-01">
   <owner>pbirk@chromium.org</owner>
   <owner>src/android_webview/OWNERS</owner>
   <summary>
@@ -7374,7 +7374,7 @@
 </histogram>
 
 <histogram name="Android.WebView.OnGeolocationPermissionsShowPrompt.Retain"
-    enum="BooleanRetain" expires_after="2025-06-30">
+    enum="BooleanRetain" expires_after="2026-05-01">
   <owner>pbirk@chromium.org</owner>
   <owner>src/android_webview/OWNERS</owner>
   <summary>
@@ -7387,7 +7387,7 @@
 </histogram>
 
 <histogram name="Android.WebView.OnPermissionRequest.Granted"
-    enum="BooleanGranted" expires_after="2025-06-30">
+    enum="BooleanGranted" expires_after="2026-05-01">
   <owner>pbirk@chromium.org</owner>
   <owner>src/android_webview/OWNERS</owner>
   <summary>
@@ -7403,7 +7403,7 @@
 </histogram>
 
 <histogram name="Android.WebView.OnPermissionRequest.RequestedResourceCount"
-    units="resources" expires_after="2025-06-30">
+    units="resources" expires_after="2026-05-01">
   <owner>pbirk@chromium.org</owner>
   <owner>src/android_webview/OWNERS</owner>
   <summary>
@@ -7420,7 +7420,7 @@
 </histogram>
 
 <histogram name="Android.WebView.OnPermissionRequest.RequestedResources"
-    enum="PermissionResourceBitfield" expires_after="2025-06-30">
+    enum="PermissionResourceBitfield" expires_after="2026-05-01">
   <owner>pbirk@chromium.org</owner>
   <owner>src/android_webview/OWNERS</owner>
   <summary>
@@ -7440,7 +7440,7 @@
 </histogram>
 
 <histogram name="Android.WebView.OnPermissionRequest.ResponseTime" units="ms"
-    expires_after="2025-06-30">
+    expires_after="2026-05-01">
   <owner>pbirk@chromium.org</owner>
   <owner>src/android_webview/OWNERS</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/ash/histograms.xml b/tools/metrics/histograms/metadata/ash/histograms.xml
index 79d16d3..a300d47 100644
--- a/tools/metrics/histograms/metadata/ash/histograms.xml
+++ b/tools/metrics/histograms/metadata/ash/histograms.xml
@@ -8241,7 +8241,7 @@
 </histogram>
 
 <histogram name="Ash.ShortcutCustomization.CustomizationAction"
-    enum="ShortcutCustomizationAction" expires_after="2025-06-29">
+    enum="ShortcutCustomizationAction" expires_after="2026-06-29">
   <owner>jimmyxgong@chromium.org</owner>
   <owner>cros-device-enablement@google.com</owner>
   <summary>
@@ -8261,7 +8261,7 @@
 </histogram>
 
 <histogram name="Ash.ShortcutCustomization.CustomizationsLoadedOnStartup"
-    units="count" expires_after="2025-06-29">
+    units="count" expires_after="2026-06-29">
   <owner>jimmyxgong@chromium.org</owner>
   <owner>cros-device-enablement@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/attribution_reporting/enums.xml b/tools/metrics/histograms/metadata/attribution_reporting/enums.xml
index ad27555..ec89301f6 100644
--- a/tools/metrics/histograms/metadata/attribution_reporting/enums.xml
+++ b/tools/metrics/histograms/metadata/attribution_reporting/enums.xml
@@ -186,7 +186,7 @@
       label="Corresponding source - destination sites query failed"/>
   <int value="27" label="Corresponding source - invalid destination sites"/>
   <int value="28" label="Corresponding source - construction failed"/>
-  <int value="29" label="Corresponding source - invalid trigger specs"/>
+  <int value="29" label="Corresponding source - invalid trigger data"/>
   <int value="30" label="Corresponding source - dedup key query failed"/>
   <int value="31"
       label="Corresponding source - invalid randomized response rate"/>
@@ -361,16 +361,18 @@
   <int value="26" label="event_report_windows' end time value is invalid"/>
   <int value="27" label="trigger_data_matching value is invalid"/>
   <int value="28" label="(Obsolete) trigger_specs has wrong type"/>
-  <int value="29" label="trigger spec's trigger_data missing"/>
-  <int value="30" label="trigger_spec's trigger_data not a valid list"/>
+  <int value="29" label="(Obsolete) trigger spec's trigger_data missing"/>
+  <int value="30"
+      label="(Obsolete) trigger_spec's trigger_data not a valid list"/>
   <int value="31" label="trigger_data not a valid list"/>
   <int value="32" label="duplicate trigger data"/>
-  <int value="33" label="trigger_specs duplicate trigger data"/>
+  <int value="33" label="(Obsolete) trigger_specs duplicate trigger data"/>
   <int value="34" label="excessive trigger data"/>
-  <int value="35" label="trigger_specs excessive trigger data"/>
+  <int value="35" label="(Obsolete) trigger_specs excessive trigger data"/>
   <int value="36" label="invalid trigger data for matching mode"/>
   <int value="37"
-      label="top-level trigger_data and specs are mutually exclusive"/>
+      label="(Obsolete) top-level trigger_data and specs are mutually
+             exclusive"/>
   <int value="38" label="(Obsolete) summary_operator value is invalid"/>
   <int value="39" label="(Obsolete) summary_buckets not a valid list"/>
   <int value="40" label="(Obsolete) summary_buckets value invalid"/>
diff --git a/tools/metrics/histograms/metadata/chromeos/histograms.xml b/tools/metrics/histograms/metadata/chromeos/histograms.xml
index 12bc464..36d0a2f 100644
--- a/tools/metrics/histograms/metadata/chromeos/histograms.xml
+++ b/tools/metrics/histograms/metadata/chromeos/histograms.xml
@@ -1539,7 +1539,7 @@
 
 <histogram
     name="ChromeOS.FirmwareUpdateUi.InstallFailedWithDurationAfterRequest.RequestId{DeviceRequestId}"
-    units="ms" expires_after="2025-06-28">
+    units="ms" expires_after="2026-06-28">
   <owner>cambickel@google.com</owner>
   <owner>jimmyxgong@chromium.org</owner>
   <owner>cros-device-enablement@google.com</owner>
@@ -1564,7 +1564,7 @@
 </histogram>
 
 <histogram name="ChromeOS.FirmwareUpdateUi.RequestReceived.Kind{Kind}"
-    enum="FirmwareUpdateDeviceRequestID" expires_after="2025-06-28">
+    enum="FirmwareUpdateDeviceRequestID" expires_after="2026-06-28">
   <owner>cambickel@google.com</owner>
   <owner>jimmyxgong@chromium.org</owner>
   <owner>cros-device-enablement@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/chromeos_settings/histograms.xml b/tools/metrics/histograms/metadata/chromeos_settings/histograms.xml
index 61859a81..8a4dead 100644
--- a/tools/metrics/histograms/metadata/chromeos_settings/histograms.xml
+++ b/tools/metrics/histograms/metadata/chromeos_settings/histograms.xml
@@ -305,7 +305,7 @@
 </histogram>
 
 <histogram name="ChromeOS.Settings.Display" enum="DisplaySettingsType"
-    expires_after="2025-06-29">
+    expires_after="2026-06-29">
   <owner>zhangwenyu@google.com</owner>
   <owner>cros-device-enablement@google.com</owner>
   <summary>
@@ -374,7 +374,7 @@
 </histogram>
 
 <histogram name="ChromeOS.Settings.Display.UnifiedModeStatus"
-    enum="DisplaySettingsBoolean" expires_after="2025-06-28">
+    enum="DisplaySettingsBoolean" expires_after="2026-06-28">
   <owner>zhangwenyu@google.com</owner>
   <owner>cros-device-enablement@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/media/histograms.xml b/tools/metrics/histograms/metadata/media/histograms.xml
index a2db40a2..36047860 100644
--- a/tools/metrics/histograms/metadata/media/histograms.xml
+++ b/tools/metrics/histograms/metadata/media/histograms.xml
@@ -514,7 +514,7 @@
 </histogram>
 
 <histogram name="Media.Audible.ConcurrentTabsTime" units="ms"
-    expires_after="2025-09-03">
+    expires_after="2026-05-19">
   <owner>evliu@google.com</owner>
   <owner>media-dev-uma@chromium.org</owner>
   <summary>
@@ -527,7 +527,7 @@
 </histogram>
 
 <histogram name="Media.Audible.ConcurrentTabsWhenStarting" units="units"
-    expires_after="2025-06-27">
+    expires_after="2026-05-19">
   <owner>evliu@google.com</owner>
   <owner>media-dev-uma@chromium.org</owner>
   <summary>
@@ -538,7 +538,7 @@
 </histogram>
 
 <histogram name="Media.Audible.MaxConcurrentTabsInSession" units="units"
-    expires_after="2025-06-27">
+    expires_after="2026-05-19">
   <owner>evliu@google.com</owner>
   <owner>media-dev-uma@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/omnibox/histograms.xml b/tools/metrics/histograms/metadata/omnibox/histograms.xml
index f7fa25b..550b877 100644
--- a/tools/metrics/histograms/metadata/omnibox/histograms.xml
+++ b/tools/metrics/histograms/metadata/omnibox/histograms.xml
@@ -136,6 +136,7 @@
   <variant name="INVALID_SPEC" summary="invalid spec; shouldn't happen"/>
   <variant name="JOURNEYS"
       summary="Journeys landing page, side Panel or the NTP"/>
+  <variant name="JUMP_START" summary="JumpStart Omnibox (Android)"/>
   <variant name="LENS_SIDE_PANEL_SEARCHBOX"
       summary="Lens side panel searchbox"/>
   <variant name="NTP"
diff --git a/tools/metrics/histograms/metadata/optimization/enums.xml b/tools/metrics/histograms/metadata/optimization/enums.xml
index 6ad5ea8..83809f9a 100644
--- a/tools/metrics/histograms/metadata/optimization/enums.xml
+++ b/tools/metrics/histograms/metadata/optimization/enums.xml
@@ -69,7 +69,8 @@
   <int value="6"
       label="Not visible because model execution capability was disabled for
              the user account"/>
-  <int value="7" label="Not visible because the feature is already graduated"/>
+  <int value="7"
+      label="DEPRECATED: Not visible because the feature is already graduated"/>
   <int value="8"
       label="Not visible because the device is unsupported by the feature."/>
 </enum>
diff --git a/tools/metrics/histograms/metadata/page/histograms.xml b/tools/metrics/histograms/metadata/page/histograms.xml
index 9f0f44d..3d80fc4 100644
--- a/tools/metrics/histograms/metadata/page/histograms.xml
+++ b/tools/metrics/histograms/metadata/page/histograms.xml
@@ -437,7 +437,7 @@
 </histogram>
 
 <histogram name="PageLoad.BeforeSoftNavigation.CumulativeLayoutShift"
-    units="ms" expires_after="2025-10-12">
+    units="ms" expires_after="2026-05-20">
   <owner>sullivan@chromium.org</owner>
   <owner>iclelland@chromium.org</owner>
   <owner>haoliuk@chromium.org</owner>
@@ -451,7 +451,7 @@
 </histogram>
 
 <histogram name="PageLoad.BeforeSoftNavigation.InteractionToNextPaint"
-    units="ms" expires_after="2025-11-02">
+    units="ms" expires_after="2026-05-20">
   <owner>sullivan@chromium.org</owner>
   <owner>iclelland@chromium.org</owner>
   <owner>haoliuk@chromium.org</owner>
@@ -3239,7 +3239,7 @@
 </histogram>
 
 <histogram name="PageLoad.Experimental.SoftNavigations.Count"
-    units="Soft navigations" expires_after="2025-07-31">
+    units="Soft navigations" expires_after="2026-05-20">
   <owner>iclelland@chromium.org</owner>
   <owner>speed-metrics-dev@chromium.org</owner>
   <summary>
@@ -3725,7 +3725,7 @@
 </histogram>
 
 <histogram name="PageLoad.Internal.SoftNavigationOutcome"
-    enum="SoftNavigationOutcome" expires_after="2025-08-31">
+    enum="SoftNavigationOutcome" expires_after="2026-05-20">
   <owner>iclelland@chromium.org</owner>
   <owner>speed-metrics-dev@chromium.org</owner>
   <summary>
@@ -4518,7 +4518,7 @@
 </histogram>
 
 <histogram name="PageLoad.SoftNavigation.CumulativeLayoutShift" units="ms"
-    expires_after="2025-06-30">
+    expires_after="2026-05-20">
   <owner>sullivan@chromium.org</owner>
   <owner>iclelland@chromium.org</owner>
   <owner>haoliuk@chromium.org</owner>
@@ -4533,7 +4533,7 @@
 </histogram>
 
 <histogram name="PageLoad.SoftNavigation.InteractionToNextPaint" units="ms"
-    expires_after="2025-11-02">
+    expires_after="2026-05-20">
   <owner>sullivan@chromium.org</owner>
   <owner>iclelland@chromium.org</owner>
   <owner>haoliuk@chromium.org</owner>
@@ -4546,7 +4546,7 @@
 </histogram>
 
 <histogram name="PageLoad.SoftNavigation.LargestContentfulPaint" units="ms"
-    expires_after="2025-06-30">
+    expires_after="2026-05-20">
   <owner>sullivan@chromium.org</owner>
   <owner>iclelland@chromium.org</owner>
   <owner>haoliuk@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/quickoffice/histograms.xml b/tools/metrics/histograms/metadata/quickoffice/histograms.xml
index 201cc8e3..26f823f 100644
--- a/tools/metrics/histograms/metadata/quickoffice/histograms.xml
+++ b/tools/metrics/histograms/metadata/quickoffice/histograms.xml
@@ -23,7 +23,7 @@
 <histograms>
 
 <histogram name="Quickoffice.csvFormattedCellCount" units="units"
-    expires_after="2025-07-01">
+    expires_after="2026-07-01">
   <owner>joelhockey@google.com</owner>
   <owner>quickoffice-chrome-eng@google.com</owner>
   <summary>
@@ -33,7 +33,7 @@
 </histogram>
 
 <histogram name="Quickoffice.csvNonEmptyCellCount" units="units"
-    expires_after="2025-07-01">
+    expires_after="2026-07-01">
   <owner>joelhockey@google.com</owner>
   <owner>quickoffice-chrome-eng@google.com</owner>
   <summary>
@@ -43,7 +43,7 @@
 </histogram>
 
 <histogram name="Quickoffice.csvSheetCount" units="units"
-    expires_after="2025-07-01">
+    expires_after="2026-07-01">
   <owner>joelhockey@google.com</owner>
   <owner>quickoffice-chrome-eng@google.com</owner>
   <summary>
@@ -53,7 +53,7 @@
 </histogram>
 
 <histogram name="Quickoffice.docPageCount" units="units"
-    expires_after="2025-07-01">
+    expires_after="2026-07-01">
   <owner>joelhockey@google.com</owner>
   <owner>quickoffice-chrome-eng@google.com</owner>
   <summary>
@@ -62,7 +62,7 @@
 </histogram>
 
 <histogram name="Quickoffice.docParagraphCount" units="units"
-    expires_after="2025-07-01">
+    expires_after="2026-07-01">
   <owner>joelhockey@google.com</owner>
   <owner>quickoffice-chrome-eng@google.com</owner>
   <summary>
@@ -72,7 +72,7 @@
 </histogram>
 
 <histogram name="Quickoffice.docSectionCount" units="units"
-    expires_after="2025-07-01">
+    expires_after="2026-07-01">
   <owner>joelhockey@google.com</owner>
   <owner>quickoffice-chrome-eng@google.com</owner>
   <summary>
@@ -81,7 +81,7 @@
 </histogram>
 
 <histogram name="Quickoffice.docxPageCount" units="units"
-    expires_after="2025-07-01">
+    expires_after="2026-07-01">
   <owner>joelhockey@google.com</owner>
   <owner>quickoffice-chrome-eng@google.com</owner>
   <summary>
@@ -90,7 +90,7 @@
 </histogram>
 
 <histogram name="Quickoffice.docxParagraphCount" units="units"
-    expires_after="2025-07-01">
+    expires_after="2026-07-01">
   <owner>joelhockey@google.com</owner>
   <owner>quickoffice-chrome-eng@google.com</owner>
   <summary>
@@ -99,7 +99,7 @@
 </histogram>
 
 <histogram name="Quickoffice.docxSectionCount" units="units"
-    expires_after="2025-07-01">
+    expires_after="2026-07-01">
   <owner>joelhockey@google.com</owner>
   <owner>quickoffice-chrome-eng@google.com</owner>
   <summary>
@@ -108,7 +108,7 @@
 </histogram>
 
 <histogram name="Quickoffice.ErrorTypes" enum="QuickofficeErrorTypes"
-    expires_after="2025-07-01">
+    expires_after="2026-07-01">
   <owner>joelhockey@google.com</owner>
   <owner>quickoffice-chrome-eng@google.com</owner>
   <summary>
@@ -121,7 +121,7 @@
 </histogram>
 
 <histogram name="Quickoffice.FileFormat" enum="QuickofficeFileFormat"
-    expires_after="2025-07-01">
+    expires_after="2026-07-01">
   <owner>joelhockey@google.com</owner>
   <owner>quickoffice-chrome-eng@google.com</owner>
   <summary>
@@ -132,7 +132,7 @@
 </histogram>
 
 <histogram name="Quickoffice.pptMasterCount" units="units"
-    expires_after="2025-07-01">
+    expires_after="2026-07-01">
   <owner>joelhockey@google.com</owner>
   <owner>quickoffice-chrome-eng@google.com</owner>
   <summary>
@@ -142,7 +142,7 @@
 </histogram>
 
 <histogram name="Quickoffice.pptSlideCount" units="units"
-    expires_after="2025-07-01">
+    expires_after="2026-07-01">
   <owner>joelhockey@google.com</owner>
   <owner>quickoffice-chrome-eng@google.com</owner>
   <summary>
@@ -152,7 +152,7 @@
 </histogram>
 
 <histogram name="Quickoffice.pptxMasterCount" units="units"
-    expires_after="2025-07-01">
+    expires_after="2026-07-01">
   <owner>joelhockey@google.com</owner>
   <owner>quickoffice-chrome-eng@google.com</owner>
   <summary>
@@ -162,7 +162,7 @@
 </histogram>
 
 <histogram name="Quickoffice.pptxSlideCount" units="units"
-    expires_after="2025-07-01">
+    expires_after="2026-07-01">
   <owner>joelhockey@google.com</owner>
   <owner>quickoffice-chrome-eng@google.com</owner>
   <summary>
@@ -171,7 +171,7 @@
 </histogram>
 
 <histogram name="Quickoffice.xlsFormattedCellCount" units="units"
-    expires_after="2025-07-01">
+    expires_after="2026-07-01">
   <owner>joelhockey@google.com</owner>
   <owner>quickoffice-chrome-eng@google.com</owner>
   <summary>
@@ -181,7 +181,7 @@
 </histogram>
 
 <histogram name="Quickoffice.xlsNonEmptyCellCount" units="units"
-    expires_after="2025-07-01">
+    expires_after="2026-07-01">
   <owner>joelhockey@google.com</owner>
   <owner>quickoffice-chrome-eng@google.com</owner>
   <summary>
@@ -191,7 +191,7 @@
 </histogram>
 
 <histogram name="Quickoffice.xlsSheetCount" units="units"
-    expires_after="2025-07-01">
+    expires_after="2026-07-01">
   <owner>joelhockey@google.com</owner>
   <owner>quickoffice-chrome-eng@google.com</owner>
   <summary>
@@ -201,7 +201,7 @@
 </histogram>
 
 <histogram name="Quickoffice.xlsxFormattedCellCount" units="units"
-    expires_after="2025-07-01">
+    expires_after="2026-07-01">
   <owner>joelhockey@google.com</owner>
   <owner>quickoffice-chrome-eng@google.com</owner>
   <summary>
@@ -211,7 +211,7 @@
 </histogram>
 
 <histogram name="Quickoffice.xlsxNonEmptyCellCount" units="units"
-    expires_after="2025-07-01">
+    expires_after="2026-07-01">
   <owner>joelhockey@google.com</owner>
   <owner>quickoffice-chrome-eng@google.com</owner>
   <summary>
@@ -221,7 +221,7 @@
 </histogram>
 
 <histogram name="Quickoffice.xlsxSheetCount" units="units"
-    expires_after="2025-07-01">
+    expires_after="2026-07-01">
   <owner>joelhockey@google.com</owner>
   <owner>quickoffice-chrome-eng@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/sb_client/enums.xml b/tools/metrics/histograms/metadata/sb_client/enums.xml
index a29f9f5..f4f3de2 100644
--- a/tools/metrics/histograms/metadata/sb_client/enums.xml
+++ b/tools/metrics/histograms/metadata/sb_client/enums.xml
@@ -47,6 +47,9 @@
   <int value="2"
       label="Classification is not triggered because the URL is not forced by
              real-time URL check"/>
+  <int value="3"
+      label="Classification is not triggered because the trigger model ping
+             is converted to force request request type ping"/>
 </enum>
 
 <enum name="BooleanForceRequest">
diff --git a/tools/metrics/histograms/metadata/search/OWNERS b/tools/metrics/histograms/metadata/search/OWNERS
index 566301b..b4ab36a 100644
--- a/tools/metrics/histograms/metadata/search/OWNERS
+++ b/tools/metrics/histograms/metadata/search/OWNERS
@@ -6,4 +6,7 @@
 pnoland@chromium.org
 
 # For search engine choice releated metrics only.
-alexilin@chromium.org
\ No newline at end of file
+alexilin@chromium.org
+
+# For auxiliary search related metrics only.
+hanxi@chromium.org
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 9f5d367..6bc08ef 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,8 +5,8 @@
             "full_remote_path": "perfetto-luci-artifacts/v50.1/linux-arm64/trace_processor_shell"
         },
         "win": {
-            "hash": "dc7e0666ce0a847cc1dfaf33bfea7ae7dc1db2fc",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/112f38219a74c203321cb0a563bcf937dae2f3de/trace_processor_shell.exe"
+            "hash": "723c1dafd0ca7b7c01ac77a49bec9ede038399a0",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/0a066d12ea83708e99be5859f3de2f48d06e2c21/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "99f971ca131f6d11c73f4b918099d434bdd8093c",
@@ -21,8 +21,8 @@
             "full_remote_path": "perfetto-luci-artifacts/v50.1/mac-arm64/trace_processor_shell"
         },
         "linux": {
-            "hash": "da0c3e780366c0dde3891e30805d94d037011cf6",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/874674e91c82c0a52df3fa8b4262dc910ca8ea4a/trace_processor_shell"
+            "hash": "e58b0fd4afa1f510cc2775f3f688bd13f1c9a658",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/342080f33a28c65850ef2e15df7618869502a273/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc
index 8d335657..546d398 100644
--- a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc
+++ b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc
@@ -1199,9 +1199,9 @@
 
   LONG i = 0;
   for (const gfx::NativeViewAccessible& descendant : descendants) {
-    IRawElementProviderSimple* raw_provider;
+    Microsoft::WRL::ComPtr<IRawElementProviderSimple> raw_provider;
     descendant->QueryInterface(IID_PPV_ARGS(&raw_provider));
-    SafeArrayPutElement(safe_array, &i, raw_provider);
+    SafeArrayPutElement(safe_array, &i, raw_provider.Get());
     ++i;
   }
 
diff --git a/ui/accessibility/platform/iaccessible2/scoped_co_mem_array.cc b/ui/accessibility/platform/iaccessible2/scoped_co_mem_array.cc
index 22f5924..47d0860 100644
--- a/ui/accessibility/platform/iaccessible2/scoped_co_mem_array.cc
+++ b/ui/accessibility/platform/iaccessible2/scoped_co_mem_array.cc
@@ -4,6 +4,10 @@
 
 #include "ui/accessibility/platform/iaccessible2/scoped_co_mem_array.h"
 
+#include <unknwn.h>
+
+#include <algorithm>
+
 #include "base/containers/span.h"
 #include "third_party/iaccessible2/ia2_api_all.h"
 
@@ -22,4 +26,10 @@
   }
 }
 
+template <>
+void ScopedCoMemArray<IUnknown*>::FreeContents(
+    base::span<IUnknown* const> contents) {
+  std::ranges::for_each(contents, &IUnknown::Release);
+}
+
 }  // namespace ui
diff --git a/ui/accessibility/platform/iaccessible2/scoped_co_mem_array.h b/ui/accessibility/platform/iaccessible2/scoped_co_mem_array.h
index ee95011..c79f2b2 100644
--- a/ui/accessibility/platform/iaccessible2/scoped_co_mem_array.h
+++ b/ui/accessibility/platform/iaccessible2/scoped_co_mem_array.h
@@ -20,6 +20,7 @@
 #include "base/win/windows_types.h"
 
 struct IA2TextSelection;
+struct IUnknown;
 
 namespace ui {
 
@@ -86,6 +87,11 @@
 void ScopedCoMemArray<IA2TextSelection>::FreeContents(
     base::span<const IA2TextSelection> contents);
 
+// Release the reference to each IUnknown pointer in the array.
+template <>
+void ScopedCoMemArray<IUnknown*>::FreeContents(
+    base::span<IUnknown* const> contents);
+
 }  // namespace ui
 
 #endif  // UI_ACCESSIBILITY_PLATFORM_IACCESSIBLE2_SCOPED_CO_MEM_ARRAY_H_
diff --git a/ui/accessibility/platform/inspect/ax_tree_formatter_win.cc b/ui/accessibility/platform/inspect/ax_tree_formatter_win.cc
index dc7900f..9c779749 100644
--- a/ui/accessibility/platform/inspect/ax_tree_formatter_win.cc
+++ b/ui/accessibility/platform/inspect/ax_tree_formatter_win.cc
@@ -737,27 +737,32 @@
     dict->Set("table_columns", static_cast<int>(table_columns));
 }
 
-static std::u16string ProcessAccessiblesArray(IUnknown** accessibles,
-                                              LONG num_accessibles) {
+static std::u16string ProcessAccessiblesArray(
+    base::span<IUnknown* const> accessibles) {
   std::u16string related_accessibles_string;
-  if (num_accessibles <= 0)
+  if (!accessibles.size()) {
     return related_accessibles_string;
+  }
 
   base::win::ScopedVariant variant_self(CHILDID_SELF);
-  for (int index = 0; index < num_accessibles; index++) {
-    related_accessibles_string += (index > 0) ? u"," : u"<";
-    Microsoft::WRL::ComPtr<IUnknown> unknown = accessibles[index];
+  for (IUnknown* unknown_ptr : accessibles) {
+    base::StrAppend(&related_accessibles_string,
+                    {related_accessibles_string.empty() ? u"<" : u","});
+    Microsoft::WRL::ComPtr<IUnknown> unknown(unknown_ptr);
     Microsoft::WRL::ComPtr<IAccessible> accessible;
     if (SUCCEEDED(unknown.As(&accessible))) {
       base::win::ScopedBstr name;
-      if (S_OK == accessible->get_accName(variant_self, name.Receive()))
-        related_accessibles_string += base::WideToUTF16(name.Get());
-      else
-        related_accessibles_string += u"no name";
+      if (S_OK == accessible->get_accName(variant_self, name.Receive())) {
+        base::StrAppend(&related_accessibles_string,
+                        {base::WideToUTF16(name.Get())});
+      } else {
+        base::StrAppend(&related_accessibles_string, {u"no name"});
+      }
     }
   }
 
-  return related_accessibles_string + u">";
+  base::StrAppend(&related_accessibles_string, {u">"});
+  return related_accessibles_string;
 }
 
 void AXTreeFormatterWin::AddIA2TableCellProperties(
@@ -777,25 +782,21 @@
     dict->Set("ia2_table_cell_row_index", static_cast<int>(row_index));
   }
 
-  LONG n_row_header_cells;
-  IUnknown** row_headers;
-  if (SUCCEEDED(
-          ia2cell->get_rowHeaderCells(&row_headers, &n_row_header_cells)) &&
-      n_row_header_cells > 0) {
+  ScopedCoMemArray<IUnknown*> row_headers;
+  if (SUCCEEDED(ia2cell->get_rowHeaderCells(row_headers.Receive(),
+                                            row_headers.ReceiveSize())) &&
+      row_headers.size() > 0) {
     std::u16string accessibles_desc =
-        ProcessAccessiblesArray(row_headers, n_row_header_cells);
-    CoTaskMemFree(row_headers);  // Free the array manually.
+        ProcessAccessiblesArray(row_headers.as_span());
     dict->Set("row_headers", accessibles_desc);
   }
 
-  LONG n_column_header_cells;
-  IUnknown** column_headers;
-  if (SUCCEEDED(ia2cell->get_columnHeaderCells(&column_headers,
-                                               &n_column_header_cells)) &&
-      n_column_header_cells > 0) {
+  ScopedCoMemArray<IUnknown*> column_headers;
+  if (SUCCEEDED(ia2cell->get_columnHeaderCells(column_headers.Receive(),
+                                               column_headers.ReceiveSize())) &&
+      column_headers.size() > 0) {
     std::u16string accessibles_desc =
-        ProcessAccessiblesArray(column_headers, n_column_header_cells);
-    CoTaskMemFree(column_headers);  // Free the array manually.
+        ProcessAccessiblesArray(column_headers.as_span());
     dict->Set("column_headers", accessibles_desc);
   }
 }
diff --git a/ui/android/BUILD.gn b/ui/android/BUILD.gn
index e0a25922..6244f756 100644
--- a/ui/android/BUILD.gn
+++ b/ui/android/BUILD.gn
@@ -419,6 +419,8 @@
     "java/src/org/chromium/ui/interpolators/Interpolators.java",
     "java/src/org/chromium/ui/listmenu/BasicListMenu.java",
     "java/src/org/chromium/ui/listmenu/ContextMenuCheckItemProperties.java",
+    "java/src/org/chromium/ui/listmenu/ContextMenuRadioItemProperties.java",
+    "java/src/org/chromium/ui/listmenu/ContextMenuSubmenuItemProperties.java",
     "java/src/org/chromium/ui/listmenu/ListMenu.java",
     "java/src/org/chromium/ui/listmenu/ListMenuButton.java",
     "java/src/org/chromium/ui/listmenu/ListMenuDelegate.java",
diff --git a/ui/gl/swap_chain_presenter.cc b/ui/gl/swap_chain_presenter.cc
index 5188150..6ac19fe 100644
--- a/ui/gl/swap_chain_presenter.cc
+++ b/ui/gl/swap_chain_presenter.cc
@@ -481,18 +481,18 @@
 SwapChainPresenter::SwapChainPresenter(
     DCLayerTree* layer_tree,
     Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device,
-    Microsoft::WRL::ComPtr<IDCompositionDevice2> dcomp_device)
+    Microsoft::WRL::ComPtr<IDCompositionDevice3> dcomp_device)
     : layer_tree_(layer_tree),
       swap_chain_buffer_count_(BufferCount(
           layer_tree->force_dcomp_triple_buffer_video_swap_chain())),
       switched_to_BGRA8888_time_tick_(base::TimeTicks::Now()),
       d3d11_device_(d3d11_device),
-      dcomp_device_(dcomp_device),
       is_on_battery_power_(
           base::PowerMonitor::GetInstance()
               ->AddPowerStateObserverAndReturnBatteryPowerStatus(this) ==
           base::PowerStateObserver::BatteryPowerStatus::kBatteryPower) {
   DVLOG(1) << __func__ << "(" << this << ")";
+  CHECK_EQ(dcomp_device.As(&dcomp_device_), S_OK);
 }
 
 SwapChainPresenter::~SwapChainPresenter() {
@@ -1977,14 +1977,8 @@
     ReleaseSwapChainResources();
 
     Microsoft::WRL::ComPtr<IDCompositionSurface> dcomp_surface;
-    Microsoft::WRL::ComPtr<IDCompositionDevice> dcomp_device1;
-    HRESULT hr = dcomp_device_.As(&dcomp_device1);
-    if (FAILED(hr)) {
-      DLOG(ERROR) << "Failed to get DCOMP device. hr=0x" << std::hex << hr;
-      return false;
-    }
-
-    hr = dcomp_device1->CreateSurfaceFromHandle(surface_handle, &dcomp_surface);
+    const HRESULT hr =
+        dcomp_device_->CreateSurfaceFromHandle(surface_handle, &dcomp_surface);
     if (FAILED(hr)) {
       DLOG(ERROR) << "Failed to create DCOMP surface. hr=0x" << std::hex << hr;
       return false;
diff --git a/ui/gl/swap_chain_presenter.h b/ui/gl/swap_chain_presenter.h
index 64057952..17f52c9 100644
--- a/ui/gl/swap_chain_presenter.h
+++ b/ui/gl/swap_chain_presenter.h
@@ -32,7 +32,7 @@
  public:
   SwapChainPresenter(DCLayerTree* layer_tree,
                      Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device,
-                     Microsoft::WRL::ComPtr<IDCompositionDevice2> dcomp_device);
+                     Microsoft::WRL::ComPtr<IDCompositionDevice3> dcomp_device);
 
   SwapChainPresenter(const SwapChainPresenter&) = delete;
   SwapChainPresenter& operator=(const SwapChainPresenter&) = delete;
@@ -317,7 +317,7 @@
   gfx::Size staging_texture_size_;
 
   Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device_;
-  Microsoft::WRL::ComPtr<IDCompositionDevice2> dcomp_device_;
+  Microsoft::WRL::ComPtr<IDCompositionDesktopDevice> dcomp_device_;
   Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain_;
 
   // Handle returned by DCompositionCreateSurfaceHandle() used to create YUV
diff --git a/ui/shell_dialogs/select_file_dialog_linux_portal.cc b/ui/shell_dialogs/select_file_dialog_linux_portal.cc
index b23d357..8dafba0 100644
--- a/ui/shell_dialogs/select_file_dialog_linux_portal.cc
+++ b/ui/shell_dialogs/select_file_dialog_linux_portal.cc
@@ -19,6 +19,7 @@
 #include "base/nix/xdg_util.h"
 #include "base/no_destructor.h"
 #include "base/notreached.h"
+#include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/time/time.h"
@@ -399,7 +400,7 @@
       break;
   }
 
-  if (!default_path.empty()) {
+  if (!default_path.empty() && base::IsStringUTF8(default_path.value())) {
     if (default_path_exists) {
       // If this is an existing directory, navigate to that directory, with no
       // filename.
diff --git a/ui/views/window/dialog_client_view_unittest.cc b/ui/views/window/dialog_client_view_unittest.cc
index 7643967..1643b4a 100644
--- a/ui/views/window/dialog_client_view_unittest.cc
+++ b/ui/views/window/dialog_client_view_unittest.cc
@@ -164,24 +164,6 @@
     delegate_->DialogModelChanged();
   }
 
-  Button* GetButtonByAccessibleName(View* root, const std::u16string& name) {
-    Button* button = Button::AsButton(root);
-    if (button && button->GetViewAccessibility().GetCachedName() == name) {
-      return button;
-    }
-    for (views::View* child : root->children()) {
-      button = GetButtonByAccessibleName(child, name);
-      if (button) {
-        return button;
-      }
-    }
-    return nullptr;
-  }
-
-  Button* GetButtonByAccessibleName(const std::u16string& name) {
-    return GetButtonByAccessibleName(widget_->GetRootView(), name);
-  }
-
   DialogClientView* client_view() {
     return static_cast<DialogClientView*>(widget_->client_view());
   }
@@ -807,9 +789,9 @@
 
   widget()->Show();
 
-  Button* ok = GetButtonByAccessibleName(u"ok");
-  Button* cancel = GetButtonByAccessibleName(u"cancel");
-  Button* extra = GetButtonByAccessibleName(u"extra");
+  View* ok = client_view()->ok_button();
+  View* cancel = client_view()->cancel_button();
+  View* extra = client_view()->extra_view();
 
   ASSERT_NE(ok, cancel);
   ASSERT_NE(ok, extra);
@@ -868,9 +850,9 @@
 
   SizeAndLayoutWidget();
 
-  auto* ok = GetButtonByAccessibleName(u"ok");
-  auto* cancel = GetButtonByAccessibleName(u"cancel");
-  auto* extra = GetButtonByAccessibleName(u"extra");
+  View* ok = client_view()->ok_button();
+  View* cancel = client_view()->cancel_button();
+  View* extra = client_view()->extra_view();
 
   int ok_left = ok->bounds().x();
   int cancel_left = cancel->bounds().x();
diff --git a/v8 b/v8
index 871f1db..d7fa052 160000
--- a/v8
+++ b/v8
@@ -1 +1 @@
-Subproject commit 871f1dbb7c6d0378be4993459fe3a9e7ce0643dc
+Subproject commit d7fa05296b658db5ecdc143f6cc60185c60001d0